I think I’m really starting to get Ruby. At least I can read Ruby and tell you what’s happening.
I haven’t read a single tutorial on RSpec, the testing framework we use at Chatwoot for the Rails backend. I didn’t want to spend too much time in tutorial hell, and I’m sure that I should read more about it soon.
I’ve written about Ruby blocks before, but I think it bears repeating. This is a method call with a block as an input.
|
|
Don’t worry about how the function is implemented for now. I’m hand-waving this so that you can notice something.
This is also a method call with a block as an input.
|
|
But here’s something else that’s a method call with a block as an input.
In lib/calculator.rb
:
|
|
In spec/calculator_spec.rb
:
|
|
The highlighted lines include a method call to it
with a block.
Look at that again. It’s a method call.
That is crazy. It is so sublime that I can’t explain how excited this makes me. If I have to teach Ruby to a Pythonista I’d ask them to ensure they see what this is. Until you grok this, it won’t matter how much you try to understand Ruby. Ruby’s readability comes from this feature. I’m still not sold on RSpec though, but I am open to learning it because it is, ultimately, Ruby.
Block Magic
Let’s really see what you can do with blocks.
Building Your Own Language
|
|
These are all method calls on integers. times
is a method. downto
is a method. And every
method takes blocks.
But we can also take this to the monke.
|
|
That line is readable, not because Ruby is magically more readable than other languages,
but because we’re daisy-chaining methods we implemented on the built-in type. We gave Integer
wings.
We created a mini language that looks like it somehow isn’t Ruby but, sweet God in Vaikuntha, it really is.
Resource Management
In Python I’m used to doing this.
|
|
But you’d be surprised how much production code is in the wild that doesn’t use this.
You should be writing it like this in Python, but with
is a language keyword in Python.
What would I do in Ruby?
|
|
File.open
is a regular old Ruby method. It takes block. THEY ALL TAKE BLOCKS.
|
|
That just added a neat side-effect to the database operation.
Building a DSL
Now let’s really turn things up.
|
|
Just stop reading and look at that syntax.
I had a problem when I first tried learning Ruby in 2021. I had tried, because I was trying to just
learn a new programming language (as one should definitely do annually), and I was a little gob-smacked
because I didn’t understand the config.rb
file from Rails. It had a bunch of things like this.
It looked like a damned configuration file.
Django tries to do this with the settings.py
file, but it doesn’t succeed.
This is exactly why Rails routing looks like this.
|
|
That’s not something special from Rails. That’s just a bunch of Ruby methods taking blocks! The draw
, resources
, member
, namespace
, they’re all just regular methods.
All of this looked like some magical DSL to me. But it was not. It was just regular old Ruby.
Custom Control Flow
Okay let’s make our own unless
or if
syntax. Just because I’m feeling like it.
|
|
Or maybe a retry
function?
|
|
Putting this all together
|
|
Each and every one of those was a method call that took a block!
It’s all a block. Once I saw this pattern, I could not unsee it!
And why would I want to unsee it? It’s beautiful. It sparkles, damn it.
The magic isn’t just that you can do this. It’s that Ruby’s syntax makes it so natural that blocks become invisible. When you write 5.times { puts “Hello” }, you don’t think “I’m calling the times method and passing it a block.” You think “I’m doing something 5 times.” You read RSpec code and think “Yeah, that reads like English.”
That’s the genius of Ruby. The language gets out of your way and lets you think about the problem, not the syntax.