Archive for Open Classes

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)

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

Ruby and Open Classes

When I introduce people to Ruby (at least to programmers) I tend to highlight the differences between Ruby and established programming languages, such as Java, PHP, and C/C++. I opt highlight the differences because at least all (if not those that based around C) generally have the same structure, with regards to conditions, loops, etc.

Note: The intention is not to create a flame war, or to do any bashing

Disclaimer: Out of the listed programming languages, my strength is primarily in PHP, so if I am wrong about something, do bring it up! It will make for a good discussion, and I’ll learn something new.

Note: If you are unfamiliar with Ruby, the last statement in Ruby is always returned. You can opt to use a return statement, but why? Especially when you expect the method to return something.

Classes are Open

Ruby is unique in that classes are open. Generally once you close off those brackets in that class your writing, you can’t continue adding methods to it. In Ruby you can. You can extend (not in the same way as inheritance) a class once it’s been “closed”.

You can also extend Ruby’s core classes, such as String, Hash, Numeric, Fixnum, etc.

Example: Sanitizing a String

Say for instance you want to sanitize a string of all non alphanumeric characters. Let’s say the definition of sanitize, is to replace all non alphanumeric characters with an underscore.

In PHP, you’d probably have to do this:

In Ruby, you can do this:

Now which one seems more intuitive?

In PHP we are calling a separate function entirely, that may operate on any variable type. This can be improved in several ways: Adding error checking code. Following PHP naming conventions for String functions, and call it strsanitize.

In Ruby we are basically redeclaring a new method for the String class, which operates on itself.

Example: Getting a Random Element from an Array

PHP already comes with a function to do precisely this: array_rand.

Sadly Ruby doesn’t, but hey. Let’s plug it in!

Example: Easier Time Calculations

Note: the alias statement allows us to easily duplicate a method, so I’m just duplicating them here to make singular representations

Cool eh?

Well lets extend it once again to give us the time in relation to now.

So now we’ll be using the core class Time, which when used with the addition and subtraction methods removes seconds from the time.

Note: Ruby on Rails actually uses this to extend much of the functionality not found by default in the Ruby core classes. They are packaged inside ActiveSupport, and are called Core Extensions. These blocks of code is the same as found in ActiveSupport. (activesupport/lib/active_support/core_ext/numeric/time.rb File)

The Rails Way

To achieve the simplicity found in Rails, Rails extends alot of Ruby’s core classes as seen above. Have a look into activesupport/lib/active_support/core_ext/ File. As of ActiveSupport v1.4.2 there are 63 files, providing a plethora of methods that make life easier.

Advance Example: Extending ActiveRecord

Lets pull out the big guns now and see how to extend ActiveRecord.

The convention in Rails is to put things in name space. In this I’ll use an Aizatto namespace inside ActiveRecord (ActiveRecord::Aizatto). Similarly they tend to separate the class methods and instance methods into two different modules. Giving them a unique namespace each.

So here is a rough skeleton for you to work with and see how things go:

Conclusion

Well thats the basics of Open Classes in Ruby. As can be seen open classes makes it easier to introduce new methods to existing Classes (even core classes), to add any additional methods. Sadly there isn’t really much else for me to discuss here, so enjoy!

Comments (1)