2012-12-10 50 views
0

我有一个表格,有大量字段,我将其作为小数存储在我的数据库中。处理混合数字 - 在模型中?

然而,我想用户能够输入的级分,并且具体地混合数字(例如38 1/2)

This answer提供了一种用于转换混合编号,以分数一个好的解决方案,我已经修改了它,并把它放在一个函数在我的模型:

def to_dec 
    self.chest = self.chest.split.map { |r| Rational(r) }.inject(:+).to_f 
end 

而且我已经尝试了validate :to_decbefore_save :to_dec调用它。但是,这些都不起作用。

我知道为什么,但不知道如何解决它。

通过使用puts self.chest我可以知道,在我有机会调整完成后,该值正在转换为数字。进入'10 1/2' 到我的表单和保存,puts self.chest给了我10.0即使功能就是:

def to_dec 
    puts self.chest 
end 

此外,puts '38 1/2'.split.map { |r| Rational(r) }.inject(:+).to_d给了我38.5,所以我知道这个问题是不是在转换。

我已经将视图输入类型从数字更改为字符串,但我似乎仍然以我的模型中的数字结束。

我被卡住了。

此外,其他一些领域也需要支持混合数字 - 我想以某种方式抽象出来,所以我不需要为每个值编写不同的函数。

回答

1

原因是ActiveRecord在定义模型属性时会查看数据库模式。这就是所有属性如何在你的模型类中出现,而不用你用attr_accessor :chest(或def chestdef chest=)手动定义它们,就像你必须在一个普通的旧Ruby对象上一样。

当它这样做时,ActiveRecord也查看列类型,并在分配属性时应用类型转换。因此,因为您的chest列是小数点,所以它会将您给它的任何字符串转换为浮点数。在这样做的时候,它会抓住可以转换的字符串的第一位,并丢弃其余的,这就是为什么你得到38.0

您可以通过几种方法解决这个问题 - 您可以将数字作为字符串存储在数据库中,这可能会节省一些转换次数(但如果您需要使用数字表示形式在查询中),或者您可以使用virtual attribute

基本上,你需要定义一个getter和setter方法,像这样:

def rational_chest 
    # I assume this would create a rational from a float 
    # and respond to `to_s` in a sane manner... 
    Rational(chest) if chest 
end 

def rational_chest=(value) 
    self.chest = parse_rational(value) 
end 

private 

def parse_rational(value) 
    return nil if value.blank? 
    value.split.map{|r| Rational(r)}.inject(:+).to_f 
end 

然后在你的表单中使用rational_chest,而不是只是chest

既然你需要在一些领域这样做,你可以再打一个漂亮一点的元编程:

def self.rational_attribute(attribute) 
    define_method("rational_#{attribute}") do 
    Rational(send(attribute)) 
    end 

    define_method("rational_#{attribute}=") do |value| 
    send "#{attribute}=", value.split.map{|r| Rational(r)}.inject(:+).to_f 
    end 
end 

def self.rational_attributes(*attributes) 
    attributes.each {|attribute| rational_attribute attribute} 
end 

rational_attributes :chest, :waist, :etc 

如果使用这几种机型,不干了一个模块(WIT hout self. s和extend它!

+0

我编辑了你的答案'if胸部',因为如果留空即抛出一个错误。真的很有帮助,很好的解释!一个缺点是允许不正确的分数(77/2等),但通过移除Rational()可以很容易地解决这个问题。其中给出了一个小数。最终我可以使用这个[gem](https://github.com/clord/fraction)将其转换回混合编号。谢谢您的帮助! – bookcasey

+0

嗯......我能够解决这个问题,为第一个例子留下一个空白值,但我正在为第二个例子挣扎。我想允许一个空白输入。有任何想法吗?我得到这个错误'未定义的方法'to_d为零:NilClass' – bookcasey

+0

啊啊,好的电话,我没有想过这种情况。我可能会将解析分割为一个单独的方法,如我最近的编辑所示。总的来说,这听起来像你的需求比你的基本'Rational'类复杂一点,或许值得考虑开发一个自定义的'Measurement'类。 –