Тип вводит виртуальный атрибут модели ActiveRecord

У меня есть модель с некоторыми атрибутами и виртуальным атрибутом. Этот виртуальный атрибут используется для создания флажка в форме создания.

class Thing < ActiveRecord::Base
  attr_accessor :foo
  attr_accessible :foo
end

Так как поле является флажком в форме, атрибут foo получит значение '0' или '1' как значение. Я бы хотел, чтобы он был логическим из-за следующего кода:

class Thing < ActiveRecord::Base
  attr_accessor :foo
  attr_accessible :foo

  before_validation :set_default_bar

  private

  def set_default_bar
    self.bar = 'Hello' if foo
  end
end

Проблема здесь в том, что условие будет истинным, даже если foo '0'. Я хотел бы использовать механизм кастомизации типа ActiveRecord, но единственное, что я нашел для этого, это следующее:

class Thing < ActiveRecord::Base
  attr_reader :foo
  attr_accessible :foo

  before_validation :set_default_bar

  def foo=(value)
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  end


  private

  def set_default_bar
    self.bar = 'Hello' if foo
  end
end

Но я чувствую себя грязным, делая это таким образом. Есть ли лучшая альтернатива без повторной записи метода преобразования?

Спасибо

Ответы

Ответ 1

Ваше решение из исходного сообщения выглядит как лучшее решение для меня.

class Thing < ActiveRecord::Base
  attr_reader :foo
  def foo=(value)
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
  end
end

Если вы хотите немного почистить вещи, вы всегда можете создать вспомогательный метод, который определяет ваш метод записи foo= для вас, используя value_to_boolean.

Я бы, вероятно, создал модуль с методом под названием bool_attr_accessor, чтобы упростить вашу модель следующим образом:

class Thing < ActiveRecord::Base
  bool_attr_accessor :foo
end

Кажется, что ActiveModel должен предоставить нам что-то подобное, так что виртуальные атрибуты действуют скорее как "реальные" (ActiveRecord-persisted) атрибуты. Приведение этого типа важно, когда у вас есть логический виртуальный атрибут, который отправляется из формы.

Возможно, мы должны отправить патч Rails...

Ответ 2

В Rails 5 вы можете использовать метод attribute. Этот метод определяет атрибут с типом в этой модели. При необходимости он переопределит тип существующих атрибутов.

class Thing < ActiveRecord::Base
  attribute :foo, :boolean
end

Предупреждение: неправильное поведение этой функции attribute в рельсах 5.0.0 для моделей, загружаемых из db. Поэтому используйте рельсы 5.0.1 или новее.

Ответ 3

Посмотрите validates_acceptance_of код (нажмите Показать источник). Они реализовали его по сравнению с "0".

Я использую его в формах регистрации таким образом:

class User < ActiveRecord::Base
  validates_acceptance_of :terms_of_service
  attr_accessible :terms_of_service
end

Если вам действительно нужна передача из строки и т.д., вы можете использовать это:

def foo=(value)
  self.foo=(value == true || value==1 || value =~ (/(true|t|yes|y|1)$/i)) ? true:false
end

Или добавьте метод typecast для класса String и используйте его в модели:

class String
 def to_bool
   return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
   return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
   raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
 end
end

Ответ 4

Почему вы этого не делаете:

def foo=(value)
  @foo = value
  @bar = 'Hello' if value == "1"
end