Ruby Metaprogramming Classes and BlankSlate Classes
Bài đăng này đã không được cập nhật trong 3 năm
Today I feel like going back to the foundation of Ruby "Classes". Classes are nothing but objects. This is the most distinguised aspect of Ruby which set it apart from other languages.
1. Class is object
First we have to make sure that we understand 'object' and 'class' and 'methods' clearly.
class MyClass
def my_method
@var = 1
end
end
obj = MyClass.new
obj.class #MyClass
Here is the point: instance variable @var
lives in object obj
where method my_method
lives in class MyClass
. Instance_method lives in class because because different object use the same method.
Now the crucial point is: every class is an object of class Class
, which in turn has 3 unherited instance methods [:new, :allocate, :superclass]. So as a result every classes has 3 method above at least.
Let follow the following examples:
"ruby".class # => String
String.class # => Class
inherited = false
Class.instance_methods(inherited) # => [:superclass, :allocate, :new]
BasicOject is the root class of Ruby. BasicObject is just created in Ruby 1.9 to create a BlankSlate class which we will discuss later.
String.superclass # => Object
Object.superclass # => BasicObject
BasicObject.superclass # => nil
Every class is just an object even 'BasicObject'
Oject.class # => Class
BasicOject.class # => Class
Let see what is the superclass of Class
Class.superclass # => Module
Module.superclass # => Object
So, a class is just module with three additional methods— new( ), allocate( ), and superclass( )—that allow you to create objects or arrange classes into hierarchies. Apart from these (admittedly important) differences, classes and modules are pretty much the same. Most of what applies to classes also applies to modules, and vice versa.
2. What is BlankSlate class?
For example, you have code like which implement method_missing one aspectof the magics of Ruby metaprogramming:
We have class Phone
.
class Phone
def respond_to?(method)
@data_source.respond_to?("get_#{method}_info" ) || super
end
def initialize(book_id, data_source)
@id = phone_id
@data_source = data_source
end
def method_missing(name, *args)
super if !@data_source.respond_to?("get_#{name}_info" )
info = @data_source.send("get_#{name}_info" , args[0])
price = @data_source.send("get_#{name}_price" , args[0])
result = "#{name.to_s.capitalize}: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end
end
And Class DataSource
.
class DataSource
def initialize
end
def get_display_info(phone_id)
"Retina screen"
end
def get_display_price(phone_id)
40
end
def get_cpu_info(phone_id)
"2.16 Ghz"
end
def get_cpu_price(phone_id)
220
end
end
If we run the following code:
my_phone = Phone.new(12, DataSource.new)
my_phone.cpu # => "* Cpu: 2.16 Ghz ($220)"
my_phone.display # => nil
It should display message
"Display: Retina screen ($40)"
But on the contrary it show nil
Let's find out:
Object.instance_methods.grep /^d/ # => [:dup, :display, :define_singleton_method]
It seems that Object defines a method named display( ) (a seldom-used method that prints an object on a port and always returns nil). Computer inherits from Object, so it gets the display( ) method. The call to Phone#display( ) finds a real method by that name, so it never lands on method_missing( ). You’re calling a real, live method instead of a Ghost Method.
Wheneverthe name of a Ghost Method clashes with the name of a real, inherited
method, the latter wins. If you don’t need the inherited method, you
can fix the problem by removing it. To stay on the safe side, you might
want to remove most inherited methods from your proxies right away.
The result is called a Blank Slate
, a class that has fewer methods than the Object class itself.
So we make some change to class Phone
class Phone
instance_methods.each do |m|
undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/
end
...
end
Now if you run code again everything is just fine.
We will meet more in the next post and more in depth on metaprogramming. I hope you enjoy the lesson. Finally I welcome any comments which will help improve my future post. Thank you. Please follow and subcribe.
All rights reserved