Using factory_girl with PaperClip 4.0 - ruby-on-rails

Does anyone know the proper way to create PaperClip 4.0 attachments with factory_girl, bypassing any of the PaperClip processing and validation?
I used to just be able to do the following in my factory:
factory :attachment do
supporting_documentation_file_name { 'test.pdf' }
supporting_documentation_content_type { 'application/pdf' }
supporting_documentation_file_size { 1024 }
# ...
end
This would basically trick PaperClip into thinking that there was a valid attachment.
After upgrading from 3.5.3 to 4.0, I now get a validation error:
ActiveRecord::RecordInvalid: Validation failed: Image translation missing: en.activerecord.errors.models.attachment.attributes.supporting_documentation.spoofed_media_type
NOTE: The original discussion for PaperClip 3.X is here: How Do I Use Factory Girl To Generate A Paperclip Attachment?

The issue appears to be caused by line 61 in media_type_spoof_detector.
Paperclip is trying to find the mime type of the "file" you have uploaded. When there isn't one, it's failing validation to protect you from file type spoofing.
I haven't tried this myself, but perhaps your best bet would be to use a real file, and set it using the fixture_file_upload method from ActionDispatch::TestProcess.
factory :attachment do
supporting_documentation { fixture_file_upload 'test.pdf', 'application/pdf' }
# This is to prevent Errno::EMFILE: Too many open files
after_create do |attachment, proxy|
proxy.supporting_documentation.close
end
end
You will need to include ActionDispatch::TestProcess in test_helper.rb
This was first posted here.

Related

Testing Carrierwave Uploads with Minitest

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.

Speed up image processing of Paperclip in Acceptance Tests

When running capybara feature specs I can see lots of Slow factory notices which are populated by factory_girl. These Slow factory things heavy slow down the feature specs, I think, even feature specs are intrinsic slow specs. Then I had some inspect and found out most of the Slow factory was caused by paperclip. We had model using paperclip here:
FactoryGirl.define do
factory :asset do
image Rails.root.join('spec/fixtures/sample.jpg').open
end
end
So I wonder if there's a way like test mode for paperclip to speed up tests. I have simple solution here: Just copy the original file instead of actually crop it.
You can set the paperclip image fields in your factory, which will cause paperclip to not even attempt to process the image:
factory :asset do
# Set the image fields manually to avoid uploading / processing the image
image_file_name { 'test.jpg' }
image_content_type { 'image/jpeg' }
image_file_size { 256 }
end
I found the way out to achieve this, see this code:
FactoryGirl.define do
factory :asset do
image_file_name { 'sample.jpg' }
image_content_type 'image/jpeg'
image_file_size 256
after(:create) do |asset|
image_file = Rails.root.join("spec/fixtures/#{asset.image_file_name}")
# cp test image to direcotries
[:original, :medium, :thumb].each do |size|
dest_path = asset.image.path(size)
`mkdir -p #{File.dirname(dest_path)}`
`cp #{image_file} #{dest_path}`
end
end
end
end
Manually cp the test image to the real asset image paths in a factory_girl after create hook. It works like a charm.

Stubbing Paperclip S3 requests in specs

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.

Parsing an Email Attachment w Paperclip - Possible w/o a tempfile?

My rails 3 app on heroku receives incoming emails. I want to be able to accept attachments but can't get rails to process the attachments without erroring.
The ideal would be to pass the attachment provided by ActionMailer.
message_all = Mail.new(params[:message])
message_all.attachments.each do |a|
attachments.each do |a|
.attachments.build(
:attachment => a
)
end
end
It errors with: NoMethodError (undefined methodrewind' for #)`
Where attachments is a model, with attachment is paperclip
Ideas y? Is there a different way to pass the attachment = a , to paperclip?
I tried another approach, creating a tempfile:
tempfile = File.new("#{Rails.root.to_s}/tmp/#{a.filename}", "w+")
tempfile << a.body
tempfile.puts
attachments.build(
:attachment => File.open(tempfile.path) )
The problem with the tempfile is files without extentions "blah" instead of "blah.png" are breaking paperclip which is why I want to avoid the tempfile. and creating Identity errors, imagemagick doesn't know what they are w/o the ext.
hugely appreciate any advice on this.
The problem with the methods that you are using is that they don't contain all of the necessary information for paperclip like the content type and the original filename. I wrote a blog post about this a while back and how you could fake the format and use an email attachment as a paperclip attachment.
The bottom line was to do this:
file = StringIO.new(attachment)
file.class.class_eval { attr_accessor :original_filename, :content_type }
file.original_filename = attachment.filename
file.content_type = attachment.mime_type

Unit testing paperclip uploads with Rspec (Rails)

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.

Resources