Custom Rails Validation throwing undefined method error - ruby-on-rails

I need to validate that a field is a positive value, and wrote a little methid in lib/validations.rb which does exactly that and added it to my model. However, now one of my rspec tests is failing complaining...
NoMethodError: undefined method '<' for nil:NilClass"
...when the value is undefined. I've also added validates_presence_of :price to my model, above my custom validates_positive_or_zero method.
I would think that validates_presence_of would be called first, and when the field is nil it wouldn't run further validation. Do I have to change my validates_positive_or_zero method to check if the field exists to fix this? Can anyone give me some insight into what's going on?

Rails already has this:
http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_numericality_of
Use :greater_than => 0

All validators are run, regardless of the order they're defined in. That way the error array is filled with all the validation errors, allowing the end user to fix all the errors and not just one at a time.
So yes, you need to check if a value exists for the field, and that the value can be compared to a number. For example, uou probably also need to take into account that the value can be anything other than a number - a blank String would be a likely value - which would also cause an error.
But it's all academic, because as Stephen pointed out, just use validates_numericality_of :field, ::greater_than_or_equal_to => 0

Related

Rails - instance_method_already_implemented causing issue while displaying the value of field

I've used model_name as a field name which is causing an error
model_name is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
I looked at other similar question on SO and tried overriding the instance_method_already_implemented method as below
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'model_name'
super
end
end
and also tried using the gem named safe_attributes. The only diff in using the gem was that I did not need to used the above method as it intends.
I'm facing issues while displaying the value of this column. When I implement either of the above two, the value for the field is the name of the Model in which its present. In my case, Machinery.
If I use return false in the above class method, I get the error saying undefined methodparam_key' for "xyz":Stringwhile on the edit page wherexyz` is a value for the model_name column for that record but the value is correctly displayed on the index page.
I'm not sure how to proceed with this error. Any guidance will be helpful. Please let me know if any other details are required.
Thanks in Advance

rails if statement in controller - checking nil

I have an if statement in my update action in one of my controllers. It looks like this:
if !#bid.attributes.values.include?(nil)
build(#bid.id)
end
I am checking to see if there are any nil valued attributes in my Bid object before building a bid report. I know the build method works fine because it builds a report when not wrapped in the if statement. When it is wrapped in this if statement, it doesn't run. I have checked to make sure that there are no nil values in the object. I went into the rails console and all attributes have non-nil values. In addition, I am able to check this in the views to confirm that there are no nil values.
I have also tried writing as:
build(#bid.id) unless #bid.attributes.values.include?(nil)
and a couple other variations. None are allowing the build to run.
Your code seems fine, I'm betting it's your data that's the problem instead. Mostly likely, assuming this an active record instance, the attribute is id which will be nil until the new record gets saved.
What do you get in the terminal when you add this line right before your if?
puts #bid.attributes.to_yaml
You should be able to see what has values and what does not. And I'm pretty sure at least one of those values is nil.
I would recommend being more explicit about exactly which fields are required. And this is exactly what validations are for.
class Person < ActiveRecord::Base
validates :name, presence: true
end
You explicitly validate each field so that when it's absent you get a very specific error message about why: "Person name can't be blank." So instead of wondering why it wont save, you get told why at the point it fails to save.

Rails Active Record - How to do validation which calls method rather than throws error

Active Record validations throw an error when they fail. What I have in a model is
validate_format_of :field_which_cannot_have_spaces, :with => /^[^\s]+$/, :message => "Some error message"
What I want instead, is for a string replacement to substitute spaces for underscores (snake_case).
The advantages of using validation for me, are that it runs every time the field is changed unless save(validate: false), and that I don't need to repeat the replacement in the create and update controller methods.
Front end javascript solutions won't help if the user hacks the form... a rails solution is needed!
It sounds like you want a callback rather than a validation. This can run each time your object is modified.
So, to remove spaces from your field before the object is saved you can do:
before_save :remove_spaces_from_x
def remove_spaces_from_x
self.field_which_cannot_have_spaces.gsub!("\s","_")
end
Note also that validation do not always raise an error when they fail. If you use save! or create! then an error is raised but if you use the equivalent save or create then no error is raised, false is returned and the object's errors are populated with details of the validation failure.
Co-worker just told me to do the following in the model:
def field_which_cannot_have_spaces=(input_from_form)
super(input_from_form.gsub("\s","_"))
end
This will change the value as it is set.
"Validations are for informing the client there is a problem, and shouldn't be doing something other than throwing an error."
Hope this helps someone else...

Validation: how to check for specific error

I know how to check an attribute for errors:
#post.errors[:title].any?
Is it possible to check which validation failed (for example "uniqueness")?
Recently I came across a situation where I need the same thing: The user can add/edit multiple records at once from a single form.
Since at validation time not all records have been written to the database I cannot use #David's solution. To make things even more complicated it is possible that the records already existing in the database can become duplicates, which are detected by the uniqueness validator.
TL;DR: You can't check for a specific validator, but you can check for a specific error.
I'm using this:
# The record has a duplicate value in `my_attribute`, detected by custom code.
if my_attribute_is_not_unique?
# Check if a previous uniqueness validator has already detected this:
unless #record.errors.added?(:my_attribute, :taken)
# No previous `:taken` error or at least a different text.
#record.errors.add(:my_attribute, :taken)
end
end
Some remarks:
It does work with I18n, but you have to provide the same interpolation parameters to added? as the previous validator did.
This doesn't work if the previous validator has written a custom message instead of the default one (:taken)
Regarding checking for uniqueness validation specifically, this didn't work for me:
#post.errors.added?(:title, :taken)
It seems the behaviour has changed so the value must also be passed. This works:
#post.errors.added?(:title, :taken, value: #post.title)
That's the one to use ^ but these also work:
#post.errors.details[:title].map { |e| e[:error] }.include? :taken
#post.errors.added?(:title, 'has already been taken')
Ref #34629, #34652
By "taken", I assume you mean that the title already exists in the database. I further assume that you have the following line in your Post model:
validates_uniqueness_of :title
Personally, I think that checking to see if the title is already taken by checking the validation errors is going to be fragile. #post.errors[:title] will return something like ["has already been taken"]. But what if you decide to change the error message or if you internationalize your application? I think you'd be better off writing a method to do the test:
class Post < ActiveRecord::Base
def title_unique?
Post.where(:title => self.title).count == 0
end
end
Then you can test if the title is unique with #post.title_unique?. I wouldn't be surprised if there's already a Rubygem that dynamically adds a method like this to ActiveRecord models.
If you're using Rails 5+ you can use errors.details. For earlier Rails versions, use the backport gem: https://github.com/cowbell/active_model-errors_details
is_duplicate_title = #post.errors.details[:title].any? do |detail|
detail[:error] == :uniqueness
end
Rails Guide: http://guides.rubyonrails.org/active_record_validations.html#working-with-validation-errors-errors-details

Ruby: Dynamically calling available methods raising undefined method (metaprogramming)

I have an Activerecord object called Foo:
Foo.attribute_names.each do |attribute|
puts Foo.find(:all)[0].method(attribute.to_sym).call
end
Here I'm calling all attributes on this model (ie, querying for each column value).
However, sometimes, I'll get an undefined method error.
How can ActiveRecord::Base#attribute_names return an attribute name that when converted into its own method call, raises an undefined method error?
Keep in mind this only happens on certain objects for only certain methods. I can't identify a pattern.
Thank you.
The NoMethodError should be telling you which method does not exist for what object. Is it possible that your find returns no record? In that case, [][0] is nil and you will get a NoMethodError for sure.
I would use .fetch(0) instead of [0], and you will get a KeyError if ever there is no element with index 0.
Note: no need for to_sym; all builtin methods accept name methods as strings or symbols (both in 1.8 and 1.9)
Maybe something to do with access? Like if a class has an attr_protected attribute, or something along that line. Or for attributes that are not database columns, which have no accessors defined?

Resources