Words and Code

One writer’s journey from words to code.

Metaprogramming Dynamic Methods: Using Public_send

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
2.2.0 > 3 + 4
 => 7

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
2.2.0 > 3.send(:+, 4)
 => 7

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
class Book < ActiveRecord::Base
  validates_presence_of :title, :author
end

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
class AddViewableByToBooks < ActiveRecord::Migration
  def change
    add_column :books, :viewable_by, :string, null: false, default: "title"
  end
end

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
def show_cover?
  self.viewable_by == 'media'
end

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
def book_html
  if show_cover?
    # Code here will generate and return
    # an html image tag to render in view.
  else
    send(self.viewable_by)
  end
end

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
class Book < ActiveRecord::Base
  validates_presence_of :title, :author

  def show_cover?
    self.viewable_by == 'media'
  end

  def book_html
    if show_cover?
      # Code here will generate and return
      # an html image tag to render in view.
    else
      public_send(self.viewable_by)
    end
  end
end

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 and public_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.