Как получить максимальную длину, настроенную для проверки ActiveRecord?

Учитывая модель:

class Person
  validates_lenght_of :name, :maximum => 50
end

У меня есть некоторый код представления, который показывает обратный отсчет и устанавливает этот максимум. Однако я жестко закодировал номер 50 в этом коде зрения. Есть ли способ извлечь это число из модели?

Что-то вроде:

Person.maximum_length_of_name

Я пробовал это:

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:[email protected]/Users/sjors/.rvm/gems/ruby-1.9.2-p0/gems/activemodel-3.0.6/lib/active_model/validations/length.rb:9 (lambda)>, :maximum=>50}>]

Информация там, но я не знаю, как ее извлечь:

Ответы

Ответ 1

Используйте validators_on метод

irb(main):028:0> p Person.validators_on(:name)[0].options[:maximum]
50
=> 50

Как сказал @Max Williams, он работает только на Rails 3

Ответ 2

Проблема с ответом @nash заключается в том, что валидаторы не владеют определенным порядком. Я выяснил, как сделать то же самое с помощью только какого-то кода, но в каком-то более безопасном режиме (потому что вы можете добавить больше валидаторов позже и нарушить порядок, который вы получите):

(Person.validators_on(:name).select { |v| v.class == ActiveModel::Validations::LengthValidator }).first.options[:maximum]

Я думаю, что он работает только для Rails 3.

Ответ 3

[Edit 2017-01-17] Carefull мой ответ старый (2012) и был для Rails 3. Он может не работать/быть идеальным для новых версий Rails.


Чтобы принести немного более суровый дух, вы можете создать универсальный метод класса, чтобы получить максимальное значение "length_validator" для любого атрибута, например:

Создайте модуль в своем каталоге lib и увеличьте ActiveSupport:: Концерн:

module ActiveRecord::CustomMethods
  extend ActiveSupport::Concern
end

# include the extension 
ActiveRecord::Base.send(:include, ActiveRecord::CustomMethods)

Добавьте в него "модуль ClassMethods" и создайте метод get_maximum:

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

РЕДАКТИРОВАТЬ 1: Конфигурация

Вам также нужно добавить требование в один из ваших инициализаторов.

Например, вот мои конфигурации:

  • Я поместил свой файл в lib/modules/active_record/extensions.
  • Я добавил это в мои autoload_paths: config.autoload_paths += %W(#{config.root}/lib/modules) Примечание: это не требуется, но лучше всего использовать, если вы хотите разместить некоторые из ваших пользовательских классов и модулей, которыми вы делитесь между вашими приложениями.
  • В одном из моих инициализаторов (config/initializers/custom.rb) я добавил эту строку: require "active_record/extensions"

И это должно быть! Перезагрузите сервер, а затем...

END EDIT 1

И тогда вы сможете сделать что-то вроде этого:

<%= form_for @comment do |f| %>
  <%= f.text_area(:body, :maxlength => f.object.class.get_maximum(:body)) #Or just use Comment.get_maximum(:body) %>
<% end %>

Надеюсь, это поможет другим!:) Конечно, вы можете настроить метод так, как вы хотите, и добавить параметры и сделать фантазию.; -)

Ответ 4

Более краткий:

Person.validators_on(:name).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:maximum]

Использует detect{} вместо select{}.first и is_a? вместо class ==.
Это также работает с Rails 4.1.

Ответ 5

Еще более динамичным, чем ответ Кулгара, было бы использовать что-то вроде этого:

Person.validators_on(attribute)
  .select { |v| v.class == ActiveRecord::Validations::LengthValidator }
  .select { |v| v.options[:maximum].present? }.first.options[:maximum]

Таким образом вы можете заказать валидаторы внутри модели так, как вы хотите.

Используйте его как помощник Rails

Затем вы можете использовать этот код для написания помощника:

# Returns the maximum length for a given attribute of a model
def get_maxlength(model, attribute)
  model.validators_on(attribute)
    .select { |v| v.class == ActiveRecord::Validations::LengthValidator }
    .select { |v| v.options[:maximum].present? }.first.options[:maximum]
end

И используйте помощника следующим образом:

= f.text_field :name, maxlength: get_maxlength(f.object.class, :name) # Or use get_maxlength(Person, :name)

Ответ 6

Если вы находитесь в рельсах 2, это нелегко. Я помню, как пробовал это раньше и не уходил далеко. Вы можете получить объекты проверки, но они не перечисляют, какое поле они для меня выглядят очень странно. Люди сделали для этого плагины (т.е. Чтобы проверить или отразить проверку AR-объектов), например https://github.com/redinger/validation_reflection

Ответ 7

Кулгарский ответ хорош и, возможно, идеален. Ниже приведен более простой способ сделать это для Rails 4, который не требует изменения любой конфигурации, при этом недостатком является то, что вы должны добавить строку include каждой модели, которая хочет ее использовать.

модели/проблемы/introspection.rb:

module Introspection

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def max_length(property)
      Resource.validators_on(property.to_sym).
          select{ |v| v.kind_of?(ActiveModel::Validations::LengthValidator) }.
          first.options[:maximum]
    end
  end

end

Затем в вашей модели:

class Comment < ActiveRecord::Base
  include Introspection
  ..
end

Затем вы можете сделать что-то вроде этого:

<%= form_for @comment do |f| %>
  <%= f.text_area(:body, :maxlength => f.object.class.max_length(:body)) %> # Or just use Comment.max_length(:body) %>
<% end %>