March 2023
OOP
Ruby is an Object Oriented Programming language, so most everything is an object (i.e. an instance of some class), and we can call methods on our objects.
3.class
=> Integer
"string".length
=> 6
[1,2,3].include? 1
=> true
Operators
In Ruby, there are a couple dozen operators: +
, -
, *
, /
, %
, **
, =
, <
, etc.
Some rocket science:
2 + 2
=> 4
Operators and OOP?
These operators … don’t really look like methods being called on objects?
Well, turns out 2 + 2
is a shorthand version of 2.+(2)
where +
is just a method of the Integer class.
2.+(2)
=> 4
2.+ 2 # parens optional
=> 4
Native classes
Not all operators are available everywhere - each class specifies which operators to support.
For Integer
, there are many operators, as we tend to like to operate on integers.
A quick glance via Integer.instance_methods.sort
:
[:!, :!=, :!~, :%, :&, :*, :**, :+, :-, :/, :<, :<<, :<=, :<=>, :==, :===, :=~, :>, :>=, :>>, :[], :^, ...]
There are fewer available for a class like String
; and this makes intuitive sense, as it isn’t clear what a -
would do for an instance of the String
class.
String.instance_methods.sort
=> [:!, :!=, :!~, :%, :*, :<, :<<, :<=, :<=>, :==, :===, :=~, :>, :>=, ...]
But one common use of +
for strings is concatenation:
"my string" + " has been concatenated"
=> "my string has been concatenated"
Custom Classes
We can define operators on our own custom classes if we’d like.
class Cat
attr_reader :lives_remaining
def initialize
@lives_remaining = 9
end
def -(lives)
@lives_remaining -= lives
end
end
cat = Cat.new
cat.lives_remaining
=> 9
cat - 3
cat.lives_remaining
=> 6
Why does this matter?
Since operators are just methods (usually displayed in shorthand), we can do method-y things!
One example is the safe navigation operator &
.
Sometimes, if we’re not sure whether an object is nil
, we can chain &
before the method call, so we don’t get an undefined method for nil:NilClass
.
It’s quite common to see stuff like my_object&.my_method
.
Using the Safe Navigator for Operators
Let’s say we have a method that calculates the tax on a transaction.
def tax(amount)
amount * 0.07
end
tax 100
=> 7
But perhaps in practice, we don’t know the values that will find their way into this method. Occasionally, the argument could be nil
.
Currently, our app would blow up:
amount = nil
tax(amount)
`undefined method `*' for nil:NilClass (NoMethodError)`
Let’s redefine our method using the safe navigation operator:
def tax(amount)
amount&.* 0.07
end
tax(nil)
=> nil
Another possible solution would be to protect areas of code with guard clauses (ie return if amount.nil?
), but the save navigation operator is nice and tidy, and in some circumstances, might be enough.
That’s all.