Discoveries in Ruby(and Rails): Give classes the ability to be compared using th...
source link: https://dev.to/rockwell/discoveries-in-rubyand-rails-give-classes-the-ability-to-be-compared-using-the-comparable-module-5f6p
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Posted on Jan 15
Discoveries in Ruby(and Rails): Give classes the ability to be compared using the Comparable module
The equality-test operators is a set of members that consist of ==(equals), !=(not equal), > (greater than), <(less than), >=(greater or equal), and <= (less or equal), with the most common being the == operator.
We can see that with both Integer
and Float
which are subclasses of the Numeric
class. Objects of both classes can be compared using the equality-test operators because they include the Comparable module, and override the comparison method <=>
, also called the spaceship operator or spaceship method.
Let's imagine in an e-commerce sight we have Product
s. A product can have many different Price
per customer/conuntry. The modeling might look like this
class Price
attr_accessor :amount_cents
def initialize(amount_cents)
self.amount_cents = amount_cents
end
end
Enter fullscreen mode
Exit fullscreen mode
Let's imagine that we need to compare the price of two products(for whatever reason we need to). To compare the amount_cents
we might do something like
if price_1.amount_cents < price_2.amount_cents
puts "price_1 is less than price_2"
elsif price_1.amount_cents > price_2.amount_cents
puts "price_1 is greater than price_2"
else
puts "both prices are equal"
end
Enter fullscreen mode
Exit fullscreen mode
To compare the price objects we simply need to do so on the amount_cents
attribute, since it's an instance of Numeric
(either float or integer) it can be compared using the equality-test operators mentioned above.
But, we cannot do something like this
if price_1 < price_2
puts "price_1 is less than price_2"
elsif price_1 > price_2
puts "price_1 is greater than price_2"
else
puts "both prices are equal"
end
Enter fullscreen mode
Exit fullscreen mode
Because our class Price
does not include the Comparable
module, thus it does not support this. If we try to we get
undefined method `<' for #<Price:0x00007fbd98992de0 @amount_cents=10> (NoMethodError)
Enter fullscreen mode
Exit fullscreen mode
The ruby interpreter tells us that the class does not have support for the <
method. Because we did not define this.
Lo! and behold the Comparable
module in action.
Let's allow our Price
class to work nice with the equality-test operators. To do that we need to do
- include the Comparable module
- override the
<=>
method
Let's rewrite the Product
class to support that.
class Price
include Comparable
attr_accessor :amount_cents
def initialize(amount_cents)
self.amount_cents = amount_cents
end
def <=>(other_price)
if self.amount_cents < other_price.amount_cents
-1
elsif self.amount_cents > other_price.amount_cents
1
else
0
end
end
end
Enter fullscreen mode
Exit fullscreen mode
Notice the return value for the <=>
method.
- -1 means that it's less than the passed argument.
- 1 means that it's greater than the passed argument.
- 0 means both prices are equal.
Now, we can run this code
if price_1 < price_2
puts "price_1 is less than price_2"
elsif price_1 > price_2
puts "price_1 is greater than price_2"
else
puts "both prices are equal"
end
Enter fullscreen mode
Exit fullscreen mode
and get the output
price_1 is less than price_2
=> nil
Enter fullscreen mode
Exit fullscreen mode
Including the Comparable module provides us with more methods more than just the equality-test operators. For example, using the Price
class determine if a price is between two other price
price_1 = Price.new 100
price_2 = Price.new 200
price_3 = Price.new 150
price_3.between? price_1, price_2
Enter fullscreen mode
Exit fullscreen mode
The output would be true
We can also sort them!
[price_3, price_1, price_2].sort
[<Price:0x00007fa65e225d38 @amount_cents=100>,
<Price:0x00007fa65e0be9b8 @amount_cents=150>,
<Price:0x00007fa661b965e0 @amount_cents=200>]
Enter fullscreen mode
Exit fullscreen mode
Conclusion
We can allow our classes to work well with the equality-test family members and save ourselves much time trying to access attributes for comparison. And as a side-effect our Ruby code becomes more natural and ruby-ish, and we type less :).
Thanks for reading, and Happy Coding!.
Resources
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK