使用任意名称定义方法

时间:2017-03-24 01:59:14

标签: ruby-on-rails ruby

我的rails应用程序有一个名为Game的{​​{1}}模型,用于存储和关联体育竞赛的相关信息,包括home_team_idhome_team_score等数据库项目以及类似的方法winning_team_idwinning_team_score将逻辑应用于存储的数据。

我在app/models/中有另一个文件,它定义了一个独立的类Record。通过将recordgames数组作为参数传递来创建team_id,以便为@wins创建实例变量,例如@points_forteam } {}对应team_id

Record内我定义两个实例方法average_points_foraverage_points_against,它们完全符合您的期望:

class Record
    def games_played
        return @wins + @losses
    end
    def average_points_for
        return (@points_for.to_f / games_played).round(2)
    end
    def average_points_against
        return (@points_against.to_f / games_played).round(2)
    end
end

似乎非DRY将average_points_foraverage_points_against声明为两种不同的方法。我意识到我能做到这一点......

class Record
    def games_played
        return @wins + @losses
    end
    def average_points(which)
        return eval("(@points_#{which}.to_f / games_played).round(2)")
    end
end

...但average_points("for")看起来很丑陋 - 我更喜欢average_points_for的惯例。

我更喜欢的是这样的:

class Record
    def games_played
        return @wins + @losses
    end
    def average_points_#{which}
        return (@points_#{which}.to_f / games_played).round(2)
    end
end

有办法做到这一点吗?

1 个答案:

答案 0 :(得分:4)

在我看来,对于你的用例,重构它来干它会更清晰:

private def average(value)
  (value.to_f / games_played).round(2)
end

def average_points_for
  average(@points_for)
end

def average_points_against
  average(@points_against)
end

但是,正如评论所示,您可以使用define_method。 Rails确实带来了很大的好处,就像Ruby本身一样,OpenStruct;但在这种情况下,这是一种矫枉过正。如果你真的想这样做,那就是这种方式(未经测试,可能包含错误):

%i(for against).each do |which|
  define_method(:"average_points_#{which}") do
    (instance_variable_get(:"@points_#{which}").to_f / games_played).round(2)
  end
end