Metaprogramming in
Ruby
Nicola Calcavecchia - 24/04/2013
calcavecchia@{elet.polimi.it|gmail.com}
Principles of Programming...
Metaprogramming
• Writing code that manipulates language constructs at runtime
• In Ruby no distinction:
• Same as “regula...
Introspection
• Allows to get information about objects at runtime
• Methods
• Instance variables
• etc.
class MyClass
! d...
Open Classes
• Classes can be “opened” for change
• What about the open/closed principle?
class MyClass
! def a; "method a...
Monkeypatching
• Refers to the general idea of modifying the runtime code
without modifying the original source code
• Pro...
Objects and classes
• Objects contains instance variables
• Remember: instance variables exists only when assigned
• Objec...
Classes are objects too
• class
• superclass
• All objects inherit from BasicObject
Class.superclass # => Module
Module.su...
Classes and superclasses
obj1
obj2
MyClass
Object
Class
Module
class
class
class
superclass superclass
•What’s the class o...
Invoking methods
• Calling a method involves two steps:
1. Method lookup
• Identify receiver class
• Escalate ancestor cha...
Ancestor chains
module M
! def my_method
! ! 'M#my_method'
! end
end
class C
! include M
end
class D < C; end
D
C
M
Object...
self
• Every line of Ruby is executed within an object
• Called current object: self
• Only one object holds the self at a...
Calling methods
dynamically
• Remember “sending messages to objects” ?
• Method send sends messages to objects
• Can be us...
Defining methods
dynamically
• Use the Module#define_method method
• Provide a block for the method body
class MyClass
! ["s...
method_missing
• What happens if no method is found in the ancestor
hierarchy?
• A method called method_missing is called
...
An example
class Mapper
! def initialize()
! ! @map = {}
! end
! def add(key, value)
! ! @map[key.downcase] = value
! end
...
instance_eval
• Allows to evaluate a piece of code within the
scope of an object
• That is: changes the self for a piece o...
class_eval
• Evaluates a block in the context of an existing class
• Changes the self (i.e., it reopens the class)
def add...
Singleton methods
• In Ruby it is possible to add a method to a single
instance of an object
str1 = "This is a string!"
st...
Singleton methods - 2
• Are stored in special classes called eigenclasses
• Invoking Object#class does not show eigenclass...
Method aliases
• Introduce new names for methods
class MyClass
! def my_method; 'my_method()'; end
! alias :m :my_method
e...
Kernel#eval
• Instead of taking a block, it takes a string containing the
code
• Code is executed and the result of expres...
Hook methods
• Various aspects of classes and method definition can
be caught at runtime
• Similar to events
• For example ...
References
23
Upcoming SlideShare
Loading in …5
×

Metaprogramming in Ruby

945 views

Published on

Published in: Technology
1 Comment
3 Likes
Statistics
Notes
No Downloads
Views
Total views
945
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
25
Comments
1
Likes
3
Embeds 0
No embeds

No notes for slide

Metaprogramming in Ruby

  1. 1. Metaprogramming in Ruby Nicola Calcavecchia - 24/04/2013 calcavecchia@{elet.polimi.it|gmail.com} Principles of Programming Languages 1
  2. 2. Metaprogramming • Writing code that manipulates language constructs at runtime • In Ruby no distinction: • Same as “regular” programming • Enables: • Code that writes code • DSLs • Introspection (e.g., reflection) 2
  3. 3. Introspection • Allows to get information about objects at runtime • Methods • Instance variables • etc. class MyClass ! def initialize() ! ! @var1, @var2 = 0, 2 ! end ! def my_method ! end end m = MyClass.new m.class => MyClass m.methods => # lot of methods ... m.instance_variables => [:@var1, :@var2] m.public_methods => # ... m.private_methods => # ... m.instance_of? MyClass => true m.instance_of? Object => false m.is_a? MyClass => true m.is_a? Object => true 3
  4. 4. Open Classes • Classes can be “opened” for change • What about the open/closed principle? class MyClass ! def a; "method a"; end end m = MyClass.new m.methods - Object.new.methods => [:a] class MyClass ! def b; "method b"; end end m.methods - Object.new.methods => [:a, :b] class Numeric ! KILOBYTE = 1024 ! def kilobytes ! ! self * KILOBYTE ! end end puts 2.kilobytes => 2048 4
  5. 5. Monkeypatching • Refers to the general idea of modifying the runtime code without modifying the original source code • Problems: • Redefining existing methods • Change methods used in other pieces of the code • Ruby 2.0 introduced “scoped” monkeypatching 5
  6. 6. Objects and classes • Objects contains instance variables • Remember: instance variables exists only when assigned • Objects contains: • Instance variables • Reference to its class • object_id class MyClass ! def my_method ! ! @v = 1 ! end end obj = MyClass.new obj.my_method obj1 @v = 1 MyClass my_method() object instance variables class methods class 6
  7. 7. Classes are objects too • class • superclass • All objects inherit from BasicObject Class.superclass # => Module Module.superclass # => Object String.superclass # => Object Object.superclass # => BasicObject BasicObject.superclass # => nil 7
  8. 8. Classes and superclasses obj1 obj2 MyClass Object Class Module class class class superclass superclass •What’s the class of Object ? •What’s the superclass of Module ? •What’s the class of Class ? class MyClass; end obj1 = MyClass.new obj2 = MyClass.new BasicObject superclass nil superclass 8
  9. 9. Invoking methods • Calling a method involves two steps: 1. Method lookup • Identify receiver class • Escalate ancestor chain until the method is found 2. Method execution • The actual code is executed class A; end class B < A; end B.ancestors => [B, A, Object, Kernel, BasicObject] The ancestor chain includes also modules 9
  10. 10. Ancestor chains module M ! def my_method ! ! 'M#my_method' ! end end class C ! include M end class D < C; end D C M Object Kernel BasicObject 10
  11. 11. self • Every line of Ruby is executed within an object • Called current object: self • Only one object holds the self at any given time • Instance variables and methods (without explicit receiver) are called on self • In class or module definition the role of self is taken by the class or module class MyClass ! self! # => MyClass end 11
  12. 12. Calling methods dynamically • Remember “sending messages to objects” ? • Method send sends messages to objects • Can be used to dynamically call methods class MyClass ! def my_method(my_arg) ! ! my_arg * 2 ! end end obj = MyClass.new obj.send(:my_method, 3)! # => 6 12
  13. 13. Defining methods dynamically • Use the Module#define_method method • Provide a block for the method body class MyClass ! ["steve", "jeff", "larry"].each{|d| ! ! ! define_method d.to_sym do ! ! ! ! puts d ! ! ! end ! ! } end obj = MyClass.new obj.steve! # => "steve" obj.jeff! # => "jeff" obj.larry! # => "larry" 13
  14. 14. method_missing • What happens if no method is found in the ancestor hierarchy? • A method called method_missing is called • A common idiom is to override this method in order to intercept unknown messages • Define ghost methods • Methods that do not actually exists! 14
  15. 15. An example class Mapper ! def initialize() ! ! @map = {} ! end ! def add(key, value) ! ! @map[key.downcase] = value ! end ! def method_missing(method_name, *args) ! ! key = method_name.to_s.downcase ! ! return @map[key] if @map.key? key ! end end m = Mapper.new m.add("Rome","IT") m.add("London","UK") puts m.rome puts m.london 15
  16. 16. instance_eval • Allows to evaluate a piece of code within the scope of an object • That is: changes the self for a piece of code class MyClass ! def initialize ! ! @v = 1 ! end end obj = MyClass.new obj.instance_eval do ! self!# => #<MyClass:0x83fd33 @v=1> ! @v! ! # => 1 end v = 2 obj.instance_eval { @v = v} obj.instance_eval { @v }! # => 2 BREAKS ENCAPSULATION! Read/write private data With great power comes great responsibility! 16
  17. 17. class_eval • Evaluates a block in the context of an existing class • Changes the self (i.e., it reopens the class) def add_method_to_(a_class) ! a_class.class_eval do ! ! def m ! ! ! "Hello!" ! ! end ! end!! end add_method_to String "abc".m ! # => "Hello!" More flexible than reopening it with the class keyword (i.e., parametric) 17
  18. 18. Singleton methods • In Ruby it is possible to add a method to a single instance of an object str1 = "This is a string!" str2 = "Another str" def str1.title? ! self.upcase == self end str1.title?!# => false str2.title? => NoMethodError: undefined method `title?' for "Another str":String 18
  19. 19. Singleton methods - 2 • Are stored in special classes called eigenclasses • Invoking Object#class does not show eigenclasses • Special syntax to enter in their scope class << str1 ! # Eigenclass scope ! def title? ! ! upcase == self ! end end #str1 title? String Object str1 superclass superclass Eigenclass Method lookup revisited 19
  20. 20. Method aliases • Introduce new names for methods class MyClass ! def my_method; 'my_method()'; end ! alias :m :my_method end obj = MyClass.new obj.my_method! # => "my_method()" obj.m ! ! ! # => "my_method()" class String ! alias :real_length :length ! def length ! ! real_length > 5 ? 'long' : 'short' ! end end We can invoke the method with two names Redefine but still use the old one (this is also called around alias) 20
  21. 21. Kernel#eval • Instead of taking a block, it takes a string containing the code • Code is executed and the result of expression is returned a = 1 b = 2 c = eval("a + b") puts c => 3 Problems • Code injection • Readability • etc. 21
  22. 22. Hook methods • Various aspects of classes and method definition can be caught at runtime • Similar to events • For example method_missing class String ! def self.inherited(subclass) ! ! puts "#{self} was inherited by #{subclass}" ! end end class MyString < String; end => "String was inherited by MyString" module M ! def self.included(other_mod) ! ! "M was mixed into #{other_mod}" ! end end class C ! include M end => "M was mixed into C" 22
  23. 23. References 23

×