Checking if attribute_changed? in ActiveModel::EachValidator - ruby-on-rails

I have a class Person that has first_name, middle_name, last_name.
I have a customed Each validator for these 3 attributes like so
validates :first_name, :middle_name, :last_name, nameValidator: true
In this validator I want to check if any one of these 3 attributes changed and given a few more conditions I'll validate name. For that I'm trying attribute_changed? but it doesn't work.
I've checked different methods from ActiveModel::Dirty and Activerecod::Dirty but nothing seems to work to check changes in each attribute. What am I missing?
module Person
class nameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return unless can_normalize?(record)
#Normalization code
end
def can_normalize?(record)
anything_new = record.new_record? || attribute_changed?
end
end
end

If you need to check that some attribute was changed, you need to call attribute_changed? method on the record and pass this attribute like this
return unless record.new_record? || record.attribute_changed?(attribute)
Or may be use metaprogramming method like this
return unless record.new_record? || record.public_send("#{attribute}_changed?")
Some notes:
In Ruby we usually use PascalCase for class names and snake_case for hash keys
Validations are used for data validation, not for some normalization. It is usually used to add validation errors

Related

How to validate non-db attributes on an ActiveRecord model?

I have the following class:
class Instance < ActiveRecord::Base
attr_accessor :resolution
validates_format_of :resolution, with: /\A\d+x{1}\d+\d/
def resolution=(res)
validate!
(set the resolution etc)
end
def resolution
(get the resolution and return)
end
end
The resolution attribute is not stored in the database, but is a transient property of the instance.
When it calls validate!, validation fails no matter what what rule is included. Without it, validation doesn't work at all.
How can I validate properly?
You can use =~ operator instead to match a string with regex, using this you can add a condition in setter methods
def resolution=(res)
if res =~ /\A\d+x{1}\d+\d/
# do something
else
# errors.add(...)
end
end
but, as you have already used attr_accessor, you don't have to define getter and setter explicitly, the role of attr_accessor is to define getter and setter methods only, you can do this instead
class Instance < ActiveRecord::Base
attr_accessor :resolution
validates_format_of :resolution, with: /\A\d+x{1}\d+\d/
end
The above solution will work perfectly!
Hope this helps!
The validate! method should be called after setting the variable. In the given example you call validate! when the value is still nil.

Rails: validate by calling class method of other class

I have a lot of models that contains a field called source_name. I need to implement a validator in each of them that will check if the source_name lives up to curtain conditions.
Now I also have another class called SourceNameManager. In this model I have a method called valid_source_name? which takes a source_name_name and returns true or false.
What is the simplest way to make a validation that just validates source_name by calling the external service class SourceNameManager.valid_source_name?('some_name').
I was thinking about something like:
validates :source_name, ->(record) { SourceNameManager.valid_source_name?(record.source_name) }
but I don't think that works
Create a file app/models/source_name_validator.rb:
class SourceNameValidator < ActiveModel::EachValidator
validate_each(record, attribute, value)
unless SourceNameManager.valid_source_name?(value)
record.errors[attribute] << 'is not valid'
end
end
end
Then in each model where you want to validate the source name, add:
validates :source_name, source_name: true

Pass field name as parameter to custom validation method Rails 4

I have a custom validation method:
def my_custom_validation
errors.add(specific_field, "error message") if specific_field.delete_if { |i| i.blank? }.blank?
end
The goal is to disallow parameters which contains [""] pass through validation, but I need to call this method like:
validate :my_custom_validation #and somehow pass here my field names
For example:
validate :my_custom_validation(:industry)
Since you need to validate multiple attributes this way I would recommend a custom validator like so:
class EmptyArrayValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || "cannot be emtpy") if value.delete_if(&:blank?).empty?
end
end
Then validate as
validates :industry, empty_array: true
validates :your_other_attribute, empty_array: true
Or if you don't want to specifically create a class because it is only needed for 1 model you could include this in the model itself
validates_each :industry, :your_other_attribute, :and_one_more do |record, attr, value|
record.errors.add(attr, "cannot be emtpy") if value.delete_if(&:blank?).empty?
end
If you'd like to keep the method based validation you can use a Ruby lambda like below:
validate -> { my_custom_validation(some_model_field) }
See similar question

Rails validate type date?

I think i might be dreaming, but i think i read somewhere that you can validate the type of an attribute of an object before you save it? Something
like validates :transaction_date, :type => Date and that will make sure that its a date?
Is this possible in Rails 3.2? i am trying to find evidence of this on the net. i have already looked here at the rails api and i am going through the ActiveRecord support.
As a complement to the other answers, note that you can define a custom validator to let you use exactly the syntax you proposed:
validates :transaction_date, :type => Date
as follows:
class TypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add attribute, (options[:message] || "is not of class #{options[:with]}") unless
value.class == options[:with]
end
end
Of course, if you want to allow subclasses, you could change the test to use kind_of?.
Rails doesn't support this directly; the closest it comes is probably validates_format_of, or supplying your own custom validator.
I think what you want is the validates_timeliness gem. It not only validates that something is a valid date, but you can specify whether it should be before or after today and various other date range checks.
You can use following gem:
https://github.com/codegram/date_validator
It contains a date validator. You can add a few options.
A new gem has been created to help validate types in rails and an explanatory blog post exists to answer more of the "why" it was created in the first place.
With this library your code would simple be:
class Post < ActiveRecord::Base
validates_type :transaction_date, :date
end
This will throw an exception when anything except a a Date is assigned to :transaction_date.
Here's another custom validator that uses is_a? instead of class, and therefore is a bit less strict and handles things such as Numeric:
class IsAValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add attribute, (options[:message] || "is not a #{options[:with]}") unless
value.is_a?(options[:with])
end
end
Usage:
validates :number, is_a: Numeric

how attr_accessor works in ActiveResource rails 3?

How does attr_accessor works in ActiveResource?
class User < ActiveResource::Base
attr_accessor :name
end
How its different from attr_accessor in ActiveRecord?
attr_accessor is built into Ruby, not rails. You may be confusing it with attr_accessible, which is part of ActiveRecord. Here's the difference:
attr_accessor
Take a class:
class Dog
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
self.first_name = first_name
self.last_name = last_name
end
end
attr_accessor creates a property and creates methods that allow it to be readable and writeable. Therefore, the above class would allow you to do this:
my_dog = Dog.new('Rex', 'Thomas')
puts my_dog.first_name #=> "Rex"
my_dog.first_name = "Baxter"
puts my_dog.first_name #=> "Baxter"
It creates two methods, one for setting the value and one for reading it. If you only want to read or write, then you can use attr_reader and attr_writer respectively.
attr_accessible
This is an ActiveRecord specific thing that looks similar to attr_accessor. However, it behaves very differently. It specifies which fields are allowed to be mass-assigned. For example:
class User
attr_accessible :name, :email
end
Mass assignment comes from passing the hash of POST parameters into the new or create action of a Rails controller. The values of the hash are then assigned to the user being created, e.g.:
def create
# params[:user] contains { name: "Example", email: "..."}
User.create(params[:user])
#...
end
For the sake of security, attr_accessible has to be used to specify which fields are allowed to be mass-assigned. Otherwise, if the user had an admin flag, someone could just post admin: true as data to your app, and make themselves an admin.
In summary
attr_accessor is a helper method for Ruby classes, whereas attr_accessible is an ActiveRecord thing for rails, to tighten up security.
You don't need to have attr_accessor to work with ActiveResource.
The base model (ActiveResource::Base) contains the #attributes hash in which you can 'dump' properties as you wish. (you should be careful though on what params you allow)
The way it does this, is by handling the method_missing? method.
You can take a look here
If you define attr_accessor, what ruby does is that it creates a setter and a getter method, so it will break the method_missing functionality since it will never get to execute that code.
If you still want to use attr_accessor, you should create a Concern something like this:
module Attributes
extend ActiveSupport::Concern
module ClassMethods
def attr_accessor(*attribs)
attribs.each do |a|
define_method(a) do
#attributes[a]
end
define_method("#{a}=") do |val|
#attributes[a] = val
end
end
end
end
end

Resources