+1

Ruby Metaprogramming Classes and BlankSlate Classes

Ruby.png

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí