Reputation: 86
My rails application has an ActiveRecord
model called Game
which stores and relates information about a sport contest, including database items like home_team_id
and home_team_score
as well as methods like winning_team_id
and winning_team_score
which apply logic to the stored data.
I have another file in app/models/
which defines an independent class Record
. A record
is created by passing as parameters an array of games
and a team_id
to create instance variables such as @wins
and @points_for
for the team
corresponding to the team_id
.
Within Record
I to define two instance methods average_points_for
and average_points_against
, which does exactly what you'd expect:
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
Seems non-DRY to declare average_points_for
and average_points_against
as two different methods. I realize I could do this...
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
...but average_points("for")
looks ugly — I prefer the convention of average_points_for
.
What I'd prefer is something like this:
class Record
def games_played
return @wins + @losses
end
def average_points_#{which}
return (@points_#{which}.to_f / games_played).round(2)
end
end
Is there a way to do this?
Upvotes: 1
Views: 73
Reputation: 198304
In my opinion, for your use case it would be cleaner to just refactor it to DRY it up:
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
But, as comments indicate, you could use define_method
. Rails does it to great benefit, as does Ruby itself in, say, OpenStruct
; but it is an overkill in this case. If you really wanted to do it, this would be the way (untested, may contain bugs):
%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
Upvotes: 4