Dynamic Attachment Size for Paperclip (Rails) - ruby-on-rails

Is there anyway to have the validates_attachment_size except a dynamic file size limit? Here's an example:
class Document < ActiveRecord::Base
belongs_to :folder
has_attached_file :document
validates_attachment_size :document, :less_than => get_current_file_size_limit
private
def get_current_file_size_limit
10.megabytes # This will dynamically change
end
end
I've tried this but I keep getting an error saying "unknown method". Lambdas and Procs don't work either. Has anyone ever tried this? Thanks

Paperclip doesn't allow to pass function as size limit parameter. So you probably need to write custom validation:
validate :validate_image_size
def validate_image_size
if document.file? && document.size > get_current_file_size_limit
errors.add_to_base(" ... Your error message")
end
end

Long shot...
validates_attachment_size :document, :less_than => :get_current_file_size_limit
Usually when passing a function you have to pass the symbol and not the actual function.

There is a built-in Paperclip validation now:
validates_attachment_size :mp3, :less_than => 10.megabytes
Change mp3 to whatever your paperclipped file's name is.
See this post for more helpful Paperclip tips: http://thewebfellas.com/blog/2008/11/2/goodbye-attachment_fu-hello-paperclip

Related

Rails Paperclip Gem - save parent model ID to path

I have a model of ThreesixtyViewer which also has a nested resource of the ThreesixtyViewerImage model. An image attribute is being saved via the paperclip gem - but I am having issues updating the file path how it's needed.
The images for each ThreesixtyViewer needs to be saved together within one directory that is associated with the specific viewer. For example:
/public/system/threesixty_viewer_images/12/large/filename.jpg
In this example, the 12 in the path would be the id of the specific threesixtyviewer - but I cannot find any examples with that functionality. If the ThreesixtyViewer had an ID of 57, then the path would look like so:
/public/system/threesixty_viewer_images/57/large/filename.jpg
threesixty_viewer.rb
belongs_to :article
has_many :threesixty_viewer_images, dependent: :delete_all
accepts_nested_attributes_for :threesixty_viewer_images, allow_destroy: true
threesixty_viewer_image.rb
belongs_to :threesixty_viewer
has_attached_file :image, styles: { small: "500x500#", large: "1440x800>" },
path: ':rails_root/public/system/:class/:VIEWER_ID/:size/:filename',
url: '/system/:class/:VIEWER_ID/:size/:filename'
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
I know the :path and :url attributes need to beupdated for the has_attached_file within the threesixty_viewer_image.rb - but I am unsure as to how I can get the id for the threesixty_viewer... for now I added a :VIEWER_ID in it's place.
Any help would be greatly appreciated! Thanks in advance for anyone who can lend an eye!
You can add any of the models attributes to that path for this object. I believe you can even go so far as adding anything that the method will respond to, so you can even create path helpers for returning specific strings (such as month it was saved, year, etc).
In your case, ThreesixtyViewerImage is a child model, your table should include a column for the parent model. In your case, that attribute is probably :threesixty_viewer_id
Here is what I think you need for setting that path on threesixty_viewer_image.rb:
has_attached_file :image,
styles: { small: "500x500#", large: "1440x800>" },
path: ":rails_root/public/system/:class/:threesixty_viwer_id/:size/:filename",
url: "/system/:class/:threesixty_viewer_id/:size/:filename"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
EDIT
Everything I said above is dead wrong. My apologies! What you need to use is a Paperclip::Interpolation. Here's a link
Here's what I did to make use:
Create a new file: config/initializers/paperclip_interpolators
Place something like this in that file:
Paperclip.interpolates :threesixty_viewer_id do |attachment, style|
attachment.instance.threesixty_viewer_id
end
Restart your application
Regenerate your paths/folders for the attachments. Here's a Link
Anytime you want another attribute in your path, just add another interpolator! Sorry again for misleading you before.
#colin_hagan is on the right path - I would suggest you look into Paperclip Interpolations:
#app/models/threesixty_viewer_image.rb
class ThreesixtyViewerImage < ActiveRecord::Base
belongs_to :threesixty_viewer
has_attached_file :image,
path: ":rails_root/public/system/:class/:threesixty_viewer_id/:size/:filename",
url: "/system/:class/:threesixty_viewer_id/:size/:filename"
Paperclip.interpolates :threesixty_viewer_id do |attachment, style|
attachment.instance.threesixty_viewer_id
end
end
Notice the double quotes, rather than single.
Double quotes should be used for interpolation - single quotes are for literal strings (I think).
Another thing I found some time back is the paperclip_defaults option -- allowing you to specify styles etc for any attachments:
#config/application.rb
...
config.paperclip_defaults = {
styles: { small: "500x500#", large: "1440x800>" }
}
These can be overridden in your respective models - it's just handy for me, as it means you don't have to explicitly define styles each time you have an attachment.

Paperclip Rails error when trying to refresh

My User model:
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => { :profile => "200x200>", :collab => "300x200>", :msg => "50x50>" }, :default_url => "missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
...
I have just added the :msg and :profile styles and I'm trying to refresh them so they show up properly in my views.
I've tried running:
rake paperclip:refresh CLASS=User
and I get this error:
rake aborted!
ArgumentError: wrong number of arguments (0 for 1)
/home/jrile/rails/cs480/app/models/user.rb:44:in `hash'
/home/jrile/.rvm/gems/ruby-2.1.0/gems/paperclip-4.1.1/lib/paperclip/attachment_registry.rb:42:in `names_for'
/home/jrile/.rvm/gems/ruby-2.1.0/gems/paperclip-4.1.1/lib/paperclip/attachment_registry.rb:16:in `names_for'
/home/jrile/rails/cs480/lib/tasks/paperclip.rake:15:in `obtain_attachments'
Here's line 44 of user.rb (not sure why this has anything to do with paperclip)
def User.hash(token)
Digest::SHA1.hexdigest(token.to_s)
end
I was trying to add an avatar following railstutorial.org.
EDIT: Also, in all my views where I'm trying to display the avatar, it's displaying ":msg" even if I'm trying to display one of the other two. I.E.,
<%= image_tag user.avatar.url(:profile) %>
is showing the 50x50 avatar.
For the first issue, from this SO question
You shouldn't override ruby core methods like object#hash they are made for specific reasons and changing their behavior could cause unexpected results , apparently later on the tutorial this will change to:
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end

Rails 3.2 + Paperclip: Assign default image on user creation

I am working on adding a profile picture to the User model in my Rails app. I've already gotten screenshots successfully working with another model, but for some reason I'm having a lot of difficulties with profile pictures. To handle profile pictures, I've created a new ProfilePics model:
class ProfilePic < ActiveRecord::Base
attr_accessible :image, :user_id
has_attached_file :profile_pic, :default_url => "/system/user_profile_pics/profile.png",
:url => "/system/user_profile_pics/:id/:basename.:extension",
:path => ':rails_root/public:url'
:styles => { :large => "800x400", :thumb => "36x36" }
# **** Associations ****
# State that each profile picture can have an associated user
belongs_to :users
# **** Validations ****
# Only allow the user to upload .bmp, .gif, .jpg, .jpeg, and .png files
validates_attachment_content_type :image, :content_type => /^image\/(bmp|gif|jpg|jpeg|png)/
# Validate the presence of the user id
validates :user_id, :presence => true
# Order all profile pictures by ID, from first to last
default_scope :order => 'profile_pics.id ASC'
end
When a user signs up, he/she should be set the default profile picture. This picture is the image file specified in the :default_url argument for the has_attached_file method. However, I can't seem to figure out how to assign the user the default profile picture in the controller, after the User has been created. I don't want to add the profile picture to the sign up form, and if I just omit it from the controller, I receive the following error message:
undefined method `before_image_post_process'
I haven't made the profile picture a requirement on user creation. I believe I have all of the correct database tables set up, but for some reason I keep getting this error. Here's my attempt at assigning the user the default profile picture in the controller:
if #user.save
# Create a profile picture for the user
#user.profile_pic = ProfilePic.new(:image => nil, :user_id => #user.id)
...
end
When debugging, immediately after saving the user, typing "#user.profile_pic" in the console returns the same 'before_image_post_process' error.
Does anyone have any insight on this issue? Thank you very much in advance!
You are getting this error because you defined the attached file attribute as profile_pic but you are doing the Paperclip validation on the image attribute.
When you define a has_attached_file attribute, Paperclip automatically creates a <name>_post_process callback which it uses later in the validation (where is the name of the has_attached_file attribute).
You created profile_pic_post_process but then the validation is looking for image_post_process, hence the error.
Change your validation line in the ProfilePic model:
# Only allow the user to upload .bmp, .gif, .jpg, .jpeg, and .png files
validates_attachment_content_type :profile_pic, :content_type => /^image\/(bmp|gif|jpg|jpeg|png)/

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

Smarter paperclip validations

I'm using paperclip in a rails app and have the following three validations in my model
validates_attachment_presence :photo
validates_attachment_size :photo, :less_than=>1.megabyte
validates_attachment_content_type :photo, :content_type=>['image/jpeg', 'image/png', 'image/gif']
If the user forgets to add an attachment, all three validations fail and thus, the user is presented with the following three 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
I think it would be best to just show the first error in this instance since the other two errors are purely consequential... I would prefer the user to only ever see the second two errors if an attachment has been added but doesn't meet the validation criteria.
I'm certain there is no pre-baked validation that does this sort of thing and from reading the code in vendor/plugins/paperclip/lib/paperclip.rb I see that the validates_attachment_size method supports the :unless parameter as shown:
def validates_attachment_presence name, options = {}
message = options[:message] || "must be set."
validates_presence_of :"#{name}_file_name",
:message => message,
:if => options[:if],
:unless => options[:unless]
end
So, I was thinking that I could do something like the following:
validates_attachment_size :photo, :less_than=>1.megabyte, :unless=> :photo.blank
But that breaks the app. Anyone have any experience of doing this sort of thing? Would be a nice contribution to the paperclip source code.
EDIT:
I've tried using this:
validates_attachment_size :photo, :less_than=>1.megabyte,
:unless=> Proc.new { |image| image[:photo].nil? }
It doesn't quite work though as I've just managed to upload a 5mb mp3 with this validation in place. But it's promising as the error message doesn't appear when the user has not attached a photo.
validates_attachment_size :photo, :less_than => 1.megabyte,
:unless => Proc.new { |imports| imports.photo_file_name.blank? }
I think you can do it other way. Don't mess with validations. You probably have something like this in your form:
<%= f.error_messages %>
You can remove it and write your own helper to display error messages. Errors are stored in hash:
#photo.errors
Or if you want to get to them through form builder:
f.object.errors

Resources