2. Reflection
a.k.a. Introspection
A program can examine its state and structure
A program can alter its state and structure
Ruby’s reflection API is rich and most of the methods are defined by Kernel,
Object and Module
3. Determine type of an object
o.class #=> Returns the class of an object c
c.superclass #=> Returns the superclass of a class c
o.instance_of? c #=> Determines whether the object o.class == c
o.is_a? c or o.kind_of? c
#=> determines o is an instance of c or any of its subclasses. If c is a module, then this
method tests whether o.class (or any of its ancestors) includes this module.
c === o
#=> For any class or module c, determines if o.is_a?(c)
o.respond_to? Name
#=> Determines whether the object has public or protected method with the specified
name. Pass true as 2nd parameter to check private methods as well
4. module C
end
class A
include C
end
class B < A
end
a = A.new
a.instance_of?(A) #=> true
b = B.new
b.instance_of?(B) #=> true
b.instance_of?(A) #=> false #=> instance_of? does not check inheritence
b.is_a?(A) #=> true
b.is_a?(B) #=> true
b.is_a?(C) #=> true
B.is_a?(A) #=> false
A.is_a?(A) #=> false
5. o = “niranjan”
o.class #=> String
o.class.superclass #=> Object
o.class.superclass.superclass #=> nil : Object has no superclass
# Ruby 1.9 only
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil
6. Ancestors of class or module
module A; end
module B; include A; end
class C; include B; end
C < B #=> true : C includes B
B < A #=> true
C < A #=> true
String < Numeric #=> nil: strings are not numbers
A.ancestors #=> [A]
B.ancestors #=> [B, A]
C.ancestors #=> [C, B, A, Object, Kernel]
String.ancestors #=> [String, Comparable, Object, Kernel]
C.include?(B) #=> true (public instance method defined by the Module class)
C.include?(A) #=> true
B.include?(A) #=> true
A.include?(A) #=> false
A.include?(B) #=> false
A.included_modules #=> [ ]
B.included_modules #=> [A]
C.included_modules #=> [B, A, Kernel]
7. Module.nesting
module M
class C
Module.nesting #=> [M::C, M]
end
end
The class method Module.nesting returns nesting of modules at the current
location.
Module.nesting[0] => current class or module
Module.nesting[1] => containing class or module
8. Variables and Constants
- global_variables
- local_variables
- instance_variables
- class_variables
- constants
These methods except local_variables return array of strings in Ruby 1.8 and
array of symbols in Ruby 1.9.
The local_variables method returns an array of strings in both versions of Ruby.
9. Querying, setting, removing and testing variables
Local variables :-
x=1
eval(“x”) #=> 1
eval(“x = 2”)
eval(“x”)
Instance variables :-
o = Object.new
o.instance_variable_set(:@x, 10)
o.instance_variable_get(:@x) #=> 10
o.instance_variable_defined?(:@x) #=> true
Class variables :-
Object.class_variable_set(:@@x, 5) #=> Private in Ruby 1.8
Object.class_variable_get(:@@x) #=> 5 ; Private in Ruby 1.8
Object.class_variable_defined?(:@@x) #=> true; Ruby 1.9 and later
10. Continued…
Constants :-
Math.const_set(:EPI, Math::E*Math::PI)
Math.const_get(:EPI) #=> 8.539…
Math.const_defined? :EPI #=> true
In Ruby 1.9, we can pass ‘false’ as 2nd argument to const_get and const_defined?
to specify that these methods should only look at the current class or module and should
not consider inherited constants.
Object and Module define private methods for undefining instance variables, class
variables and constants. (Use eval or send to invoke these methods)
They return the value of the removed variable or constant.
o.instance_eval {remove_instance_variable(:@x)}
String.class_eval {remove_class_variable (:@@x)}
Math.send :remove_const, :EPI
11. Listing and testing methods
o = “test”
o.methods #=> [ names of all public methods ]
o.public_methods #=> same as above
o.public_methods(false) #=> Exclude inherited methods
o.protected_methods #=> [ ]
o.private_methods #=> array of all private methods
o.private_methods(false) #=> Exclude inherited private methods
def o.single; 1; end # define a singleton method
o.singleton_methods #=> *“single”+
String.instance_methods == “s”.public_methods #=> true
String.instance_methods(false) == “s”.public_methods(false) #=> true
String.public_instance_methods == String.instance_methods #=> true
String.protected_instance_methods #=> []
String.private_instance_methods(false) #=> *“initialize_copy”, “initialize”+
The class methods of a class or module are singleton methods of the Class or Module object. So to list
the class methods, use Object.singleton_methods:
Math.singleton_methods #=> *“acos”, “log10”, …+
12. Define, undefine and alias methods
define_method –> private instance method of Module class
# add an instance method names m to class c with body b
def add_method(c,m,&b)
c.class_eval {
define_method(m,&b)
}
end
add_method(String, :greet) ,‘Hello’ + self-
‘world’.greet #=> ‘Hello world’
To create a synonym or an alias for an existing method :-
alias plus + # make plus a synonym for the + operator (2 identifiers to be hardcoded)
Or
alias_method # private instance method of Module class (dynamic programming)
13. Continued…
undef method_name
remove_method or undef _method => private methods of Module class
remove_method removes the definition of the method from the current class
If there is a version defined by a superclass, that version now will be inherited.
undef_method prevents any invocation of the specified method through an
instance of the class, even if there is an inherited version of that method.
14. Aliasing
A more practical reason for aliasing methods is to insert new functionality into a
method – augmenting existing methods
def hello
p ‘hello’
end
alias original_hello hello
def hello
p ‘some stuff’
original_hello
p ‘some more stuff’
end
15. Method lookup or method name resolution algorithm
For method invocation expression o.m, Ruby performs name resolution with the following
steps :-
1. First, it checks the eigenclass of o for singleton methods named m
2. If no method m is found in the eigenclass, ruby searches the class of o for an instance
method named m
3. If not found, ruby searches the instance methods of any modules included by the class of
o. If that class includes more than one module, then they are searched in the reverse of
the order in which they were included. i.e. the most recently included module is searched
first.
4. If not found, then the search moves up the inheritance hierarchy to the superclass. Steps 2
and 3 are repeated for each class in the inheritance hierarchy until each ancestor class and
its included modules have been searched.
5. If not found, then method_missing is invoked instead. In order to find an appropriate
definition of this method, the name resolution algorithm starts over at step 1. The Kernel
module provides a default implementation of method_missing, so this second pass of
name resolution is guaranteed to succeed.
6. This may seem like it requires ruby to perform an exhaustive search every time it invokes a
method.
7. However, successful method lookups will be cached so that subsequent lookups of the
same name will be very quick.
16. e.g. “hello”.world
1. Check the eigenclass for singleton methods.
2. Check the String class. There is no instance method named ‘world’.
3. Check the Comparable and Enumerable modules of the String class for an instance method
named ‘world’.
4. Check the superclass of String, which is Object.
5. Look for method_missing in each of the spots above. The first definition of
method_missing we find id in the Kernel module, so this method gets invoked.
6. It raises an exception NoMethodError : undefined method ‘world’ for “hello”:String.
7. However, successful method lookups will be cached so that subsequent lookups of the
same name will be very quick.