Rails unit tests for functions which use attachments - ruby-on-rails

I'm creating a program which will be examining images which are uploaded by the users logged in. I have the RMagick code written up to do the examination (basically finding out if a pixel black is in an image), but I don't know how to write the unit tests for this model.
Currently I'm using paperclip to attach the uploaded file to the model, which I understand uses a number of fields in the database for tracking the files. How should I set up my fixtures so that I can do unit testing on the same data every time?
My model is currently:
class Map < ActiveRecord::Base
has_attached_file :image, :styles => { :small => "150x150>" }
validates_attachment_presence :image
validates_uniqueness_of :name, :message => "must be unique"
def pixel_is_black(x, y)
<code to return true if position (x,y) in image is black>
end
end

Best to read up on the usage of fixture_file_upload in your functional tests
For unit tests, i normally have a teardown method that deletes the files once done they have been modified (though it can be used to copy over originals, whatever you want - see the fileutils library for that)
def setup
FileUtils.cp 'original.jpg', '/path/to/where/file/exists/in/fixture.jpg'
end
def teardown
Fileutils.rm '/path/to/where/file/exists/in/fixture.jpg', :force => true
end
http://api.rubyonrails.org/classes/ActionController/TestProcess.html#M000406
http://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html

Related

Paperclip add image from URL in has_many association

I've this models:
Prodcuts -> Products_Images.
I can add multiple images from a form uploading the image that it's my computer. I want to add images from URLs instead of locally images.
Paperclip has added this feature:
https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL
but I don't know how to apply it in a has_many association.
I've tried adding a method in ProductImages model and call it for each URL after product is created. I don't know if I must use this method directly in Product model.
Where should I try to put the method of the wiki of Paperclip?
Here is a great gist (that I did not write). It should get you there: https://gist.github.com/jgv/1502777
require 'open-uri'
class Photo < ActiveRecord::Base
has_attached_file :image # etc...
before_validation :download_remote_image, :if => :image_url_provided?
validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => 'is invalid or inaccessible'
private
def image_url_provided?
!self.image_url.blank?
end
def download_remote_image
io = open(URI.parse(image_url))
self.original_filename = io.base_uri.path.split('/').last
self.image = io
self.image_remote_url = image_url
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
All credit to the author.
In the future, it's usually best to post the code with your attempt to solve the problem.

Rails4 and Paperclip 4.0.2 callback turns as infinite loop

I set paperclip to My "Document" model, with a fairly standard config. It works well, but I want to separate the addtitional styles file generation inside a background job (using Resque).
(I would like to stop the creation of the :orginal style to to assign a file manually, but it doesn't seem to be possible)
So, to extract styles inside a background job, I first stop paperclip styles processors.
Then I call the reprocess! inside an after_save callback to generate them.
Doing this put the update action inside an infinite loop, and it is exactly when I call the reprocess!
Here is my Document model (simplified for understanding purpose)
class Document < ActiveRecord::Base
has_attached_file :attachment
before_post_process :stop_process
after_save :process_styles #same result with after_update (since user can only add attachment when updating profile)
# Kill all paperclip styles
#still generate the :orginal style. Block that too to copy it manually from remote folder would be nice
def stop_process
false
end
#call for the generation of the additional styles (:medium, :thumb)
def process_styles
Profile.processDocumentJob(id)
end
#will be a background job
def self.processDocumentJob(id)
document = Document.find(id)
document.attachment.reprocess!
document.save(validate: false)
end
end
Despite my document.save(validate: false), the process loop during the updating.
I tried after_update callback, tweak my code and conditions, with no success.
Thank you in advance for your precious help
I know this is an old question, but happened to me today and solved this way....a bit hacky btw... I'm not proud of my solution. This works in Rails 5.
Added a virtual attribute to my model "skip_callbacks"
Added a method to check if the attribute is true "should_skip_callbacks?"
In my callback added "unless: :should_skip_callbacks?" condition
Before the paperclip reprocess, set the virtual attribute "skip_callbacks" to true
Here's a simplified version of my model:
class Organization < ApplicationRecord
attr_accessor :skip_callbacks
has_attached_file :logo, styles: { header: "380x160>" }
validates_attachment :logo, content_type: {content_type: ['image/png','image/jpg','image/gif']}
after_save :reprocess_logo, unless: :should_skip_callbacks?
def should_skip_callbacks?
self.skip_callbacks
end
def reprocess_logo
self.skip_callbacks = true
self.logo.reprocess!
end
end

Custom Paperclip processor not being called

I have a user model that is generated using Devise. I am extending this model using paperclip to enable file upload and also the processing of a file using a custom paperclip processor.
My paperclip field is declared in the user model as follows. PaperClipStorage is a hash that I create with the paperclip variables. Also, the being stored on AWS S3.
has_attached_file :rb_resume, PaperclipStorageHash.merge(:style => { :contents => 'resume_contents'}, :processors => [:resume_builder])
validates_attachment_content_type :rb_resume, :if => lambda { |x| x.rb_resume? }, :content_type => ['application/pdf', 'application/x-pdf', 'application/msword', 'application/x-doc']
The validates_attachment_content_type check is being done to make sure that it only processes pdf and MS word files.
My processor looks as follows
module Paperclip
class ResumeBuilder < Processor
def initialize(file,options = {}, attachment = nil)
#file = file
#attachment = attachment
puts "Attachment is not null " if !attachment.nil?
end
def make
rb = MyModule::MyClass.new(#file.path) ### Do something with the file
section_layout = rb.parse_html
#attachment.instance_write(:whiny, section_layout)
#file
end
end
end
In my user model I also have an after_save callback that is supposed to take the section_layout generated in the processors make method. Code is as follows
after_save :save_sections
def save_sections
section_layout = rb_resume.instance_read(:whiny)
# Do something with section_layout...
end
Now my problem is that the processor code is never being called, and I can't figure out why.
Because of that the section_layout variable is always nil.
Another point to note is that the same model also has two other has_attached_file attributes. None of the other two use a custom processor.
I've been struggling with this for last 3 hours. Any help would be greatly appreciate.
Thanks
Paul
Error in my has_attached_file declaration
has_attached_file :rb_resume, PaperclipStorageHash.merge(:style => { :contents => 'resume_contents'}, :processors => [:resume_builder])
should actually be
has_attached_file :rb_resume, PaperclipStorageHash.merge(:styles => { :contents => 'resume_contents'}, :processors => [:resume_builder])
Notice the plural styles as opposed to singular style

Rails App Failing to Save Image Uploads to Amazon S3 Using Paperclip and Mongoid

I'm trying to store images associated with a Coupon object in an Amazon S3 instance. My Rails 3.1 application uses Mongoid for document storage, and I'm not attempting to introduce Paperclip (via mongoid-paperclip) to store images for coupons on Amazon S3.
I've created a permission group on Amazon S3 and added a user; the valid permissions have been added to my application (which I can verify, because if I remove or alter the permissions, I receive an error), but when I attempt to save a file, the file's information is stored in the database, but the file is not uploaded. If I remove mongoid-paperclip from the equation, files are not stored locally either (although I do see that they exist in a temp folder on my local machine and are processed via ImageMagick).
Models
My Coupon objects embeds many Image objects as such:
class Coupon
include Mongoid::Document
include Mongoid::Timestamps
# Relationships
embeds_one :image, as: :imageable
# Database Schema
field :name
field :description
field :expires, type: Date
# Validation
validates :name, :description, :presence => true
end
class Image
include Mongoid::Document
include Mongoid::Paperclip
include Mongoid::Timestamps
# Relationships
embedded_in :imageable, polymorphic: true
has_mongoid_attached_file :file,
:path => ':id/:style.:extension',
:storage => :s3,
:s3_credentials => File.join(Rails.root, 'config', 's3.yml'),
:styles => {
:original => ['920x920>', :jpg]
}
end
I do not see any output from Paperclip in my console or logs and cannot determine how to enable such output. The only information logged in relation to the file being uploaded is as follows, immediately before the page is redirected after successfully updating attributes:
| Command :: identify -format %wx%h '/var/folders/ff/vxzlz741287dsr006bv2s59c0000gn/T/stream20111022-80997-o1pqk.png[0]'
| Command :: convert '/var/folders/ff/vxzlz741287dsr006bv2s59c0000gn/T/stream20111022-80997-o1pqk.png[0]' -resize "920x920>" '/var/folders/ff/vxzlz741287dsr006bv2s59c0000gn/T/stream20111022-80997-o1pqk20111022-80997-5z9phe.jpg'
This seems to be a problem with the identify command provided by your local ImageMagick install. Do you have ImageMagick's libraries installed on your system? I had a similar problem once and it seems that installing ImageMagick from brew fixed it up. FYI: My problem was caused by bad symbolic links, the identify command (and others) simply weren't linked correctly from when I compiled ImageMagick from source.
Maybe, This code is not using callbacks.
Paperclip use the callback of after_save to save a image.
embeds_one :image, as: :imageable, cascade_callbacks: true
You should use cascade callbacks of Mongoid.
http://mongoid.org/en/mongoid/docs/callbacks.html

Paperclip to upload images to S3 in Rails. The files upload at a very slow rate. A work-around?

I'm working on a rails app where the user will be uploading large quantities of images.
My current setup: Using SWFUpload to upload multiple files at once using the Paperclip plugin with S3 storage. After the original image is uploaded to S3, Delayed_Job is used for the post processing (thumbnails, etc).
The problem I have is that the images upload at a very slow rate. I'm assuming the default Paperclip setup is that the image will go from the user to -> my server to -> s3.
I was thinking that I can have the images upload directly to s3 but I'm not sure how to implement that with Paperclip and post processing. I couldn't find any plugins or examples dealing with this.
Does anyone have suggestions? If not, can you point me in the right direction?
Thanks in advance!
Tim
I've ran into this same problem a few times. The way I solved it was by creating 2 models, a Image model and a TempImage model, which inherits from the Image model. This requires you to have a type column on your Image table. The TempImage model saves the image locally, then when you access it from the Image model directly and resave it, it will follow whatever is defined in the Image model, being Amazon S3.
Example:
# Will save in the database as a TempImage inside the Image table
temp = TempImage.create(:asset => File.new('some_path', 'r'))
# When you find it again through the Image model, it bypasses the type column
# so next time you save it, it is saved as an Image.
amazon = Image.find(temp.id)
amazon.save!
Here is my delayed job:
class MoveToS3Job < Struct.new(:temp_revision_id)
def perform
upload = Image.find(temp_revision_id)
temp_path = File.expand_path("tmp/uploads/#{upload.asset_file_name}", Rails.root)
upload.asset = File.new(temp_path, 'r')
upload.save!
if File.exists?(temp_path) && !File.directory?(temp_path)
File.delete(temp_path)
end
rescue ActiveRecord::RecordNotFound
# If the record wasn't found, do some sort of
# error report, but don't keep it in the queue.
end
end
Here is the TempImage model:
class TempImage < Image
has_attached_file :asset, {
:path => ":rails_root/tmp/uploads/:basename_:updated_at.:extension"
}
end
Then the original Image model:
class Image < ActiveRecord::Base
# Validations
validates :asset, :presence => true
# Paperclip
has_attached_file :asset, :styles => {
:preview => ['100x100#', :png],
:thumb => ['50x50#', :png]
},
:default_style => :thumb,
:storage => :s3,
:bucket => 'bucket-name',
:s3_credentials => File.expand_path('config/s3.yml', Rails.root),
:path => "photos/:id_partition/:style.:extension"
end
Your original Image model should always contain your post processing, as that will be done in the background.
You can always overwrite some methods to make it a little cleaner, but this gives you a better idea of how it works and what you need to do to so you can have it work like you want it to.
If you end up going the route of uploading directly to S3 which offloads the work from your Rails server, please check out my sample projects:
Sample project using Rails 3, Flash and MooTools-based FancyUploader to upload directly to S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader
Sample project using Rails 3, Flash/Silverlight/GoogleGears/BrowserPlus and jQuery-based Plupload to upload directly to S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload
By the way, you can do post-processing with Paperclip using something like this blog post describes:
http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip

Resources