bar_1

contents_map

2014年12月24日水曜日

[Ruby]シンボルとコードブロックからクラスを動的に定義するメソッド: class_define

Ruby では Class.new で無名のクラスオブジェクトは生成することができる。
このとき
  • クラス名
  • クラス本体
を与えて、動的にクラスを作れないか?を考える。
通常のクラス定義 class Foo では、識別子のレベルでクラスの名前 (定数) が与えられていなければならない。
Class.new では、無名のクラス・オブジェクトを作れるが、今回の目的を果たすには命名が必要である。
だから、どうすればいいかというと
  • 無名のクラス・オブジェクトを作ってから、それにクラス名を与える
  • この無名のクラス・オブジェクトは、クラスの中身 (これから作ろうとしているクラスで評価されるコードブロック) を持つ
ことができればよい。

class_define

以下のコードで作ってみた。

#
# file: class_define.rb
#

# create nameless class object with code block.
# ==== Args
# &block :: code block
# ==== Return
# a class object.
# ==== See Also
# * class_define
def create_class( &block )
  class_obj = Class.new
  class_obj.class_eval(&block)  # self will be bound at designation.
  class_obj
end

# name a nameless class object.
# ==== Args
# name :: class name in symbol.
# class_obj :: class object.
# parent :: current class.
# ==== Return
# const value (class name)
# ==== See Also
# * class_define
def designate_class( name, class_obj, parent=self )
  #eval "#{name.to_s}=class_obj"
  parent.const_set(name, class_obj)
end

# generate a class with code block.
# ==== Usage
# in top-level or class definition scope,
#   class_define :Foo do
#     def foo; "#{__method__} #{self}"
#   end
# ==== Args
# const_sym :: a name of class in symbol.
# parent :: current class.
# &block :: body of class.
# ==== Return
#
def class_define( const_sym, parent=self, &block )
  parent    = parent.class unless parent.to_s =~ /^[A-Z]/
  class_obj = create_class(&block)
  designate_class(const_sym, class_obj, parent)
end

#### end of filename: class_define.rb

テスト


require "class_define"

class_define :Foo do
  def foo; "#{__method__} #{self}"; end
end

class_define :Car do
  def foo; "#{__method__} #{self}"; end
end

p Foo.new.foo
p Car.new.foo
begin
  foo
rescue => e
  puts e.message
end

# class in class (Foo::Bar)
class Foo
  class_define :Bar do
    def foo; "#{__method__} #{self}"; end
  end
end

# class in class (Car::Cdr)
class_define :Cdr, Car do
  def foo; "#{__method__} #{self}"; end
end

p Foo::Bar.new.foo
p Car::Cdr.new.foo
実行すると、

 $ ruby -I. test-class_define.rb
"foo #<Foo:0x000001011679c8>"
"foo #<Car:0x00000101167838>"
undefined local variable or method `foo' for main:Object
"foo #<Foo::Bar:0x00000101167180>"
"foo #<Car::Cdr:0x00000101166ff0>"
となり、いい感じである。

0 件のコメント:

コメントを投稿

何かありましたら、どうぞ: