A really amazing thing happened to me last week: I wrote some code that I was actually proud of! I was trying to solve an interesting problem for an application I started building at work recently, and I implemented the first solution that came into my mind. And after I took a step back from my text editor and actually looked at what I had written, I realized something. My code was actually good.
The very fact that I was so surprised and thrilled at the prospect of writing good code speaks volumes to the nature of programming. Most of the time I abhor the methods I write because I know they could be written better, but I don’t have the syntactical flow (yet!) to write them well. So last week’s incident of “code pride”, however fleeting, was rather noteworthy.
I looked back on my code over the weekend and thought about what made it seem so beautiful to me. What did I do differently that made me beam with pride knowing that I had been the one to write that particular method? The short answer to that question is: metaprogramming. Metaprogramming is nothing more than abstraction of code, which often means that your code will write more code for you! Of course, there are a lot of different techniques and approaches to this, and I’m certainly no expert. But I did learn a little something about a meta method called public_send
, and I’ll show you just how I used it!
Gotta Dispatch? Do It Dynamically.
Everything in Ruby – everything in programming, really – is just an abstraction. We sprinkle some syntactic sugar to make our lives easier and code simpler, but it’s all just an abstraction of something else. When we’re refactoring by metaprogramming, it’s this same concept of abstraction that we have to keep in mind. And when we look for and find patterns within our code, it’s generally a sign that we could be encapsulating and abstracting away that piece of functionality.
My favorite example of abstraction is method dispatching. Method dispatching is how we send a message to an object. And boy, do we do that a lot. Since everything in Ruby is an object, whenever you want an object to do something, you have to send it a message. And luckily, because Ruby is so great, the method we use to “send” messages is called just that: send
.
The send
method is called in our programs way more than we might realize. For example, if we open up our console and do some simple math:
1 2 |
|
what we’re really doing is sending a message to the 3
Integer object, and telling it to perform an action (+
) in terms of another object (the 4
Integer object):
1 2 |
|
The send
method takes a parameter of either a string or symbol, which is the method name. The method name will always be the first parameter, and the second parameters is passed to the method as an argument.
Now, this is great when you know that you want to add 3 and 4. But who is going to add 3 and 4 all of the time? No one, that’s who. You’re probably going to want to add 3 to 5, and 6, and on and on…
Enter dynamic dispatching to save the day!
Dynamic dispatching, as the slightly odd yet adorable gif above demonstrates, involves sending various messages (read: methods) to objects, with the added caveat that our methods keep changing, depending on the situation. Dynamic dispatching also allows us to send different methods to objects in our program without any other object knowing the contents of that message. A good indicator for using dynamic dispatching is if you have call a method in a specific situation, but don’t know what exactly that method will be.
Confused yet? Okay, okay, let me give you an example.
You Can Send Whuteva You Like
Knowing that you can use send
to “send” different methods to an object is only half the fun. The other half is figuring out when to pull out this tool from your Toolbelt of Knowledgeâ„¢ – yes, I’m trademarking this phrase – and actually use it.
So…I guess it’s time for me to show you a real-life example of how I used recently dynamic dispatch to invoke specific methods in my application! Hopefully to neither your surprise nor chagrin, I’ll be using my eCommerce Bookstore example.
In my store, I have a paginated list of different books available for purchase. Each book has only a limited amount of space it can take up in the view, so as an admin of the site, it’ll be up to me to decide how I want different books to show up. Some of my books have awesome book covers, so I want to use thumbnail images of their cover as the main “viewable attribute” in my store. Since I’m using the paperclip
gem, this will be pretty easy.
However, some books in my store don’t even have book covers! My vast collection of Shakespearean plays, for example, would be much better served by making the author as the “visible attribute”, rather than nonexistent covers. And I think the Game of Thrones book series should have the book title as the “visible attribute”.
So, how can we handle this? Well, let’s first look for any patterns.
1. Look For Patterns
In our view, we want to be able to show each Book
object using its primary viewable attribute. The problem we’re dealing with here is that an admin will mark different attributes for a Book
object as “viewable”, which means that we can’t predict whether it’ll be a title
, author
, or an image. But we do know that every Book
object has to have some “viewable attribute”.
Cool, so there is some sort of pattern emerging here: we need to render an attribute, and we don’t know what it will be. Or…do we?
2. Consider The Data
Since we’re building out an admin panel for this application, we know that every book needs to have a title
and an author
. The book cover (which we’ll refer to as media
) is optional, but the other two are not, which means we’ll have a validation for our Book
objects:
1 2 3 |
|
This validation makes me think about the other things that will always be present on a Book
object, and the first thing that comes to mind is a viewable_by
attribute. If we think about it, an admin always has to mark something as “viewable”, and that “viewable” property can change when they update an object. So this is really a property unique to each Book
, which means that it could very well be a column saved in the database.
So, we’ll write a migration that adds a viewable_by
column, which can never be null
, and will always default to a Book
’s title
:
1 2 3 4 5 |
|
This migration might look pretty simple, but it is its very simplicity that lends itself so elegantly to some serious metaprogramming that we’ll do next.
3. Encapsulate And Abstract
This last part can be the hardest to wrap our minds around, but it also happens to be the coolest. As it stands, our database has a column that will be populated with either a string value "title"
, "author"
, or "media"
. These values will be changed and updated by an admin, and they will obviously and inevitably change. But here’s what’s not going to change: we’re still going to want to render the value of whatever attribute is marked as “visible” – that is to say, whatever string value is saved as viewable_by
.
If we think back to the pattern we just identified, we realize that the attribute changes, but what we want to do with it stays the same. No matter what the Book
is viewable_by
, we want to render it. We want to send a message to this object and tell it, Hey Book! Whatever you are viewable by is exactly what you should show yourself as!
And this is where we can use send
to encapsulate and abstract this away into a single method call. First, we’ll want to add a method that checks whether this is an image – if it is, we’ll hand it off to the paperclip
gem to render the image:
1 2 3 |
|
This method will return true
if we the viewable_by
attribute is set to media
, and false
if it’s not. We can use this boolean
return value in a conditional statement:
1 2 3 4 5 6 7 8 |
|
Whoaaaaa, whut is happening? The answer is: something cool! This book_html
method will render either a thumbnail image (which we let some other code worry about generating), or it returns a title
or an author
. And what’s really cool is that, we could add other attributes to our table – like year
or genre
and render html based on that, as long as it was saved in our viewable
column.
How does this work exactly? Well, whenever we create a new column in our database, we get two important methods for free: a reader and a writer. That means that we have both a title=
and a title
method.
If we look back to how the send
method works, we’ll remember that the send
method takes a parameter of a string or a symbol, which is the name of the method to be called. When we call send
and pass it the value of self.viewable_by
, we’ll actually be calling send("title")
on an instance of Book
. This will then call title
on that instance of Book
, returning that particular book’s title as a string.
The cool thing about this code is that it’s flexible, and has abstracted away a pattern into a dynamic method call, which is dispatched to the appropriate object at the appropriate time. But there’s still one major issue with this code that I can see. Let’s fix that.
To Send Or To Public Send? That Is The Question.
A lot of the most incriminating evidence against using the send
method stems from the fact that send
can even send private methods to an object. This can be dangerous for your application internally, and also leaves it vulnerable to external, malicious attacks.
A quick fix for this is to instead use public_send
, which does exactly what you think it does: send only publicly-accessible methods to the object that is its receiver. Our final code now looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Nice! Not too shabby for our first attempt at metaprogramming all the things (or at least one thing).
As hard as it is to do, you can’t be too hard on yourself when it comes to refactoring and metaprogramming. I honestly think it just comes with time, practice, and exposure. Eventually, you’ll start to recognize the same patterns again and again, and you’ll start to learn which tools are the right ones for the job.
Even though it takes a bit more effort, I think learning the different techniques of metaprogramming makes you better and both reading and writing code. By rewriting your old code and implementing some of those metaprogramming techniques, you take away some of the otherwise hard-coded lines in your application, and make it more flexible and dynamic.
If any or all of this sounds intimidating, that’s because it is! But it’s also not impossible, as I’ve recently proven to myself (and hopefully to you). Luckily, Ruby gives us a lot of tools to meta program away our boilerplate code. It’s just a question of knowing what those tools are so that you can use them when you see the perfect opportunity. And when you finally metaprogram something, you’ll be so impressed with yourself that you’ll let out a little squeal of glee like this little kitty and it’ll be the cutest thing ever:
tl;dr?
- We can use dynamic dispatching to send a method to an object without being explicit about the method’s contents within our application. The
send
andpublic_send
method are two ways of doing this, and both take a string or symbol as a parameter, and use that to call a method of the same name upon the receiving object. - Learn the basics of metaprogramming over here, and check out the docs on send and public_send.
- Curious about the different types of dynamic method calls out there? This blog post covers a few different kinds in depth.