Given a model:
class Person
validates_lenght_of :name, :maximum => 50
end
I have some view code that shows a countdown and enforces this maximum. However I hard coded the number 50 into that view code. Is there a way to extract this number from the model?
Something like:
Person.maximum_length_of_name
I tried this:
Person.validators_on(:name)
=> [#<ActiveRecord::Validations::UniquenessValidator:0x000001067a9840 @attributes=[:name], @options={:case_sensitive=>true}, @klass=Trigger(id: integer, name: string, created_at: datetime, updated_at: datetime, user_id: integer, slug: string, last_update_by: integer)>, #<ActiveModel::Validations::PresenceValidator:0x000001067a6c30 @attributes=[:name], @options={}>, #<ActiveModel::Validations::LengthValidator:0x000001067a3f08 @attributes=[:name], @options={:tokenizer=>#<Proc:0x00000101343f08@/Users/sjors/.rvm/gems/ruby-1.9.2-p0/gems/activemodel-3.0.6/lib/active_model/validations/length.rb:9 (lambda)>, :maximum=>50}>]
The information is in there, but I don't know how to extract it:
Use validators_on method
irb(main):028:0> p Person.validators_on(:name)[0].options[:maximum]
50
=> 50
As @Max Williams mentioned it works only on Rails 3
The problem with @nash answer is that validators do not own a certain order. I figured out how to do the same thing with just some more code but in some kind of safer mode ('cause you can add more validators later and break the order you get it):
(Person.validators_on(:name).select { |v| v.class == ActiveModel::Validations::LengthValidator }).first.options[:maximum]
I think it does only work for Rails 3 too.
[Edit 2017-01-17] Carefull my answer is old (2012) and was for Rails 3. It may not work / be ideal for newer Rails versions.
Just to bring a little more DRY spirit, you could create a generic class method to get maximum "length_validator" value on any attribute, like so:
Create a module in your lib directory and make it extend ActiveSupport::Concern:
module ActiveRecord::CustomMethods
extend ActiveSupport::Concern
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecord::CustomMethods)
Add the "module ClassMethods" in it and create the "get_maximum" method:
module ActiveRecord::CustomMethods
extend ActiveSupport::Concern
module ClassMethods
def get_maximum(attribute)
validators_on(attribute).select{|v| v.class == ActiveModel::Validations::LengthValidator}.first.options[:maximum]
end
end
end
EDIT 1: Configuration
You'll also have to add a require in one of your initializers.
For instance, here are my configurations:
config.autoload_paths +=
%W(#{config.root}/lib/modules)
Note: this is not required, but best practice if you want to put there some of your custom classes and modules that you share between your apps.
require "active_record/extensions"
And that should do it! Restart your server and then...
END EDIT 1
And then you should be able to do something like this:
<%= form_for @comment do |f| %>
<%= f.text_area(:body, :maxlength => f.object.class.get_maximum(:body)) #Or just use Comment.get_maximum(:body) %>
<% end %>
I hope it will help others! :) Of course you can customize the method the way you want and add options and do fancy stuff. ;-)
More concise:
Person.validators_on(:name).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:maximum]
Uses detect{}
instead of select{}.first
and is_a?
instead of class ==
.
That works with Rails 4.1 as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With