2012-07-19 79 views
14

我有一个模型具有一些属性和虚拟属性。 此虚拟属性用于在创建表单中创建复选框。类型投下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 

但我觉得脏做它的方式。没有重写转换方法,是否有更好的选择?

感谢

回答

14

您原来的帖子的解决方案看起来像是我最好的解决方案。

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

如果你想清理了一点东西,你总是可以创建一个定义您使用value_to_booleanfoo=作家方法的辅助方法。

我可能会创建一个名为bool_attr_accessor方法的模块,所以你可以简化你的模型是这样的:

class Thing < ActiveRecord::Base 
    bool_attr_accessor :foo 
end 

好像加载ActiveModel应该为我们提供了这样的事情,让虚拟属性的行为更像“真实”(ActiveRecord-Persisted)属性。只要你有一个从表单提交的布尔虚拟属性,这种类型转换就很重要。

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

0

为什么你不这样做:

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

由于值可能会更改为“true”和“false”,它仍然应该工作。轨道没关系,但在错误的地方恕我直言。 – Happynoff 2012-07-19 13:19:25

+0

关于在'foo ='中设置@bar我认为这不是做它的地方,单一责任原则和所有。而且,在我真正的用例中,将它放在before_validation中是有意义的;) – Happynoff 2012-07-19 13:24:25

+0

你说得对。你有这个解决方案:https://gist.github.com/2000623但我不认为这是一个更好的解决方案。它基本上与轨道相同。查看源代码:https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb – Dougui 2012-07-19 13:28:32

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 
+0

是的也许,但我觉得这样做有点肮脏。我宁愿使用真正的布尔值。如果在我编写“Thing.new(foo:true)”的代码的其他部分中会发生什么? – Happynoff 2012-07-19 13:45:01

+0

我发现String#to_bool干净而且有帮助。谢谢。 – linojon 2013-09-04 22:07:36

2

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

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

注意:有在轨道上5.0.0从数据库加载模型这attribute功能的不正确的行为。因此使用rails 5.0.1或更高版本。