Creating your own attr_accessor in Ruby

When going through Ruby 101, you won’t need to do much research before you come accross the use of the attr_accessor methods. There are several flavours of this;

attr_accessor gives you a getter and a setter
attr_reader just gives you a getter
attr_writer just gives you a setter

Here’s a code example of this functionality being used;

class Person
  attr_accessor :name, :age
end

#above class definition enables the following
p = Person.new
p.name = "Mikey"
p.age = 30
puts p.name

Magic. It’s almost like you’ve somehow managed to add two more methods to the person class without defining them. Well, it turns out that this is exactly what you have done! For anyone who is not used to dynamic programming languages (and I include myself in that list) this can be a really tricky point in the learning curve. This sort of functionality is used all the time in ruby, and not only within the context of attr_accessors – The Rails framework, for example, uses dynamic code to allow you to add very complex functionality to your ActiveRecord classes with a single line of code.

If you are anything like me, then you can’t just take the fact that it works for granted! I needed to know *exactly* how this sort of thing is possible! This post will clear up exactly how this attr_accessor magic works by showing you how to make your own version of the same functionality.

Dynamic code is created using a series of “eval” methods which the ruby language makes available to you. There are several flavours (eval, instance_eval, class_eval, module_eval) – the one we’ll be using here is class_eval, which you basically use to add code to a class. As we will be trying to create a method that will be accesible from all classes, we will need to add our code to the Class class (not a typo!). This is going to get a bit headswimmy, so I’ll just go ahead and show you the code itself then discuss it below.

class Class
  #firstly, the * decoration on the parameter variable
  #indicates that the parameters should come in as an array
  #of whatever was sent
  def mikeys_attr_accessor(*args)

    #We simply iterate through each passed in argument...
    args.each do |arg|
      
      #Here's the getter
      self.class_eval("def #{arg};@#{arg};end")
      
      #Here's the setter
      self.class_eval("def #{arg}=(val);@#{arg}=val;end")                      
                      
    end
  end
end

The above code allows a new class to be defined as follows;

class Person
  mikeys_attr_accessor :name, :age  
end

Which in turn allows you to write code like this;

person = Person.new
person.name = "Mikey"
person.age = 30
puts person.name
puts person.age

A lot was going on in the first code example, but the rest of it functions exactly as you would expect attr_accessor to function. The focus here will therefore be on the first bit of code. So it all starts off pretty basically, we are able in ruby to extend any class simply by using the class keyword. We then add the method “mikeys_attr_accessor” to it, using a bit of ruby syntactic sugar to allow this method to receive any number of arguments. It’s worth pointing out that we should really check that these arguments were symbols, but as this is a simple example I’ll skip that part.

Next we iterate through each argument and make two calls to the “class_eval” method. The message we send to this is basically just an interpolated string representing the code we want included in our object. Yes, I did say object – you might raise the question at this point as to why I’m using the word “object” when we are clearly adding a method to a class. You might also question the presence of the word “self” before the “class_eval” call, surely this would mean that we have somehow managed to create an “instance” of a class?!

This was pretty much the point, while learning Ruby, that I lost cabin pressure.

class Person
end

Person.class
=> Class

The capital letter at ths start of Person indicates that it is a constant. Person is constant instance of the Class class. As you’ve instantiated the Class class in your Person class, you need to use “self” on the class eval to make sure that the class_eval method is talking to the Person instantiation of the Class class. You may need to read the previous sentence a few times before it sinks in (if you feel your ears starting to bleed, contact your GP immediately).

A better question is that, if this is the case, why is class_eval used instead of instance_eval? the short answer to that is that it has to do with scope and context. If you are interested in learning a bit more about metaprogramming, i found the following question over on stack overflow to be incredibly useful. Lots of great links from the respondants.

http://stackoverflow.com/questions/788689/ruby-metaprogramming-online-tutorial

Advertisements

3 thoughts on “Creating your own attr_accessor in Ruby

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s