
Metaprogramming in Ruby: It’s All About the Self - ivey
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
======
patio11
I often feel, when reading articles like this about programming, that they
would be significantly enhanced by telling me why I should care about
metaprogramming.

It is not obvious to me that there is any value in being able to declare a
method on an object via metaprogramming versus just writing the method into
the class. If you, like me, have to see obvious value to change your
programming practices, let me provide you with a bit of motivation:

Metaprogramming allows you to cut down on boilerplate code, making your
programs shorter, easier to write, easier to read, and easier to test. Also,
it reduces the impact of changes.

For example, metaprogramming is used internally to provide a lot of the
"magic" that people associate with Rails. You never have to define a
find_by_email method in ActiveRecord because just putting an email column on
the table causes that method to be automatically generated for you. That saves
you perhaps three lines of code in the instant case, but when you start to
compound that over several dozen columns on 10 different tables, you're
talking about hundreds of lines of negacode.

Yeah, great for library writers, right? How do you use this in actual
application code? Well, to pick one example from my application: a stupendous
portion of the "business logic" involved in my app is creating print jobs.
Print jobs have a grab bag of properties associated with them and, every time
I implement a new feature, that means they typically get another property (or
several). If I had implemented that via adding a new column every time I added
a new feature, I'd have to add a new column to my table every week, and it
would get progressively harder and harder to manage.

Instead, I have a column called options in the table which is just a JSON grab
bag. My model class has a hash called default options, which has a bunch of
keys representing virtual properties and a bunch of values for what they
should take if the user hasn't specified anything. A quick little
metaprogramming loop goes over the default options hash and adds getters and
setters for those properties, so that they work transparently from the object.
(For example, print_job.name = "foo" sets print_job[:options][:name] = "foo".)
Another line of code sets the default values at initialization time.

This means that adding another property to my print jobs requires adding one
entry to that hash, rather than writing a bunch of getters, setters, and
dealing with migrating the 100,000 print jobs in the database which don't have
a value for it yet. This is _extraordinarily_ powerful in reducing the impact
of changes.

For example, I recently added color support to my application. This required
adding five properties to my print jobs. I just checked in SVN for how much of
an impact this had on the printing class -- _nine_ lines. The 35 lines of
boilerplate getters/setters I didn't have to add contained exactly no typos,
required no test cases, and caused no bugs. The data migration I didn't write
caused no downtime. The special cases that don't exist for jobs without color
information caused no subtle bugs two weeks later when they fell out of the
cache and then got regenerated.

[Edited to add: It occurs to me that I have a contrasting example, too. I do
Big Freaking Java Enterprise Web Apps at the day job.

Recently, we had to add the JSON grab bag approach to our internal framework,
because it would allow us to customize what data we associate with e.g.
students on a per-university basis without requiring code changes, separate
schema, and a redeployment.

This took a lot of blood, sweat, and tears from our best engineer, myself, and
one contractor -- probably about 2.5 man months, total. We did it with 100%
plain ol' Java, rather than metaprogramming.

It will save us literally hundreds of thousands of dollars, so that isn't a
terrible tradeoff, but if I had been using Ruby, I could have done it by
myself in two to three days.]

~~~
wgj
well_said <<QUOTE

Metaprogramming allows you to cut down on boilerplate code, making your
programs shorter, easier to write, easier to read, and easier to test. Also,
it reduces the impact of changes.

QUOTE

...and your JSON grab bag technique would make a nice blog post.

~~~
patio11
Alright. Be forewarned, it will be about 15 lines of code and 2,000 words of
explanation on why you want to do it that way. Check HN or my blog (in
profile) tomorrow morning US time.

~~~
wgj
That's exactly why it makes a good blog post.

------
jamesbritt
Everyone interested in this topic should watch this talk:

<http://mtnwestrubyconf2008.confreaks.com/11farley.html>

Ruby Internals - Patrick Farley

~~~
SlyShy
Thanks for posting this. Along with the article posted here it clarified a lot
of question I had.

~~~
jamesbritt
It pains me that metaprogramming in Ruby is often treated as some mystic
advanced voodoo topic, something only the adepts are free to enjoy.

It may seem weird to someone coming to Ruby from VB or Java, but a little time
peeking under the hood is revealing, and it should be one of the first things
explained to Ruby Nubies (instead of shoved off into chapter 23 or something
in the typical Ruby book).

------
Jim_Neath
I highly recommend reading the Metaprogramming Ruby book from pragprog for
people who wish to learn more.

Read the entire book in a couple of days and it really opened my eyes to new
ways of doing things.

<http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby>

