Just trying to add some basic tests to my Carrierwave Uploader. I am starting with the default generated tests and trying to go from there. I am getting a weird error and not sure where to go from here.
I have a polymorphic Upload model which has:
mount_uploader :file, DocumentUploader
My DocumentUploader has something like this:
def store_dir
"#{model.uploadable_type.downcase.pluralize.underscore}/#{model.parent_asset.id}/uploads/#{model.id}/"
end
This makes my store directories look like:
/locations/24/uploads/56
when I run the default tests for example:
test "should destroy upload" do
assert_difference('Upload.count', -1) do
delete :destroy, id: #upload
end
assert_redirected_to uploads_path
end
I get:
ERROR["test_should_destroy_upload", UploadsControllerTest,1.9893769259997498]
test_should_destroy_upload#UploadsControllerTest (1.99s)
NoMethodError: NoMethodError: undefined method `id' for nil:NilClass
app/uploaders/document_uploader.rb:34:in `store_dir'
In my Uploads fixtures I have the polymorphic associates set etc. Themodel is not being set to the Uploadable association in the DocumentUploader i.e. it's nil.
All my other tests work so far and my uploader works fine in production and development etc. I am sure I am missing something trivial in the set-up or it's a Carrierwave specific issue.
Chiming in really late here.
Using fog with carrierwave I have found to be a drag. carrierwave-aws gem is non-negotiable for me.
The Read Me provides a quick, overall stub to add to the uploads.rb configuration file:
config.aws_credentials = {
[...], # Required
stub_responses: Rails.env.test? # Optional, avoid hitting S3 actual during tests
}
For the presentation layer, I find this acceptable because, you often want to see results, and particularly the versions of such results. Once those run properly, they are hard to break if the method does not change - and those are rather static methods.
Related
Following Carrierwave and RailsAdmin instructions I can preform multiple uploads in RailsAdmin with Carrierwave, using aws S3. When trying to delete one of the images I get:
undefined method 'filename' for nil:NilClass
The assets field is a json field, assets:json
This is in my model:
attr_accessor :delete_assets
after_validation do
uploaders = assets.delete_if do |uploader|
if Array(delete_assets).include?(uploader.file.filename)
uploader.remove!
true
end
end
write_attribute(:assets, uploaders.map { |uploader| uploader.file.filename })
end
I tried uploader.file.identifier but read that when using fog aws S3 the uploader.file.filename should be used.
My resources so far:
RailsAdmin
https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads
Carrierwave:
https://github.com/sferik/rails_admin/wiki/CarrierWave
Stackoverflow:
Rails Admin - undefined method `' for using Carrierwave multiple upload
How do I delete a previously uploaded image from this json array? Any help is much appreciated!
The problem here is certain uploaders don't have a file attached. So your error is because you're calling filename on nil - aka uploader.file returns nil.
You'll likely want to handle this occurrence, something like the following:
attr_accessor :delete_assets
after_validation do
uploaders = assets.delete_if do |uploader|
next unless uploader.file # <-- skip deleting assets without a file
if Array(delete_assets).include?(uploader.file.filename)
uploader.remove!
true
end
end
write_attribute(:assets, uploaders.map { |uploader| uploader.file.filename })
end
Or if you'd rather remove all assets missing a file:
attr_accessor :delete_assets
after_validation do
uploaders = assets.delete_if do |uploader|
next true unless uploader.file # <-- note 'true' added here
if Array(delete_assets).include?(uploader.file.filename)
uploader.remove!
true
end
end
write_attribute(:assets, uploaders.map { |uploader| uploader.file.filename })
end
Otherwise, the route you'll need to go down will be validating the presence of a file on an uploader, which is likely a separate question.
Hope this helps - let me know how you get on or if you have any questions here :)
Having the following mailer previewer code:
class RegistrationMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/registration_mailer/welcome
def welcome
RegistrationMailer.welcome users(:one)
end
end
(full file).
Which is unable to reach my fixtures (users(:one)), return a 500 error status and print out the following error:
NoMethodError: undefined method `users' for #RegistrationMailerPreview
Can we get fixtures entries from mailer previewer?
If yes, I would like to know how to do that.
I have seen that should be possible here, but I can't require test_helper in this file (I don't know why) and I don't understand the difference between ActionMailer::TestCase and ActionMailer::Preview.
If no, is there a way to preview the mail without sending as a parameter User.first, since I could do my tests on a machine on which there is no data filled in the database.
I don't know about the default fixture framework, but I can say using FactoryBot for my fixtures that I was able to use them in my mailer previews simply by prepending the build/create methods with FactoryBot. I didn't need to require anything at the top of the file. i.e.:
class RegistrationMailerPreview < ActionMailer::Preview
def welcome
user = FactoryBot.create(:user)
RegistrationMailer.welcome(user)
end
end
To answer your second question, you could also simply replace the fixture line above with user = User.new(firstname: "Joe"). That would create a new user to use in the preview without persisting it to the database.
I know that this not an "acceptable" practice but as a background process I am caching a rendered Rails partial into the database for faster output via JSONP. I have found many resources on the topic and successfully output a Rails 2.3.x partial within a ActiveRecord model, BUT if I use dynamic URLs within a partial Rails blows up.
I get a successful output (minus the dynamic URLs) with this...
def self.compile_html
viewer = Class.new(ApplicationController)
path = ActionController::Base.view_paths rescue ActionController::Base.view_root
Question.all.each do |q|
q.html = ActionView::Base.new(path, {}, viewer).render(:partial => "questions/cached_output", :locals => { :question => q })
sleep 1 # hold for complete output and debugging only
q.save( false ) # bypass validations
end
return "Caching complete"
end
I have tried including ActionController::UrlWriter Modules and ActionView::Helpers::UrlHelper with no luck. I cannot seems to find the instantiation/inclusion order to allow the partial to access ActionController::Base.new.url_for() to write the dynamic routes to the partial.
I did attempt a similar process as RoR: undefined method `url_for' for nil:NilClass without luck
I know that render_anywhere is a solution for Rails 3.x apps, but I am working in Rails 2.3.11
Thoughts?
I have an old app that I had to do something similar (but made use of polymorphic_path and polymorphic_url) in a model.
The following includes gave me access to url_for and polymorphic_path etc.
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include ActionController::PolymorphicRoutes
include ActionController::UrlWriter
I am using Paperclip and S3 for image uploads and am trying to stub out calls to S3 from my test suite. I found the thoughtbot post which mentions doing
a.cover { a.paperclip_fixture('album', 'cover', 'png') }
but that gives me a "wrong number of arguments (4 for 2)" error. I tried switching the arguments above to an array, which removes the original error, but gives an error saying "Attribute already defined: paperclip_fixture".
Has anyone been able to get this working? Also, I'd ideally like to use the local filesystem for the development environment. Is there an easy way to do this?
Okay, I've got the basic issue figured out. This is (I believe) as Eliza said, because I'm not using shoulda (I'm using rspec 2.6.0 and factory_girl 2.1.2).
Here's what worked for me (where Profile is the class that has attachements):
Profile.any_instance.stub(:save_attached_files).and_return(true)
#profile = Factory(:profile)
At the moment I just have this right in my before method of my rspec example. There's probably a better place to put it.
With latest paperclip(from github master branch) and aws-sdk version 2, I solved my issue with following configuration :
require "aws-sdk"
Aws.config[:s3] = {stub_responses: true}
For more information, please take a look at amazon sdk
Placing this in my 'spec/rails_helper.rb' file worked for me:
require 'aws'
AWS.stub!
AWS.config(:access_key_id => "TESTKEY", :secret_access_key => "TESTSECRET")
Are you using shoulda? If you aren't using shoulda the paperclip_fixture method that you're using may come from somewhere else and thus behave differently.
Potentially relevant: https://github.com/thoughtbot/paperclip/blob/master/shoulda_macros/paperclip.rb
Many of these techniques don't seem to work with the latest paperclip and S3. What finally worked for me is the combination of:
AWS.config(:access_key_id => "TESTKEY", :secret_access_key => "TESTSECRET", :stub_requests => true)
and
Mymodel.any_instance.stubs(:save_attached_files).returns(true)
But, actually, all you really need to do in many cases is the AWS :stub_requests and it will achieve what you want.
This is how I got this working. First you have to have the fakeweb gem or it will fail. You also have to have an empty file in the spec/support/paperclip/[model]/[attachment_name][ext] path.
What I did was to copy the code from Paperclip and paste it into my factory. I was unable to get the 'paperclip_fixture' working.
factory :attachment do
file do |a|
# Stubbed Paperclip attachment from: https://github.com/thoughtbot/paperclip/blob/master/shoulda_macros/paperclip.rb#L68
# FIX: This was the only way I made this work. Calling the paperclip_fixture directly didn't work.
# See: http://stackoverflow.com/questions/4941586/stubbing-paperclip-s3-requests-in-specs
model, attachment, extension = "customer_attachment", "file", "doc"
definition = model.gsub(" ", "_").classify.constantize.
attachment_definitions[attachment.to_sym]
path = "http://s3.amazonaws.com/:id/#{definition[:path]}"
path.gsub!(/:([^\/\.]+)/) do |match|
"([^\/\.]+)"
end
begin
FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")
rescue NameError
raise NameError, "the stub_paperclip_s3 shoulda macro requires the fakeweb gem."
end
base_path = File.join(Rails.root, "spec", "support", "paperclip")
File.new(File.join(base_path, model, "#{attachment}.#{extension}"))
end
end
This is how I stub a file from paperclip without using the shoulda helpers.
before(:each) do
#sheet = double('sheet')
#sheet.stub(:url).and_return(File.join(Rails.root, 'spec','fixtures','files', 'file.xls'))
active_record_object.stub(:sheet).and_return(#sheet)
end
Hope this helps someone.
I just came across this upgrading an app from Rails 2.3 to Rails 3.0 after having followed Testing Paperclip on S3 with Cucumber & Factory Girl.
Instead of including the Paperclip::Shoulda module into the Factory class, I had to include it into the FactoryGirl::DefinitionProxy class, so I changed this:
class Factory
include Paperclip::Shoulda
end
to
class FactoryGirl::DefinitionProxy
include Paperclip::Shoulda
end
For reference, I'm using paperclip 2.4.1 and factory_girl 2.0.5.
Total Rspec noob here. Writing my first tests tonight.
I've got a model called Image. Using paperclip I attach a file called photo. Standard stuff. I've run the paperclip generator and everything works fine in production and test modes.
Now I have a spec file called image.rb and it looks like this (it was created by ryanb's nifty_scaffold generator):
require File.dirname(__FILE__) + '/../spec_helper'
describe Image do
it "should be valid" do
Image.new.should be_valid
end
end
This test fails and I realise that it's because of my model validations (i.e. validates_attachment_presence)
The error that I get is:
Errors: Photo file name must be set., Photo file size file size must be between 0 and 1048576 bytes., Photo content type is not included in the list
So how do I tell rspec to upload a photo when it runs my test?
I'm guessing that it's got somethign to do with fixtures.... maybe not though. I've tried playing around with them but not having any luck. For the record, I've created a folder called images inside my fixtures folder and the two files I want to use in my tests are called rails.png and grid.png)
I've tried doing the following:
it "should be valid" do
image = Image.new :photo => fixture_file_upload('images/rails.png', 'image/png').should be_valid
# I've also tried adding stuff like this
#image.stub!(:has_attached_file).with(:photo).and_return( true )
#image.stub!(:save_attached_files).and_return true
#image.save.should be_true
end
But rspec complains about "fixture_file_upload" not being recognised... I am planning to get that Rspec book. And I've trawled around the net for an answer but can't seem to find anything. My test database DOES get populated with some data when I remove the validations from my model so I know that some of it works ok.
Thanks in advance,
EDIT:
images.yml looks like this:
one:
name: MyString
description: MyString
two:
name: MyString
description: MyString
This should work with Rails 2.X:
Image.new :photo => File.new(RAILS_ROOT + '/spec/fixtures/images/rails.png')
As of Rails 3, RAILS_ROOT is no longer used, instead you should use Rails.root.
This should work with Rails 3:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
Definitely get the RSpec book, it's fantastic.
Rails.root is a pathname object so you can use it like this:
Image.new :photo => Rails.root.join("spec/fixtures/images/rails.png").open
Edit - probably does not work in Rails 3...
see answer by #Paul Rosania
In case anyone else finds this via Google, RAILS_ROOT is no longer valid in Rails 3.0. That line should read:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
(Note the lack of leading slash!)
I use the multipart_body gem in my integration tests. Its a bit truer to BDD than testing.
http://steve.dynedge.co.uk/2010/09/19/multipart-body-a-gem-for-working-with-multipart-data/
With respect to rspec and paperclip, the has_attached_file :photo directive creates a virtual attribute of sorts i.e. :photo ... when you assign a file or a path to photo, paperclip takes over, stores the file, optionally does processing on it e.g. auto-create thumbnails, import a spreadsheet, etc. You aren't telling rspec to test paperclip. You are invoking code and telling rspec what the results of that code -should- be.
In $GEM_HOME/gems/paperclip-2.3.8/README.rdoc, about 76% of the way through the file under ==Post Processing (specifically lines 147 and 148):
---[ BEGIN QUOTE ]---
NOTE: Because processors operate by turning the original attachment into the styles, no processors will be run if there are no styles defined.
---[ END QUOTE ]---
Reading the code, you'll see support :original ... does your has_attached_file define a style?
I use a generic ":styles => { :original => { :this_key_and => :this_value_do_not_do_anything_unless_a_lib_paperclip_processors__foo_dot_rb__does_something_with_them } }" ... just to get paperclip to move the file from some temp directory into my has_attached_file :path
One would think that would be default or more obvious in the docs.