这段代码在做什么,如何更简单地编写它?

时间:2013-05-23 19:37:43

标签: ruby-on-rails ruby refactoring metaprogramming

[:initial_amount, :rate_increase_amount].each do |method|
  define_method method do
    self["#{method}_in_cents".to_sym].to_f/100 if self["#{method}_in_cents".to_sym]
  end

  define_method "#{method}=" do |_value|
    self["#{method}_in_cents".to_sym] = _value.to_f * 100
  end
end

它出现以下错误:

NoMethodError: undefined method `initial_amount_in_cents' for #<ViolationType:0x6220a88>

我试图将其重写为:

def initial_amount_in_cents
  initial_amount_in_cents.to_f/100 if initial_amount_in_cents
end

def rate_increase_amount_in_cents
  rate_increase_amount_in_cents.to_f/100 if rate_increase_amount_in_cents
end

def initial_amount= (value)
  initial_amount_in_cents = value.to_f * 100
end

def rate_increase_amount= (value)
  rate_increase_amount_in_cents = value.to_f * 100
end

但它给了我这个错误:

ERROR SystemStackError: stack level too deep

1 个答案:

答案 0 :(得分:2)

问题

你有几个,包括:

  1. 过于“聪明”的代码。如果没有充分的理由,不要做这样的事情。
  2. 递归调用。您可以考虑使用defined?或其他东西来避免这种情况。
  3. 一个缺少大量背景的例子。
  4. 干燥并不意味着模糊。如果代码对你的编写方式没有意义,那么为了清晰起见重构它。

    可能会做什么

    代码显然试图为接受float的相关方法动态定义_in_cents方法。你必须问作者为什么他(或她)以这种方式写它,但这就是它的用途,无论它是否适合你。

    可能的解决方案

    尽管如此,这可能会有所帮助。假设您已在模型中为initial_amountrate_increase_amount定义了属性,那么您应该能够简化对Module#define_method的调用。例如:

    class YourRailsModel
      %i[initial_amount rate_increase_amount].each do |method|
        # You don't have a real Rails model here, so we create accessors to simulate
        # model attributes.
        attr_accessor method
    
        define_method "#{method}_in_cents" do
          Integer(Float send(method) * 100)
        end
      end
    end
    
    model = YourRailsModel.new
    model.initial_amount = 0.97
    # => 0.97
    
    model.initial_amount_in_cents
    # => 97