Ruby on Rails ActiveRecord conditional validation (and more..) - ruby-on-rails

I have a Product model which validates multiple attributes (including a Paperclip image attachment) like so:
validates_presence_of :name
validates_format_of :name, :with => /^([a-zA-Z0-9\ \-]{3,128})$/i
...
has_attached_file :image
validates_attachment_presence :image
validates_attachment_content_type :image, :content_type => ["image/jpeg", "image/png", "image/gif"]
Everything is working fine. What I want now is to make an (unobtrusive) hidden iframe in-place upload script using javascript. My problem is that I cannot just upload the image without the rest of the data, because it will fail validation (no name present) and also I cannot send the rest of the form without the image (same thing, fails validation).
So basically what I need (and don't know how to achieve) is to conditionally apply the model validations according to what the action is currently in progress (uploading the image or editing other data).
I hope I was clear enough. Any help is appreciated. Thanks.

Railscasts have a nice video screencast about conditional validations.

I hate having to wade through a video just to get a simple answer. In fact, I think that a blog entry is superior to a simple tutorial video simply for the fact that it is searchable. Here is a simple case in plain text for anyone else looking:
To validate the presence of password only for the create action, do this:
validates_presence_of :password, :on => :create

A comment for Peter D.
Thank you very much. I'm unable to watch that screen cast presently (though I plan to) and was looking for a speedy, brief answer.
Incorporated your suggestion into a model I have and it's working perfectly. (Though I suspect at some point I'm going to need validation on update when passwords are being changed. I'll take it as "technical debt" now though, in order to move on.)
The bit I added:
validates :password, :presence => true, :confirmation => true, :on => :create

Related

Ruby on Rails - Paperclip :allow_blank with if statement

Some use case background: I am not using Paperclip for avatars or things like that. I want people to make "submissions" that can contain either a file OR a link, depending on the category of the submission they previously chose (the actual file is being uploaded to a table called Submission Details which has a foreign key to Submissions). Some categories have a "link" type, and some categories have an "image" or a "PDF" type. If they select the link category, that subsequent URL info is stored in a separate column than the image/attachment column.
Here is the code in my model to determine which is which:
def nonlink?
if submission.category.submission_file_type == "Mixed (PDF and Images)" || submission.category.submission_file_type == "Images Only"
return true
else
return false
end
end
So ideally I would want :allow_blank if :nonlink? is true or false in the column validation area of the model.
My first question is, does validates_attachment_presence allow for :allow_blank? If it doesn't, what is the best alternative.
Secondly, what is the syntax for creating an if statement with :allow_blank in a model? Right now I have this but not sure it's right:
validates_attachment_presence :attachment, allow_blank: true, if: [:nonlink? == false]
Would appreciate any thoughts, thanks!
Solved this by doing an extra validation line containing validates_presence_of instead of just validates. I made two separate methods, nonlink and link, with one looking like:
validates_presence_of :attachment, if: :nonlink?
Using this I was able to get rid of :allow_blank entirely.

rails 3.2 skip paperclip attachment presence validation on edit action

I am new to Rails and i'm working on my first rails application that uses paperclip for attachments and i'm validating the presence of attachment using validates_attachment :avatar, presence: true which is working perfectly well on create action, but when a user is editing his account settings and doesn't upload a new avatar, there is an error for not having avatar uploaded but i want to skip that validation on edit and only validate on edit if user uploads a new one to replace existing one.
i also have
validates_format_of :avatar, :with => %r{\.(jpg|jpeg|gif|png)$}i,:unless => Proc.new {|m| m[:avatar].nil?}, :message => "Please upload files with the following extensions only
to check format only if present but doesn't seem to be working either
your help would be very much appreciated.
thanks
Use on option.
validates_format_of :avatar ... , :on => :create
http://guides.rubyonrails.org/v2.3.11/activerecord_validations_callbacks.html#on

rails multistep form

id like to make a multistep form with rails using the edit and update actions. so i would like it to be like step 1 of the form, and the user fills in his name, address, and phone number. then the user clicks save and continue and he then fills out his shipping address and then clicks save and continue and fills out his billing address. i saw ryan bates version, but its not what im looking for. i would like the order to be saved after the first step so if the user doesnt finish their form, i can call them and ask them what went wrong. can anyone refer me to a tutorial or give me an example of how to make an order form using the edit and update methods?
Typically this means you'll need to put conditions on your model validations. Some subset of your validations should apply to each form page:
class User
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :street, :if => :on_page_two?
validates_presence_of :city, :if => :on_page_two?
validates_presence_of :postal_code, :if => :on_page_two?
validates_presence_of :state, :if => :on_page_two?
validates_presence_of :country, :if => :on_page_two?
validates_acceptance_of :terms_and_conditions, :if => :on_page_three?
def on_page_two?
# whatever you need to determine the page number
end
def on_page_three?
# whatever you need to determine the page number
end
end
It's not pretty but I highly recommend a pattern like this. Anything more complicated and you'll need to rewrite it when your signup flow changes.
There are different approches to this problem.
My particular prefered solution is to implement something like a "State Machine" in the model. This way, I can persist the progress of a, per example, a multistep form, without having to hasle with more actions than new/create and edit/update.
I'm currently working on a heavy long State Machine application using the State Machine gem for rails.
Hope it helps you!
There's a great Railcast on creating multi-step wizard forms, which you can find here. It uses the Wicked gem.

Specifying two conditions with :if

I have a model which validates presence of an attribute if a check box is checked in the view.
The code is something like this:
validates_presence_of :shipping_first_name, :if => :receive_by_email_is_unchecked
I am looking to have another condition of this validation.So how do I go about doing this ??
My assumption is that something like this would do:
validates_presence_of :shipping_first_name, :if => {:receive_by_email_is_unchecked,:form_first_step_validation}
I am not sure if this is the write way of doing it or not ??
Any suggestions would be appreciated.
You can pass method names in an array:
validates_presence_of :shipping_first_name, :if => [:receive_by_email_is_unchecked, :form_first_step_validation]
Alternatively you can use proc if you don't want to define separate methods just for conditioning validations:
validates_presence_of :shipping_first_name, :if => proc { !receive_by_email? && form_first_step_validation }
I don't think that will work, but have a look at the source code for validates_presence_of https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/presence.rb
You can build your own validator to do exactly that
Ryan Bates covered this in one of his first Rails casts
http://railscasts.com/episodes/41-conditional-validations
It's still valid although syntax may be slightly different for Rails v 3 +
I assume you are working on a Rails 2.x app as the syntax you use is not Rails 3 syntax
Rails 3.x syntax would be
validates :field_1, :field_2, :presence_of => true, :if => # Use a proc, or an array of conditions here. see the valid examples and comments that you have already received for this question from #jimworm and #MichaƂ Szajbe

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