Add methods dynamically in ruby

A = Class.new

class << A
  def say(sth)
    puts sth
  end
end

A
.instance_eval do
  def do(action)
    puts "Do #{action}"
  end
end

A.say('I am ok') # => I am ok
A.do('go') # => Do go
A.new.do('go')
# => NoMethodError: undefined method `do' for #<A:0x007f9a232b3698>

Don’t be confused by the name instance_eval, it’s not execute block under A’s instance context.

instance_eval

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj).

==========================================================

Let’s take a look at class_eval

A.class_eval do
  def make(sth)
    puts "Make #{sth}"
  end
end

A.make('cookie')
# => NoMethodError: undefined method `make' for A:Class

A.new.make('cookie') # => Make cookie

It’s confused. right? class_eval named `class` but not define `class method`.

class_eval

Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected.

==========================================================

Let’s take a look at  metaclass

a = Object.new

def a.dance
  puts 'I am dancing'
end

a.dance # => I am dancing

We know that the method dance isn’t defined in the class Object, because if i instance a new obj, it can not `dance`

b = Object.new

b.dance
# => NoMethodError: undefined method `dance'

So where is it? The answer is metaclass. Yehuda Katz mentioned this concept in his post(metaprogramming-in-ruby-its-all-about-the-self). Each object in Ruby also has its own metaclass – a Class that can have methods, but is only attached to the object itself. metaclass is invisible in Ruby.

What’s going on here is that we’re adding the dance method to a’s metaclass, and the a object inherits from its metaclass and then Object.

Let’s get access to the metaclass.

metaclass = class << a; self; end # => #<Class:#<Object:0x007f9a23bf0620>>
metaclass.instance_methods.grep(/dance/) #=> [:dance]

End.

[ruby] make own DSL

Let’s take a look at below example:

class A

  def learn(&block)
    instance_eval(&block)
  end

  def a
    p 'aaaa'
  end

  def b
    p 'bbbb'
  end

  def c(str)
    p str * 2
  end

end

A.new.learn do
  a
  b
  c 'c'
end
# =>
# "aaaa"
# "bbbb"
# "cc"

We can make this form string:

class A
 
  def load(recipe)
    instance_eval(recipe)
  end

end

recipe = <<-RECIPE

a
b
c 'c'

RECIPE

A.new.load(recipe)
# =>
# "aaaa"
# "bbbb"
# "cc"

[ruby] How to add new method in a class

Look at the below example:

module M
  def a
    p "aaaa"
  end
end

class A
  include M

  def self.create_method(name, &block)
    M.send(:define_method, name, &block)
  end
end

a = A.new

hello = %q{def hello() p "Hello there!"; a end}

M.module_eval(hello)

a.hello # Event i has created `a` already
# "Hello there!"
# "aaaa"

A.create_method :foo do
  puts 'I am fool'
end

a.foo # => I am fool

Learn Ruby Metaprogramming 备忘

又见元编程,元编程是拿语言本身当做操作的对象。
元编程是Ruby的一种自省能力。

1)继承族谱
顶层的是BasicObject(ruby1.9新增)

ruby-1.9.2-rc2 > BasicObject.superclass
 => nil

Object继承自BasicObject

ruby-1.9.2-rc2 > Object.superclass
 => BasicObject

Module继承了Object

ruby-1.9.2-rc2 > Module.superclass
 => Object

Class继承了Module

ruby-1.9.2-rc2 > Class.superclass
 => Module

注意观察:

Class.new.ancestors
 => [#class:0x000000022ef638, Object, Kernel, BasicObject]
Class.ancestors
 => [Class, Module, Object, Kernel, BasicObject]
BasicObject.class
 => Class

2)main
不管是java还是c,都有个main函数作为程序的入口,ruby的在哪呢?其实我们一直在用:

ruby-1.9.2-rc2 > self
 => main
ruby-1.9.2-rc2 > self.class
 => Object

我们写的代码其实都是在这个top-level对象main中发生的,一切都是对象,看吧,一点也不奇怪。

3)constants

Module#const_get
Module#const_set
Module#remove_const

来看看ruby中的一般的常量(大写开头):

irb(main):001:0> A = "1"
=> "1"
irb(main):002:0> self.class.const_get(:A)
=> "1"

我们定义一个类的时候,其实也无形中给class这个类增加了一个常量,例如:

irb(main):004:0> class Zires;end
=> nil
irb(main):005:0> Class.constants.grep("Zires")
=> ["Zires"]

irb(main):008:0> Class.constants - Object.constants
=> []

另外,constants在ruby中还扮演着类似文件路径的作用,例如:

module C
  File = "in C"
  class Doc
    File = "in c/doc"
    def root
      p File
      p ::C::File # 类似绝对路径,absolute path
    end
  end
end

irb(main):018:0> C::File
=> "in C"
irb(main):019:0> Doc::File
NameError: uninitialized constant Doc
  from (irb):19
  from :0
irb(main):020:0> C::Doc::File
=> "in c/doc"
irb(main):021:0> C::Doc.new.root
"in c/doc"
"in C"
=> nil

4) dynamic define code

Class#new
Module#define_method
Module#remove_method
Module#method_defined?

看下面的例子:

oop = "Oops!"

class Oops
  def say
    p "I say #{oop}"
  end
end

Oops.new.say #=>undefined local variable or method `oop'

oops = Class.new do

  define_method :say do
    p "I say #{oop}" 
  end

end

oops.new.say # => "I say Oops!"

5) dynamic run code

Object#send
Object#instance_eval
Module#module_eval
Module#class_eval
Kernel#eval

block
Proc
proc
lambda
method

send:

ruby-1.9.2-p180 > 1.send(:+, 2)
 => 3

eval&binding:

ruby-1.9.2-p180 > eval("1+2")
 => 3

ruby-1.9.2-p180 > x = 1
 => 1
ruby-1.9.2-p180 > eval("x + 2")
 => 3

def bind_x(x)
  x = x
  binding
end

ruby-1.9.2-p180 > eval("x + 2", bind_x(2))
 => 4

instance_eval:

ruby-1.9.2-p180 > 1.instance_eval { self + 2}
 => 3

class_eval:

1.class.class_eval do
  def add_2
    self + 2
  end
end

ruby-1.9.2-p180 > 1.add_2
 => 3

ruby中有好几个’家伙’能响应call方法,与eval家族的区别是,他们具有延迟性:

#Proc Objects

z = Proc.new { |x| p "#{x}"}
z.call("a") # call的时候才执行
 #=> "a"

l = lambda { |x| p "#{x}"}
l.call("b")
 #=> "b"

p = proc { |x| p "#{x}"}
p.call("c")
 #=> "c"

def myself(x)
  p "#{x}"
end

 m = method(:myself).class
 #=> Method
 m.call("d")
# => "d"

Proc vs proc vs lambda vs method 相同点:

#1)都响应call方法
#2)method可以转化为proc,其余都是Proc的对象
ruby-1.9.2-p180 > Proc.new{}.class
 => Proc
ruby-1.9.2-p180 > lambda{}.class
 => Proc
ruby-1.9.2-p180 > proc{}.class
 => Proc

ruby-1.9.2-p180 > method(:myself).to_proc.class
#=> Proc

***block其实也是Proc***

Proc vs proc vs lambda vs method 不同点:

#1)lambda 是Kernel方法,proc 也是Kernel方法,Proc是类,method是obj方法
#2)关于return,看例子比较
def check(callable)
  callable.call
  return "I AM Here!"
end

check lambda { return 10 }
# => "I AM Here!"

check Proc.new { return 10 }
# => LocalJumpError: unexpected return

check proc { return 10
# ruby1.8可能输出"I AM Here!",ruby1.9把proc和Proc统一了
# proc其实就是Proc.new
# => LocalJumpError: unexpected return

def myself ; return 10 ; end
check method(:myself)
# => "I AM Here!"

#3)关于参数
l = lambda { |x| p "#{x}"}
p = Proc.new { |x| p "#{x}"}
ruby-1.9.2-p180 > p.arity
# => 1
ruby-1.9.2-p180 > l.arity
# => 1
 
ruby-1.9.2-p180 > p.call
# => ""

ruby-1.9.2-p180 > l.call
# => ArgumentError: wrong number of arguments (0 for 1)

#4)lambda? 方法

ruby-1.9.2-p180 > lambda {}.lambda?
 => true
ruby-1.9.2-p180 > proc{}.lambda?
 => false

#5)简便写法
# lamda1.9新增
ruby-1.9.2-p180 > ->(x){ p "#{x}"}.class
# => Proc

#6)method可以 unbind
#  看 UnboundMethod

6) callbacks

method_missing
const_missing

included and extended
method_added and singleton_method_added
method_removed and method_undefined
singleton_method_removed and singleton_method_undefined
inherited
 
# 这些方法看文档吧,比较全