Eric / Brooklyn

Random 5

Operator as Methods

Web Accounts

Honeypot Bots

Stripe and Tap

Git Save

Whitespace Problems

Ruby Exits

Appending in Javascript

Sendgrid Ban

Clean URLs

Integer Division

Multi-tab Websockets

Bad Content

JS Data Chutzpah

Responsive tables

Concerns

Cookies

Emoji Bits

Git

Ruby vs. Ruby

Extending Devise

Rails UJS

ENV Variables

See All

Treating Operators as Methods

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.