ruby wayの動的特性の1つ

8424 ワード

1動的evaluateコード
グローバルメソッドevalはrubyコードの断片を含む文字列をコンパイルして実行します.これは実行時に構築できるpowerのメカニズムです.
実行されるコード.次の例を見てください.
parameters = {}

ARGF.each do |line|
  name, expr = line.split(/\s*=\s*/, 2)
  parameters[name] = eval expr
end

出力は次のように仮定されます.
参照
a = 1
b = 2 + 3
c = `date`
そしてあなたのparametersはこうなるはずです{"a"=>1,"b"=>5,"c"=>"Mon Apr 30 21:17:47 CDT 2001"}.でもこれじゃ危ない
危険性が高く、「rm*」が入ってきたら憂鬱です.
rubyには、実行時にevaluateコード、class_を実行する3つの方法があります.eval, module_eval,instance_eval、最初の2つは同義で、この3つは
つの方法は実はしたことはすべて同じで、彼らはすべてevaluateの1つの文字列で、あるいは1つのblockで、しかしこの事をする時、彼らは
selfは彼ら自身の受信者に変わった.いくつかの例を見たいなら、rubyの内蔵ライブラリdelegateを見ることをお勧めします.rb.
evalメソッドは、ローカル変数の作成ドメインの外でevaluateを呼び出すこともできる.
Kernel#bindingメソッドを使用して、現在のバインドをオブジェクトに割り当てることができます.evalの2番目のパラメータはこのバインドです.
def some_ethod
  a = "local variable"
  return binding
end

the_binding = some_method
eval "a", the_binding   # "local variable"

blockが付いている場合、このblockはこのバインドの一部として格納されます.
def some_method
  return binding
end

the_binding = some_method { puts "hello" }
eval "yield", the_binding                # hello

2 const_を使うget
const_getメソッドは、クラスまたはモジュールから定数の値を取得します.
str = "PI"
Math.const_get(str)     # Evaluates to Math::PI

これはevalの使用を避ける方法です.似たような方法もありますvariable_set, instance_variable_get和
define_method.
const_getはevalより速く、彼はもっと読むことができて、もっと明確です.
3クラスを名前で動的にインスタンス化
const_を使用できますgetメソッド.rubyのすべてのクラスは、通常、Objectのグローバルメンバーとして定数として命名される.
classname = "Array"

klass = Object.const_get(classname)
x = klass.new(4, 1)        # [1, 1, 1, 1]

クラス名がネストされていると、エラーが表示されます.
class Alpha
  class Beta
    class Gamma
      FOOBAR = 237
    end
  end
end

str = "Alpha::Beta::Gamma::FOOBAR"
val = Object.const_get(str)          # error!

なぜならconst_getメソッドはそれほどスマートではありませんが、私たちはこのようにすることができます.
str = "Alpha::Beta::Gamma::FOOBAR"
val = str.split("::").inject(Object) {|x,y| x.const_get(y) }  # 237

4動的アクセスインスタンス変数
次の例を見てください.
class MyClass
  attr_reader :alpha, :beta

  def initialize(a,b,g)
    @alpha, @beta, @gamma = a, b, g
  end
end


x = MyClass.new(10,11,12)

x.instance_variable_set("@alpha",234)
p x.alpha                                   # 234

x.instance_variable_set("@gamma",345)       # 345
v = x.instance_variable_get("@gamma")       # 345

ここで注意すべきは、変数のフルネームをパラメータとしなければならない、すなわち@記号をつけることである.
5 define_の使用method
defよりdefine_methodは普通の方法をオブジェクトやクラスに追加する方法にすぎない.
1つのメソッドの体内、または他の類似の場所で、このクラスを再開したい場合(つまり、いくつかのメソッドを追加したい場合)、このクラスがsingleton classでない限り.この場合、古いバージョンのrubyではevalを使用する必要がありますが、define_を使用することができます.method.なぜならdefine_methodはprivateなので、このように使用しなければなりません.
if today =~ /Saturday|Sunday/
  Object.class_eval { define_method(:activity)  { puts "Playing!" } }
else
  Object.class_eval { define_method(:activity)  { puts "Working!" } }
end

activity

私たちもdefine_methodは直接クラスに挿入されます.
class MyClass
  define_method(:mymeth) { puts "This is my method." }
end

次のようにすることができます.
class MyClass
  def self.new_method(name, &block)
    define_method(name, &block)
  end
end

MyClass.new_method(:mymeth) { puts "This is my method." }
x = MyClass.new
x.mymeth           # Prints "This is my method."

インスタンス・レベルで使用する場合は、次のようにします.
class MyClass
  def new_method(name, &block)
    self.class.send(:define_method,name, &block)
  end
end

x = MyClass.new
x.new_method(:mymeth) { puts "This is my method." }
x.mymeth           # Prints "This is my method."

ここでsendを用いるのは、ruby 1ではsendもプライベートメソッドに用いることができるためである.9ではsendはプライベートメソッドには使用できない.
define_が見えますmethodメソッドには、このblockを定義するコンテキストを保持できることを意味するblockがパラメータとして追加されます.
class MyClass
  def self.new_method(name, &block)
    define_method(name, &block)
  end
end

a,b = 3,79

MyClass.new_method(:compute) { a*b }
x = MyClass.new
puts x.compute           # 237

a,b = 23,24
puts x.compute           # 552

クラス変数の例を次に示します.
class SomeClass
  @@var = 999

  define_method(:peek) { @@var }
end

x = SomeClass.new
p x.peek             # 999

インスタンス変数を読み込むとします.
class SomeClass
  @var = 999

  define_method(:peek) { @var }
end

x = SomeClass.new
p x.peek             # prints nil

なんとnilが印刷されています.どうしてですか.しかし、修正すると正しい値を出すことができます.
class SomeClass
  @var = 999
  x = @var

  define_method(:peek) { x }
end

x = SomeClass.new
p x.peek      # 999

なぜなら、新しいメソッドのコンテキストは、クラスではなくオブジェクトインスタンスのコンテキストです.したがって、ここでのこのクラスのインスタンス変数は、オブジェクトのインスタンス変数によって
上書きされます.すなわち、ここで印刷されるのは、インスタンス変数@varです.
6 const_の使用missing
const_missingメソッドとmethod_missingは似ています.知らない定数を手に入れたい場合は、この方法が呼び出されます.
class Module
  def const_missing(x)
    "from Module"
  end
end

class X
end

p X::BAR        # "from Module"
p BAR           # "from Module"
p Array::BAR    # "from Module"

彼をクラスの方法として使用するには、次のようにします.
class Alpha
  def self.const_missing(sym)
    "Alpha has no #{sym}"
  end
end

class Beta
  def self.const_missing(sym)
    "Beta has no #{sym}"
  end
end

class A < Alpha
end

class B < Beta
end

p Alpha::FOO       # "Alpha has no FOO"
p Beta::FOO        # "Beta has no FOO"
p A::FOO           # "Alpha has no FOO"
p B::FOO           # "Beta has no FOO"

7定義の削除
例えば、私たちはそれを徹底的に削除したいと思っています.その時、undef(defとは正反対)を使うことができます.ローカル変数定数...クラスをundefできないことに注意してください.
def asbestos
  puts "Now fireproof"
end

tax = 0.08

PI = 3

asbestos
puts "PI=#{PI}, tax=#{tax}"

undef asbestos
undef tax
undef PI

#                  

ここではインスタンス変数をundefすることはできません.
そしてremove_methodとundef_methodメソッド、彼らの違いはとても細かいです:remove_methodは現在のメソッド定義をremoveします.undef_methodメソッドは、スーパークラスのメソッドなど、すべてのメソッド定義をremoveします.
class Parent

  def alpha
    puts "parent alpha"
  end

  def beta
    puts "parent beta"
  end
end

class Child < Parent

  def alpha
    puts "child alpha"
  end

  def beta
    puts "child beta"
  end

  remove_method :alpha   # Remove "this" alpha
  undef_method :beta     # Remove every beta

end


x = Child.new

x.alpha          # parent alpha
x.beta           # Error!

remove_constはremoveを定数にします.
module Math

  remove_const :PI

end

# No PI anymore!

私たちは今removeを利用することができます.constは、クラスの定義を削除します(クラスの識別子は定数であるため).
class BriefCandle
  def test
    p "aaa"
  end
end

out_out = BriefCandle.new

class Object
  remove_const :BriefCandle
end
out=BriefCandle.new
out_out.test

remove_に注意constとremove_methodはすべて私有の方法です.