Archive for Language Features

Ruby and Metaprogramming

Metaprogramming is something that Ruby developers always get so giddy about. Perhaps because it invokes a high level of power and control over the programming language.

Some would deem it unworldly, unsafe, and perhaps even insane.

Background Behind Metaprogramming

The first paragraph by Wikipedia describes well what Metaprogramming is all about.

Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data or that do part of the work during compile time that is otherwise done at run time. In many cases, this allows programmers to get more done in the same amount of time as they would take to write all the code manually.

Note: Emphasis by me

We’ve all heard about code generating code. For example defining a database schema in Propel (a PHP ORM) and letting that create the files, clsases, and all sorts of things, even the SQL for you to setup your table.

But, Metaprogramming is not that.

It’s about doing it on the fly, while the program is executing.

Similarly like Open Classes, metaprogramming is not commonly found in all languages. In fact in languages that support it, people don’t tend to use it.

Metaprogramming is how Rails does alot of its magic. Associations (belongs_to, has_one, has_many), Acts As (acts_as_list, acts_as_nested_set, acts_as_tree). In fact its not just limited to these, but these are the first ones that come to mind.

Don’t worry if you don’t grok what metaprogramming is at first. Most people didn’t, including myself. In fact, I think this post can be improved, and I’ll try my best to make it a valuable resource.

Simple Example

For starters attr_accessor, attr_reader, and attr_writer are actually class methods. These class methods creates instance methods to access/read and mutate/assign the instance variables of the symbols stated.

  • attr_accessor — Creates both accessor and mutator methods
  • attr_reader — Creates only the accesor method
  • attr_writer — Creates only the mutator methods

This is the fully expanded version:

Note:

  • @variable refers to an instance variable
  • We don’t need to explicitly state ‘return as the last statement is always returned

So the 4 lines of code, expands into a 25 lines. Both blocks of codes, do the exact same thing but the first one is much more readable and maintainable.

Execution Within Classes

Actually code inside the class executes normally. If you want, you can have an if condition in there. Its legal space for you to call functions, and do other magic.

Note:

  • variables starting with a dollar sign ($) are global variables
  • The self in def.self.something, refers to the class, as we are in the class scope. If it was inside a instance method, it would refer to the instance scope. This declaration is creating a class method.

When trying to execute this class with:

The output is:
I’m in the User class, executing your code!
I’m done executing your code, and getting out of your class!
I’m a Class Method
Error: undefined method `something’ for # (NoMethodError)

The first two line you see are the execution class executing those lines.
In the third line we call the class method.
In the fourth line we attempt to call the instance method, but it fails. Because the condition never allowed it to be created.

When we set the $condition to false. This is the following output:

I’m in the User class, executing your code!
I’m done executing your code, and getting out of your class!
Error: undefined method `something’ for User:Class (NoMethodError)

It only outputs three lines, as its expecting a class method to exist, but due to the if condition, it was never created. Though the instance method will execute just fine.

Note: This is only sample code, you can do funkier things with it. It is simply to show that the execution follows within the class itself.

eval

Some may consider eval to be a sin! It lets you execute a string as if it was normal running code. That doesn’t make it evil, but it can cause unexpected results if those strings are magically composed together, and didn’t come out as you expected it to be.

Note: The method respond_to? is to test whether the object will “respond to” a method call, in this case kazaam. It returns false first because the method hasn’t been created yet.

In this example the MagicLamp creates a instance method only when the class method remember_incantation is called. The instance method corresponds to the incantation that the MagicLamp was told to remember.

class_eval, instance_eval, module_eval

Ruby has several evals (which make it even more confusing). eval, class_eval, instance_eval, module_eval.

Why does someone need so many evals? They each execute the code within a certain context, class, instance, or module, or whatever. I won’t go into further details, but its good to be aware that they exist.

The Rails Example

So here is another Rails Example using both Open Classes and Metaprogramming to make life easier ™. As you are aware, there exists the attr_accessor, attr_reader, and attr_writer for instance variables. But these methods only exist for instance variables. How about for class variables? How dare they discriminate such variables! But don’t worry. With the power of Ruby you can easily fix this…mixup.

So here is an example from the ActiveSupport (v1.4.2 — corresponds to Rails v1.2.3) libraries located at activesupport/lib/active_support/core_ext/class/attribute_accessors.rb File

So my dear readers, how many of you understand that? Raise your hands now! Good, good, now for those of you who don’t understand. Let me guide you.

The first line indicates that we are working with the class Class. Class is the class, that all classes belong to. So a String class is actually an instance of the Class class. Grok that? If not, try having a look at my post on Everything Is An Object In Ruby.

So in this block of code, we are defining 3 methods: cattr_reader, cattr_writer, and cattr_accessor.

cattr_accessor is the easy one. It just calls the other two methods. The asterisk (*sym) in an arguments list indicates that it wants all arguments from this current argument forward to be collected in an array variable, in this case sym.

If you are having a problem understanding that, pull up irb and try the following:

cattr_reader iterates through the array, and calls class_eval with the arguments for <<-EOS, __FILE__ and __LINE__.

The <<-EOS is referred to as a heredoc, another method of creating strings without using the single or double quotation marks. To indicate the end of the string you enter the first characters preceding the heredoc (EOS), which yup, you’ve guessed it! It stands for “End of String“. These characters can be anything really.

__FILE__ and __LINE__ are magic constants. They aren’t exactly constants per se, but indicate the current state of the execution, in which file and line it currently is in.

So class_eval is taking in a very large string which describe three components.

The first section tests to see if the class variable has been created, and unless it has, it is assigned the value of nil. @@variable refers to a class variable.

The second section creates the class method to return the value of the class variable.

This final section creates the instance method to return the value of the class variable.

So whenever you call cattr_reader from an executing class. These class variable is created, unless it already has, and the class and instance methods to access that variable is created as well.

cattr_writer is slightly different in that it also accepts options, via the last argument being an Hash.

If the Hash is the last element in the array, it is simply popped off.

Here we are creating the class method to write to the class variable.

What you see here is actually a block whose result will be outputted as a string. Inside the block, you’ll actually see the string contents, where it declares the method to be created. But at the very end you’ll notice the unless condition, which is where the options come into play. So unless you specify a :instance_writer => true, in your arguments an instance method will be created allowing to access the class variable

Well that pretty much describes the whole file.

How to use this then?

Note: Due to its brevity, this example sucks big time and it can be highly improved. But I can’t think of one now. I’ll replace it later.

Conclusion

You can do some really sweet things with Metaprogramming, but the examples here are not sufficient to describe what you can do. They are too succinct, and don’t give a context on how it is to be used. But I hope this provides a helpful insight to the existence and usage of Metaprogramming.

At least you know that Metaprogramming exists, so if ever need be. Ruby is there for you.

Comments (5)

Everything Is An Object in Ruby

In Ruby, everything is an object.

That’s right, including those “primitive types” as understood by other languages.

Even classes are an instance of a class. Funky.

But what does mean?

For starters, it means everything has an interface for method invocation.

Results:

Hello World
Hello World
Hello World
Hello World
Hello World

Note: In Ruby you can make numbers easier to read by separating them with a delimiter (in this case the underscore).

To find out whether an object can “respond to” a certain method invocation, is as simple as:

As a Numeric (1) cannot respond to a downcase, it returns a false.

Note:

  • :downcase is a Symbol. Anything prefixed with a colon is a Symbol. A Symbol is like a quick way to represent a constant without defining it, or giving it any value. For all intensive purposes its to make it easy to identify certain things in Ruby. For example, fields, methods, keys, and many more, it is not limited to just this.
  • The String method downcase helps to downcase/lowercase the letters

Epiphany

When I started Ruby programming, I was at awe when I saw object instantiation.
In Ruby:

Compare to the traditional style, in PHP:

new is a class method of the User class, and not a statement of its own.

Behind The Scenes: The Creation of a Ruby Class

So since all classes are a class of Class, when you define a new class, like so:

Behind the scenes you are actually creating an instantiation from the Class class:

They both accomplish the same thing.

Together With The Power of Open Classes

As everything is an object, and Ruby allows Open Classes, the core classes of Ruby can be easily extended to adapt to your needs. See my previous posts about Open Classes.

Example:

Comments

The Potential For Abuse With Open Classes

It is believed that open classes provide an invitation for for potential abuse. My argument is that anything provides an invitation for potential abuse, hell I can be sniffing glue in my room for all you know. (No I don’t).

It is commonly understood that PHP is repeatedly abused to produce spaghetti code of PHP, SQL, HTML, JavaScript, and CSS. Yummy! This is not a fault of PHP, but a fault of the developers of the spaghetti code. There are alot of new frameworks out there that are based on a Model View Controller design pattern to separate login from presentation. symfony and CodeIgniter come to mind.

Anyone can write spaghetti code in any language. It is the job of the developer to organize code.

Much in the same way in that anything can be done via open classes. It is up to the developer to write good code.

For example Rails comes with a reasonable set of core extensions which don’t abuse the use of open classes.

Comments

« Previous entries