Rails - Where to add validation code? (Controller or Model) - ruby-on-rails

I'm new to Rails, and am following this tutorial
I creates a simple model called HighScores.
I would like to customize this so that I can add a validation method for the score. I know there are shortcuts like validates_ that we can use, but for the purpose of learning, I'd like to write a method that ensures the score is between a certain range.
Where should the validate method go? In models/high_score.rb or in controllers/high_scores_controllers.rb? Or maybe in `/helpers/high_scores_helper.rb?

Validation that the model has correct data should go in the model itself. This ensures that any future attempt to save the model's data will use this validation, regardless of the path taken.
models\high_score.rb
Also -- FWIW, the validates methods aren't short cuts, they are well tested code that you should embrace and use.

The validation should go in models.
Here is an example of a range validation:
validates :score, :numericality => { :greater_than => 0 }
validates :score, :numericality => { :less_than => 100 }

Related

Is there a better way of validating a non model field in rails

I have a form field in ROR 4 app called as 'measure'. It is not a database column, but its values will help model create child entries of its own via acts_as_tree : https://github.com/rails/acts_as_tree
I have to throw a validation when 'measure' is invalid. So I have created a virtual attribute known as measure and check for its validations only on a certain condition.
model someModel
attr_accessor :measure
validates_presence_of :measure, :if => condition?
Problem is when I am saving the code, I am thrown a validation which is fine. I am also thrown the same validation when I am trying to update the record in some other method of the model. The only way to surpass that is by writing this code:
# I do not want to do this, is there a better way?
self.measure = "someRandomvalue"
self.save
I am making this as virtual attribute only for throwing validations. Is there a better way of throwing validations? The form has other validations, I do not want the error for this validations to be shown differently just because it is not an attribute.
I want it to validated only when active record is saved via create and update action of the controller and not when it is being updated by some random method of model.
I have seen other developers in my team doing similar thing and was always curious about one thing - "What are you trying to achieve doing things the way you are doing?". You see, I am not sure if validators should be used for values that will not be serialized.
Anyways, you may try using format validator instead of presence, which worked in my team's case:
# Rails 3/4
validates :measure, format: { with: /^.+$/, allow_nil: true }
# Rails 2
validates_format_of :measure, :with => /^.+$/, :allow_nil => true
You may also try using allow_blank instead of allow_nil.
I would rather create a custom validator along the lines of validates_accessor_of for values that I know will never be serialized.
HTH

Rails Validation on presence of two or more entities

I've got two columns by name,
product_available_count (integer) and product_available_on (date).
I need to perform a model level validation on these columns.
The validation should check that if product_required is true then either of fields should be populated.
When a Product Manager fill in the catalogue, we need to perform a model level validation that checks that he should fill in either of the fields.
Suggest me any elegant way of writing a custom validation for my requirement.
I've tried this approach
validates :product_available_count_or_product_available_on if product_required?
def product_available_count_or_product_available_on
//logic ???
end
Is Custom validation the only way forward to my requirement. Can I use Proc or any other approach to write a better code.
I think Custom validation is best approach for this kind of problem
validate :product_available_count_or_product_available_on if product_required?
def product_available_count_or_product_available_on
if [product_available_count, product_available_on].compact.blank.size == 0
errors[:base] << ("Please select alteast one.")
end
end
but if you really donot want to write custom validation then try this
validates :product_available_count, :presence => { :if => product_required? && product_available_on.blank? }
validates :product_available_on, :presence => { :if => product_required? && product_available_count.blank? }

rails validation of uniqueness on update

I have the following validation rule on one of the models,
validates :reciept_num, :presence => true,
:numericality => { :only_integer => true },
:uniqueness => true,
:on => :update,
:if => "!status_id.nil?"`
Now, when I update the object using update_attributes method it gives me following error
reciept_num: has already been taken.
While updating the the object I'm not changing the reciept_num attribute? So, why does this validation fails on update?
If I'm not updating the value, it must be the old one and hence should pass validation. Am I missing something.
First off, validations don't run based on whether the attribute has changed or not (unless of course you ask for that explicitly). Everytime a record with a uniqueness validation saves and the validation can run (as defined by :on, :if, :unless options) it will check whether there are any instances other than itself with the value that is supposed to be unique.
Since you've got conditions on your validation, I imagine you could end up creating two instances with the same receipt num, but where both have a null status_id. Set the status_id column and the validation kicks into action and finds the other instances.
Another thing is that since your validation is on update only you could create multiple instances with the same receipt num, again trying to update the record would trigger the validation.
I'm only guessing at the precise scenarios though.

Rails: Validate unique combination of 3 columns

Hi I wan't to validate the unique combination of 3 columns in my table.
Let's say I have a table called cars with the values :brand, :model_name and :fuel_type.
What I then want is to validate if a record is unique based on the combination of those 3. An example:
brand model_name fuel_type
Audi A4 Gas
Audi A4 Diesel
Audi A6 Gas
Should all be valid. But another record with 'Audi, A6, Gas' should NOT be valid.
I know of this validation, but I doubt that it actually does what I want.
validates_uniqueness_of :brand, :scope => {:model_name, :fuel_type}
There is a syntax error in your code snippet. The correct validation is :
validates_uniqueness_of :car_model_name, :scope => [:brand_id, :fuel_type_id]
or even shorter in ruby 1.9.x:
validates_uniqueness_of :car_model_name, scope: [:brand_id, :fuel_type_id]
with rails 4 you can use:
validates :car_model_name, uniqueness: { scope: [:brand_id, :fuel_type_id] }
with rails 5 you can use
validates_uniqueness_of :car_model_name, scope: %i[brand_id fuel_type_id]
Depends on your needs you could also to add a constraint (as a part of table creation migration or as a separate one) instead of model validation:
add_index :the_table_name, [:brand, :model_name, :fuel_type], :unique => true
Adding the unique constraint on the database level makes sense, in case multiple database connections are performing write operations at the same time.
To Rails 4 the correct code with new hash pattern
validates :column_name, uniqueness: {scope: [:brand_id, :fuel_type_id]}
I would make it this way:
validates_uniqueness_of :model_name, :scope => {:brand_id, :fuel_type_id}
because it makes more sense for me:
there should not be duplicated "model names" for combination of "brand" and "fuel type", vs
there should not be duplicated "brands" for combination of "model name" and "fuel type"
but it's subjective opinion.
Of course if brand and fuel_type are relationships to other models (if not, then just drop "_id" part). With uniqueness validation you can't check non-db columns, so you have to validate foreign keys in model.
You need to define which attribute is validated - you don't validate all at once, if you want, you need to create separate validation for every attribute, so when user make mistake and tries to create duplicated record, then you show him errors in form near invalid field.
Using this validation method in conjunction with ActiveRecord::Validations#save does not guarantee the absence of duplicate record insertions, because uniqueness checks on the application level are inherently prone to race conditions.
This could even happen if you use transactions with the 'serializable' isolation level. The best way to work around this problem is to add a unique index to the database table using ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the rare case that a race condition occurs, the database will guarantee the field's uniqueness.
Piecing together the other answers and trying it myself, this is the syntax you're looking for:
validates :brand, uniqueness: { scope: [:model_name, :fuel_type] }
I'm not sure why the other answers are adding _id to the fields in the scope. That would only be needed if these fields are representing other models, but I didn't see an indication of that in the question. Additionally, these fields can be in any order. This will accomplish the same thing, only the error will be on the :model_name attribute instead of :brand:
validates :model_name, uniqueness: { scope: [:fuel_type, :brand] }

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

Resources