Imagine you jump into a console and try to debug a method that doesn't seem to do the right thing. Your Person
class' name is returned as "Johnno"
, although you expect just "John"
# person.rb
def name
# ... some code to decide what and how to return the name ...
name
end
Sometimes, when things get returned in a view, developers inspect()
the returned value and see what gets returned in the browser. Let's say you are one of those developers and do:
# person.rb - 1500 lines of code, definitely a fat model.
def name
# ... some code to decide what and how to return the name ...
name.inspect
end
But then in your view, you see a different output:
debug parse_old_name: [104, 101, 121] - Johnno
The inspect()
method returned the old "Johnno"
as expected but with some additional weird output. You spend an hour trying to understand what's going on until a colleague tells you that he monkey-patched the String class in a different method to do some debugging for another bug and forgot to remove it:
You could say it's magic. And while you were debugging you thought it was evil witchcraft. But it's actually basic Ruby functionality that lets you do good and bad.
Sometimes, we actually mean good magic. We adore some functionality that happens in the background (AKA "automagically").
On other occasions, we mean that something happens under the hood and there's no way to understand it.
Especially in the latter case, is this a good term? Or does it restrict us in the things we think we can and cannot understand?
Debunk Some Ruby Magic
Ruby is a programming language written in C (and in Ruby). It's text in files that get translated (compiled) to a lower level language (byte code) so that a server (e.g. your computer) can understand it and complete some instructions. This happens when you run ruby your-app.rb
for example. OK, that's pretty fascinating, but in the end, it boils down to physics and logical operations. We just recently learned to take computers for granted and now Ruby comes with its magic tricks.
Why do we think it's magic?
As illustrated in the monkey-patching example above, meta-programming is a big part of it. It needs first uncovering the magic, like understanding where it's monkey-patched. Then it needs further digging to understand how it happens. We mostly skip the latter part and prefer the magic label.
Another major reason is the innermost goal of Ruby to make developers happy.
Most human-friendly language
That's what they are saying about Ruby, it's the most human-friendly language to ever exist.
If this image looks to you like BSOD (Blue Screen Of Death), I understand. It definitely can be for one's brain cells and decision muscles. How in the world can you decide what to use from here if you see all the Enumerable methods for the first time? Including the fact that some of them are aliases? Why the hell are there aliases at all!?
That's some good magic, allowing you to write incredibly awesome and clear code, but for Ruby newcomers some mystic moments might come up, and googling this up the first 53 times is not unusual.
Now combine this with a web framework based on Ruby that has the same goals and you get hyper magic... 🛤
Debunk Some Rails Magic
Rails is a web development framework that's written in Ruby. Once you've run rails new project
a bunch of (mostly Ruby) files are generated into a project
folder and a bunch of libraries are installed in the according bundle. From there you encounter yourself in your app universe 🔮
Rails is perceived to be so magical for similar reasons as Ruby: Rails makes heavy use of Ruby's meta-programming and aims to be the most intuitive and developer-friendly framework.
Rails does this partly by adopting a strict convention over configuration philosophy. The good side of it is that established developers can become productive in almost any Rails codebase quite quickly.
The less good side is that if you don't know the Rails quirks and don't grok the philosophy yet, you'll have a bunch of magic moments.
Methods are called in places where you don't see them; files are (auto-)loaded here and there; callbacks... 💥
Convention over configuration
To understand on a high level what's happening there, you don't always have to dig into Rails code and understand every little detail. The docs and Rails guides are there when you need them.
Take this most simple example Rails app:
# config/routes.rb
Rails.application.routes.draw do
resources :games
end
# app/controllers/games_controller.rb
class GamesController < ApplicationRecord
def index
end
end
# app/views/index.html.erb
<h1>All the games</h1>
Can you imagine showing this to a developer who has never touched Rails or a similar framework and making them reason about how and why "All the games" is shown when you hit localhost:3000/games
?
They'll be perplexed 🔮 But after some digging through the above-mentioned resources (Rails Guides, Rails docs, weird blog posts like this here) they might come up with a few explanations, like this:
"The Rails framework code seems to run and create some methods and additional functionality on the fly based on its conventions as well as on its adherence to concepts like inheritance, MVC and a REST." - Developer
Then they might make more sense of this code and about where and why things are happening:
# THIS DEFINES SOME ROUTES METHODS DYNAMICALLY (including the URL path)
# config/routes.rb
Rails.application.routes.draw do
resources :games
end
# app/controllers/games_controller.rb
class GamesController < ApplicationRecord
def index
# This calls the render() method implicitly that sends the
# template with the right name based on convention back to
# the browser.
end
end
# app/views/index.html.erb
<h1>All the games</h1>
Whenever you stumble upon something "magical", remember the moment when you didn't know anything about Rails. You are now at exactly this moment but with a different problem at hand. Time to start digging. Magic is reduced by introducing more craft, more learning, more documentation, more digging, more bits, and bytes. It opens up opportunities to create and understand.
Appendix
Some more thoughts on magic and how to cope with it.
Ruby Golf
That's some black magic, don't touch it, for now, nothing good will come out of that, especially if you use this in production 😅🪄☠️ don't do this at home, don't do this in production.
But if you can't let your fingers off of it you can check out some good resources and learn some unusual Ruby quirks, it might bump up your understanding of Ruby to a new level:
- Start with Idiosyncratic Ruby Golfing Basicsl
- Then another light read
- Then Ruby Discord #ruby-arcana-🔮 channel
- Then practice the arcane evilness
Don't forget to keep your IRB open at all times 🎩🐰
More Ruby and Rails "Magic"
Some documented magic moments when I heard or read the word "Magic" (or said it myself 😅) in a Ruby or Rails context:
2022/04/12 - yield
in the application.html.erb
2021/02/07 - Ruby injects a magic default value when reducing
I'll be adding "Magic" findings here as I encounter them 🔎🦄
How to become a magician
Magicians in the real world do not possess supernatural superpowers (sorry if I disturbed you with this message, but the Easter Bunny isn't real either!). They checked out how things work and practiced a lot to replicate.
- Play around with things in your IRB. Run methods and check the attributes of the objects at hand
- Test-drive or automate your misunderstandings. This will often lead to you needing the internals of the underlying software, so you can make your tests work
- Get an overview of Ruby and meta-programming concepts, in books like Eloquent Ruby for a light fun read or Chris Oliver's Ruby Behind The Magic video course
- Check out Destroy All Software videos for seeing how RSpec and other day-to-day software works under the hood
- Dig the Rails Guides
- Dig some Rails Code and documentation when appropriate
- Ask the communities (like the Ruby Discord or Rails Link Slack)
- Have a process to get unstuck
Here's a quick talk that I held on the topic at the awesome Le Wagon bootcamp, if you like more visuals and examples. You will have a few more examples that I don't mention here, like Ruby's method_missing
and some more examples with visuals on Rails conventions: