Common use case from Ruby on Rails is to define a simple model which is associating a Fixnum with some additional properties (like string name): examples are sport leagues (number), or bank account (balance), and so on. Whenever the primary objective of your model (say, League) is to store the number, you would want to override the comparison operators so that instead of that ugly code:
league.number > other_league.number ... leagues.sort! {|a,b| a.number <=> b.number}you would write this beautiful ruby:
league > other_league ... leagues.sort!But overriding each comparison operator would quickly make your model look like some java code or worse. Ruby "meta-programming" to the rescue:
Class League < ActiveRecord::Base %w(<=> == < > <= >=).each do |operator| define_method operator do |other| case other.class.to_s when "League" self.number.send(operator,other.number) when "Fixnum" self.number.send(operator,other) else raise ArgumentError, "Illegal argument" end end end endThis not only would allow you to compare League with some other League in every possible perverted way you please, but also to compare apples and oranges, i.e. League and a Fixnum, and get an ArgumentError when comparing it to anything else.
You may find it useful to compare with a String, too, if a string attribute is sensible identifier for your model. We use send here to evaluate comparison operator for our number attribute, but you can send to String attributes just as easily (and to anything else which already has it's own comparison operators defined).
But, surely, while this code above defines 6 methods for you while keeping it DRY, there is a better way - using Comparable mixin. It would define 6 comparison methods (plus between?(min,max) method) all based on the <=> operator, and the code will look much clearer:
class League < ActiveRecord::Base include Comparable def <=>(other) case other.class.to_s when "League" self.number <=> other.number when "Fixnum" self.number <=> other else raise ArgumentError, "Illegal argument" end end endHave fun!
I love you
ReplyDelete