Ruby-Metaprogramming
Module and Class
Wa = Module.new
Wa.class # Module
Wa.superclass # Throw Exception 'NoMethodError' here.
# Means 'modules' do not have parents or son. 'modules' are alone.
# Only can be included or include other 'modules'.
Wa.new # Throw Exception 'NoMethodError' here. Means 'modules' couldn't be initialized.
Wa
Wa.module_eval { def a; puts 'a'; end }
Wa.class_eval { def b; puts 'b';end }
Ca = Class.new
Ca.module_eval { include Wa }
Ca.new.a # a
Ca.new.b # b
# 'module_eval' is equal to 'class_eval'.
# They can be used on 'modules' or 'classes'.
# They both means 'Open Class'.
Wa = Module.new {
def a; end
def b; end
}
Ca = Class.new
Mo = Module.new {
include Wa # 'include' method is defined at 'Module' class,
# so a module can include another module.
def c; end
}
Ca.class_eval do
include Mo
end
puts Ca.instance_methods.include?(:a) && Ca.instance_methods.include?(:b) # true
puts Mo.instance_methods.include?(:a) && Mo.instance_methods.include?(:b) # true
# 'instance_methods' is defined at 'Module', so a 'module' also have use it.
# So most methods used for 'class' can also be used for 'module'.
puts Ca.instance_methods.include?(:c) # true
module Mo
def kk
@f = 5 # This 'instance_variable' is used for a initialized Class.
# Module could not be initialized. But we can define 'instance_variable' here.
end
end
class Abc
include Mo
end
# Abc.include(Mo) # This line is equal to the previous three lines.
a = Abc.new
a.kk # If not executed, the 'instance_variable' @f is nil.
puts a.instance_variable_get(:@f).inspect # 5
puts Abc.instance_methods.grep(/k/).inspect # [:kk, :kind_of?]
puts Abc.instance_methods == a.methods # true
puts Class.instance_methods == Abc.methods # true
inherited = false
Class.instance_methods(inherited) # [:allocate, :new, :superclass]
class Bc < Abc
define_method(:abc) do
# Here we entered the scope of instance! The same as in method :abcd .
end
def abcd
# Here we entered the scope of instance! The same as in method :abc .
end
end
puts Bc.superclass == Abc # true
# In top-level env, 'self' is object 'main'.
self.respond_to?(:define_method, true)
# Return true. But 'define_method' method is defined at 'Module' class,
# so only modules have method 'define_method'. 'main' is a special object. Matz just want to make programming easy, so he make 'main' different,
# then you can use the defined method after the method defined in main.
class GGG
end
ggg = GGG.new
ggg.instance_eval do
def kkkkgg
puts "kkkk"
end
end
ggg.kkkkgg # kkkk. So 'instance_eval' can enter object's 'eigenclass' scope.
# This is different form 'class_eval'. 'class_eval' is defined in 'Module', but
# 'instance_eval' is defined in 'Object'.
# 'class_eval' means open class, the method defined in 'class_eval' is used for instance.
# These below looks like 'Classes', because they are upper cased first letter.
# But actually they are methods. They are defined in 'Kernel' module, so they can be used by any objects.
# puts self.private_methods # Then you can see them.
String(5)
Array("some")
Integer("5")
module Rack
# Here the code in file 'rack/builder' is not executed until you use the class 'Rack::Builder'.
autoload :Builder, "rack/builder"
# But 'require' is different from 'autoload' because it will run the code of "something" immediately.
# So 'rack' use 'autoload' to announce load all the 'modules' it needs but not actually load they.
require "something"
end
obj.singleton_class
is obj's eigenclass
. Let’s prove it.
class Tom
puts "class Tom's singleton_class is #{self.singleton_class.inspect}"
class << self
puts "class Tom's eigenclass is #{self.inspect}"
puts "#{self == Tom.singleton_class}" # true
end
end
tom = Tom.new
tom_sc = tom.singleton_class
puts "#{tom_sc.inspect} #{tom_sc.object_id}" #<Class:#<Tom:0x00007fe88f14bae8>>70318404820300
tom_sc.instance_eval do
puts "#{self.inspect} #{self.object_id}" #<Class:#<Tom:0x00007fe88f14bae8>>70318404820300
puts "#{tom.singleton_class == self}" # true
end
When I was reading Rails source code in railties/lib/rails/application.rb
, I see this code
def instance
super.run_load_hooks!
end
What does this mean? It really confused me. After a deep research, I realize that this code is equal to
def instance
a_returned_value = super # Keyword 'super' will call the parent's same name method. In this example, it is method named 'instance'.
a_returned_value.run_load_hooks!
end
Many people may think super.run_load_hooks!
means self.superclass.run_load_hooks!
That’s wrong.
- Prefer lambda to proc because lambda’s
return
and passingparameters
are good for more using condition.
class MyClass
def initialize(value)
@x = value
end
def my_method
@x
end
end
object = MyClass.new(2)
m = object.method(:my_method)
m.call
class MyClass
class << self
puts self.object_id # 70137814629400
end
end
sc = MyClass.singleton_class
puts sc.object_id # 70137814629400
MyClass.class_eval do
# Will treat MyClass as a class. So current class is 'MyClass'.
def abc
puts "-- abc"
end
puts "#{self == MyClass}" # true
end
MyClass.instance_eval do
# Will treat MyClass as an object. So current class is 'sc'.
# Will define a method in class 'sc'.
def i_t
puts "-- i_t"
end
puts "#{self == MyClass}" # true
end
mc = MyClass.new
mc.abc
# mc.i_t # Will throw exception because 'i_t' is a method for MyClass. 'i_t' is defined in MyClass's singleton_class
MyClass.i_t
- Do you know why the methods defined under the top level context
self is 'main'
can be used for all objects?
Because in the top level context, the current class is main
’s class: Object
. That mean you’re defining methods in class Object.