我的 Rails 应用程序有一个名为 Game
的 ActiveRecord
模型,它存储和关联有关体育比赛的信息,包括 home_team_id
和 home_team_score
以及 winning_team_id
和 winning_team_score
等方法,它们将逻辑应用于存储的数据。
我在 app/models/
中有另一个文件,它定义了一个独立的类 Record
。 record
是通过将 games
数组和 team_id
作为参数传递来创建实例变量的,例如 @wins
和 @points_for
对应于 team_id
的 team
。
在 Record
中,我定义了两个实例方法 average_points_for
和 average_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_for
和 average_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
有办法吗?
最佳答案
在我看来,对于您的用例,将其重构为 DRY it 会更干净:
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
关于ruby-on-rails - 用任意名称定义方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42990138/