键入强制转换ActiveRecord模型虚拟属性

时间:2012-07-19 12:43:32

标签: ruby-on-rails 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

但我觉得这样做很脏。没有重写转换方法,有没有更好的选择?

由于

4 个答案:

答案 0 :(得分:15)

原始帖子中的解决方案对我来说是最好的解决方案。

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应该为我们提供这样的东西,因此虚拟属性更像是&#34;真实的&#34; (ActiveRecord-persisted)属性。只要您具有从表单提交的布尔虚拟属性,此类型转换就非常重要。

也许我们应该向Rails提交补丁......

答案 1 :(得分:3)

在Rails 5中,您可以使用attribute方法。此方法定义此模型上具有类型的属性。如果需要,它将覆盖现有属性的类型。

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

警告:从db 5.0加载的模型中,rails 5.0.0中此attribute功能的行为不正确。因此,请使用rails 5.0.1或更高版本。

答案 2 :(得分:1)

查看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

或者为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

答案 3 :(得分:0)

为什么你不这样做:

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