CSC/ECE 517 Fall 2007/wiki1b 4 19: Difference between revisions
m (→Under the hood) |
(Added section "Ruby features that support meta programming") |
||
Line 22: | Line 22: | ||
[http://practicalruby.blogspot.com/2007/02/ruby-metaprogramming-introduction.html this blog] | [http://practicalruby.blogspot.com/2007/02/ruby-metaprogramming-introduction.html this blog] | ||
is a must read. | is a must read. | ||
==Ruby features that support meta programming== | |||
#Dynamism | |||
Because of its dynamic nature ruby supports creating classes and methods at runtime | |||
For example | |||
<source lang="ruby"> | |||
Course = Struct.new(:name, :dept, :strength) | |||
csc517 = Course.new(:name => "CSC 517") | |||
</source> | |||
will create a new class, assign this to the name Course and then go ahead and create an instance of this class. | |||
Another way of creating a new class and defining a new method on it is: | |||
<source lang="ruby"> | |||
c = Class.new | |||
c.class_eval do | |||
define_method :foo do | |||
puts "Hello World" | |||
end | |||
end | |||
c.new.foo # => "Hello World" | |||
</source> | |||
#Directly defining methods on Singleton classes | |||
We can use class_eval to define methods on singleton classes. This will then modify the behaviour of the sub classes(if any) and not the base class. | |||
An excellent example of this, borrowed from [http://ola-bini.blogspot.com/2006/09/ruby-metaprogramming-techniques.html Ola Bini's Blog entry Point 2] | |||
<source lang="ruby"> | |||
class Product < ActiveRecord::Base | |||
set_table_name 'produce' | |||
end | |||
module ActiveRecord | |||
class Base | |||
def self.set_table_name name | |||
define_attr_method :table_name, name | |||
end | |||
def self.define_attr_method(name, value) | |||
singleton_class.send :alias_method, "original_#{name}", name | |||
singleton_class.class_eval do | |||
define_method(name) do | |||
value | |||
end | |||
end | |||
end | |||
end | |||
end | |||
</source> | |||
Here he wants to modify the behavior of the Product class or any other class that extends the active record module but at the same time not change the functionality of the base class. | |||
So we basically use class eval to send messages to the singleton class. Now the :table_name is aliased to product | |||
#Extending Specific Objects | |||
Ruby allows us to define methods or alter the behavior of specific objects | |||
For example:- | |||
<source lang="ruby"> | |||
class Person | |||
def greet | |||
puts "hello" | |||
end | |||
end | |||
p1 = Person.new | |||
p2 = Person.new | |||
# change p2's greet method | |||
def p2.greet | |||
puts "p2's hello" | |||
end | |||
</source> | |||
It is also possible for an object alone to extend a module, thus altering only its behavior. | |||
#Method missing | |||
Ruby allows us to define actions in case a method is missing | |||
For example | |||
<source lang="ruby"> | |||
class Person | |||
def method_missing(name, *args) | |||
puts name.to_s + " called" | |||
end | |||
end | |||
p = Person.new | |||
# no "greet" method, so "method_missing" | |||
# will be called | |||
p.greet | |||
</source> | |||
Now just as we have printed out the method called, we can also define a method at runtime. | |||
A very interesting application of method missing is for [http://rubyquiz.com/quiz22.html converting roman numerals to decimals] | |||
#Method Aliasing | |||
We can alias a method to another name and then redefine the method to do some extra work and then call the old method or whatever the case be. | |||
For example | |||
<source lang="ruby"> | |||
</source> | |||
==Guide== | ==Guide== |
Revision as of 02:06, 11 October 2007
Metaprogramming in Ruby
Introduction to metaprogramming
Metaprogramming,just as the name suggests, means “Programming a program”. This means that one can define classes and methods at runtime. It is possible only because of the dynamic nature of the language.
For instance one might take the example of taking input from a CSV file into a program. In such a case, if we are using static languages, we generally make a class or structure of the different fields in the file. We then make instances using the values. In another approach we simply use associative arrays, however this seems too unnatural and cumbersome.
However in a dynamic language such as ruby, since we are now equipped with the powers of meta programming, we can create the class depending on the structure of the CSV file i.e. if the attribute names are stored in the first line of the file, we can create a class with these as variables, at runtime, on the fly and then instantiate.
For possible meta programming implementations of the CSV problem in Ruby, I would recommend you to an excellent DEV SOURCE article
Under the hood
So you think how does ruby achieve this great feat. Well its not rocket science. Every object in ruby has a associated class that is accessible to only that object. Now recall that everything in ruby is an object. Put two and two together and you realize that classes and modules are also objects and so they too have classes, all for themselves.
These classes are called Singleton classes(accessible to only a single object). So when you send a message to an object, its singleton class is first checked to see if it has methods defined that can handle this message.
Metaprogramming is made possible because of singleton classes. Whenever we dynamically define a method on an object, it is added as a method of the singleton class for this object.
For an excellent discussion about singleton classes and their properties, this blog is a must read.
Ruby features that support meta programming
- Dynamism
Because of its dynamic nature ruby supports creating classes and methods at runtime For example
Course = Struct.new(:name, :dept, :strength)
csc517 = Course.new(:name => "CSC 517")
will create a new class, assign this to the name Course and then go ahead and create an instance of this class.
Another way of creating a new class and defining a new method on it is:
c = Class.new
c.class_eval do
define_method :foo do
puts "Hello World"
end
end
c.new.foo # => "Hello World"
- Directly defining methods on Singleton classes
We can use class_eval to define methods on singleton classes. This will then modify the behaviour of the sub classes(if any) and not the base class. An excellent example of this, borrowed from Ola Bini's Blog entry Point 2
class Product < ActiveRecord::Base
set_table_name 'produce'
end
module ActiveRecord
class Base
def self.set_table_name name
define_attr_method :table_name, name
end
def self.define_attr_method(name, value)
singleton_class.send :alias_method, "original_#{name}", name
singleton_class.class_eval do
define_method(name) do
value
end
end
end
end
end
Here he wants to modify the behavior of the Product class or any other class that extends the active record module but at the same time not change the functionality of the base class. So we basically use class eval to send messages to the singleton class. Now the :table_name is aliased to product
- Extending Specific Objects
Ruby allows us to define methods or alter the behavior of specific objects For example:-
class Person
def greet
puts "hello"
end
end
p1 = Person.new
p2 = Person.new
# change p2's greet method
def p2.greet
puts "p2's hello"
end
It is also possible for an object alone to extend a module, thus altering only its behavior.
- Method missing
Ruby allows us to define actions in case a method is missing For example
class Person
def method_missing(name, *args)
puts name.to_s + " called"
end
end
p = Person.new
# no "greet" method, so "method_missing"
# will be called
p.greet
Now just as we have printed out the method called, we can also define a method at runtime. A very interesting application of method missing is for converting roman numerals to decimals
- Method Aliasing
We can alias a method to another name and then redefine the method to do some extra work and then call the old method or whatever the case be. For example
Guide
So one might think, well I can write code that will generate code, what good can that do. Well then you must read further.
There is an excellent blog entry on the classification of metaprogramming into different types (based on usability) I dont think I could do a better job of explaining metaprogramming in ruby concisely. Thus, I refer you to [1]
To help you along the way, here is some information
- DSL stands for domain specific language(for point 2)
- Point 4 gives some more uses of method missing besides the roman numeral example, we went through.
- Point 6 is similar to the wrapping scenario discussed in class.
Some other useful links
A presentation made at a conference [2]
About Metaclasses [3]