Unleash the Iterator power with &:method
Take a look at the following snippet
That’s all and good, but what if you need to map a method that is not from the object which is iterated over?
Now this looks a bit more complex. There are cases where we want to map a method that receives the object which is iterated over as an argument. You may go with the longer approach:
It feels like there’s unnecessary stuff here. We could however rewrite this as:
Multiline memoization with blocks
If you have been programming Ruby for a while, you have probably seen memoization in action:
This method memoizes the value of long_array.select { … }
so that the first time you call the method, the value is stored as an instance variable. Subsequent calls to the method don’t execute the code in the right hand of the mallet operator (||=
).
There are some cases where you may want to memoize an expression that has multiple lines:
Let’s suppose you need a method that returns the first 1000 numbers of the Fibonacci sequence in an array. The value will be used multiple times across multiple method calls inside a class. Ideally you would memoize the method. You could take the thousand_fibonacci
method and wrap the body in a block, and assign the value of the block to an instance variable using the hammer operator:
Defining methods dynamically
As a programmer we usually prefer to waste time trying to automate stuff, rather than doing it ourselves, even if it takes 10 times more to automate it.
I remember from my Java days, when you had to define getters and setters for each of the classes. It was incredibly frustrating.
Now that I do most of my coding in Ruby, those are problems of the past. Now all you have to do is type attr_accessor :instance_variable
and you have a getter and setter for it.
The point of that rant is to show you the magic of define_method
and how attr_accessor
leverages the power of dynamic programming to achieve that.
Take a look at the following class:
This really takes me back to my Java days. It feels like I’m writing getters and setters all over again.
Enter define_method
define_method
allows you, the developer, to define methods in runtime. This means that you can define the template of a method, and when the code is evaluated at runtime, the method will be defined.
It’s better to see it with code, so let’s rewrite the <role>?
methods using define_method
When Ruby evaluates the content of the User
class, it will iterate through the keys of the ROLES
hash. Doing that defines an instance method called <key_name>?
which checks if the value of the instance variable @role
is the same as the value of the ROLES
hash for that key.
Now you can have easy to understand methods for your User
class:
Reopening classes
Because Ruby trusts the programmer with the inner workings of the language, (almost) everything that Ruby provides, can be reopened and modified. Core classes like String
, Array
and Integer
can be modified so that new methods can be added into it (or to modify the methods).
Imagine you are tasked with writing a method in Ruby that receives an argument which is an Array of strings, integers, or nil elements. The method should return false if all of the elements in the Array contain empty strings, nil, or zeroes.
You could do the following:
This approach is good, but you could approach the task from a different angle. What if String
, Integer
and NilClass
(yes, nil
is an instance of NilClass
) all had the method empty?
Let’s open those classes and add the method empty?
to each of them and pass that method to all?
:
Thats it, now you can call empty?
on any instance of the classes String
, Integer
or NilClass
!
Easy debugging with the #methods
method
One of the most annoying things about programming in Ruby is that IDE’s language support is not as good as JavaScript, TypeScript or some of the other languages around. Not having a good IntelliSense makes not just coding, but also debugging a bit slower.
Luckily Ruby has a really high degree of introspection. This means that objects in Ruby can answer quite a lot about themselves. One of the methods which I used the most while debugging with external gems is calling the #methods
method on an object while on a debugging session.
Consider the CSV
class. It’s really useful for ingesting csv, converting the data with Ruby algorithms, and then exporting that data into another CSV. But what if you don’t really know which are the methods available?
You can take it a step further and try the method arity
, when used in conjunction with #method
, looks like this:
Do you know any other hidden Ruby features that make your life easier and your code cleaner?