Rubyメタプログラミング-Week-4


沈小黒の菜園へようこそ~
クラス定義
「Rubyオブジェクトモデルの最も暗いコーナーに入る」...Javaとは異なり、Rubyでは定義クラスが実際にコードを実行しているという考えが1、クラスを修正できるクラスマクロ2、他の方法の前後に追加コードをカプセル化できるエイリアスを生み出した.もちろん、クラスは強化されたモジュールにすぎないので、これらの知識はモジュールにも適用できます.
現在のクラス
メソッドの属する判断は定義メソッドの所在selfによって判断されるので,親定義のメソッドは,サブクラスでm 2定義文が実行されてもm 2は親クラスに属する.
class C
  def m1
    def m2

    end
  end
end

p C.instance_methods(false) # => [:m1]

class D<C

end

D.new.m1

p C.instance_methods(false) # => [:m1, :m2]

p D.instance_methods(false) # => []

class_eval V.S classキーワード
クラスキーを使用してクラスを変更するにはクラス名を指定する必要がありますが、クラス名をパラメータとして動的に変更するには?このときModule#class_を使うことができます.eval(別名module_eval)メソッド.
def add_method_to(a_class)
  a_class.class_eval do
    def m
      'Hello!'
    end
  end
end

add_method_to String
p "abc".m           # => "Hello!"

またclassは役割ドメインゲートであり,クラス外の変数はクラス内では見えない.そしてclass_evalは、結局はメソッド呼び出しにすぎないため、フラットな役割ドメインを使用します.
classを見つけることができますevalとinstance_evalは似てるclassevalにもパラメータ付きclass_がありますexec.instance_evalのポイントは、オブジェクト内の変数の現在のselfを呼び出しで表示するように変更し、class_evalのポイントはクラスの変更です.
クラスのインスタンス変数
Rubyのインスタンス変数は現在のselfに属するため、クラス内のメソッド外で定義されたインスタンス変数はクラスのインスタンスではなくクラスというオブジェクトに属する.
class A
  @var = 1
  def read; @var; end
  def write; @var = 2; end
  def self.read; @var; end
end

class B<A
  def self.read; @var; end
end

obj = A.new
p obj.read      # => nil
p B.read            # => nil
obj.write
p obj.read      # => 2
p A.read        # => 1

インスタンス変数の帰属は現在のselfに基づいて判断されるため、サブクラス、インスタンスはクラスインスタンス変数にアクセスできません.この変数は「クラス」というオブジェクトのselfに属するためです.
注意@先頭のクラス変数とは異なり、現在のクラスとサブクラスまたはインスタンスによってアクセスできます.そのため、クラス変数をトップレベルの役割ドメインで定義すると、すべてのクラスがトップレベルの役割ドメインの上書き下にあるため、アクセスできます.
@@var = 1

class A
    @@var = 2
end

@@var = 2

このような状況を避けるためには、できればクラス変数の使用を避け、クラスのインスタンス変数の使用を避けることが望ましい.
単品メソッド
Rubyは、単一オブジェクトへの追加方法をサポートします.
str = "this is a string"

def str.title?
    self.upcase == self
end

str.singleton_methods       # => [:title?]

Javaでsingletonクラスが同じオブジェクトのみを生成できるという概念とは異なり、ここでのsingletonメソッドは、このメソッドが定義されたオブジェクトに属しているということです.
実際,クラスメソッドを定義する際のメソッド名のselfを連想するが,クラスメソッドはクラスオブジェクトの単品メソッドである.
クラスマクロ
Rubyオブジェクトには属性がないので、Javaのようなgetterとsetterを書いてから属性にアクセスまたは変更する必要があります.
class MyClass
  def my_attribute=(value)
    @my_attribute = value
  end

  def my_attribue
    @my_attribute
  end
end

obj = MyClass.new
obj.my_attribute = 1
obj.my_attribute     # => 1

でもこう書くのは煩わしくてクールじゃない~だからルビーはModule#attrを提供してくれたreader、Module#attr_writerとModule#attr_accessor
class MyClass
  attr_accessor :my_attribute
end

obj = MyClass.new
obj.my_attribute = 1
obj.my_attribute     # => 1

類似attr_accessorのような方法をクラスマクロと呼び、キーワードのように見えますが、実際にはクラスで使える方法にすぎません.
APIを更新し、古いメソッドをdeprecatedと宣言し、新しいメソッドに自動的に移動する必要があると仮定すると、次のようなコードを使用できます.
class MyClass
  def self.deprecate(old_method, new_method)
    define_method(old_method) do |*args, &block|
      warn "Warinig, #{old_method} is deprecated, use #{new_method} now!"
      send(new_method,*args,&block)
    end
  end

  def old_m
    p "old"
  end

  def new_m
    p "new"
  end

  deprecate :old_m, :new_m
end


obj = MyClass.new
obj.old_m

単品類
Rubyメソッドを検索するときは,受信者のクラスに入ってから上へ検索する.たとえば、MyClassクラスインスタンスobj呼び出しメソッドは、objのクラスMyClassに入ってメソッドを検索します.しかし、単品メソッドはクラスに含まれていないに違いありません.そうしないと、すべてのオブジェクトにこのメソッドがあります.オブジェクト自体はクラスではないのでobjにも入っていません.では、この方法はどこにあるのでしょうか.
単品類とは
オブジェクトのクラスを尋ねると,Rubyは実際に見たクラスではなく,オブジェクト特有の非表示クラスを返す.このクラスはこのオブジェクトの単品クラス、またはメタクラスと呼ばれます.
obj = Object.new
singleton_class = class << obj
                    self      # => #>
end

p singleton_class

これにより、この単品クラスの参照(.classメソッドは単品クラスを隠すように包装される)を見ることができ、現在Rubyも提供している.singleton_classメソッドは、単一のクラスを返します.各オブジェクトには独自の単品クラスがあり、単品クラスにも1つのインスタンスしかない(そのインスタンスにも独自の単品クラスがある~ループ往復・・・ただしこの単品クラスの単品クラスの用途はまだ開発されていない).オブジェクトの単品メソッドは、対応する単品クラスに配置されます.
月曜日に学習した継承モデルを更新する必要があります:objが存在するクラスはobjの単品クラスで、単品クラスの親はMyClassで、MyClassクラスのオブジェクトの親はObjectです.
単品クラスと継承
次のようなクラス関係があると仮定すると,継承ツリーの出力を観察することによって彼らの構造関係を理解することができる.
class C
  def self.claz_method

  end
end

class D<C

end

obj = D.new

def obj.singleton_mtd

end


p C.singleton_class       # => #
p D.singleton_class       # => #
p D.singleton_class.superclass  # => #
p C.singleton_class.superclass  # => #
p BasicObject.singleton_class.superclass  # => Class    
   
  

instance_eval

instance_eval 。

s1 = 'abc'

s1.instance_eval do
    def swoosh!; reverse; end
end

このように うことはめったにありませんが、これでいいです.instance_evalの な は、self ドメインの を することです.
クラス
クラスに を するには、 のような を します.
class MyClass
    class << self
        attr_accessor :c
    end
end

のクラスでメソッドを すると、これらのメソッドは にクラスメソッドになります. は は の なので、クラス の に します.
クラス /オブジェクト
Moduleからメソッドをクラス/オブジェクトにインポートする は、このクラス/オブジェクトを き、includeモジュールでメソッドを できます.
module MyModule
    def my_method; 'hello'; end
end

class MyClass
    class << self
        include MyModule
    end
end

MyClass.my_method       #         

obj = Object.new
class << obj
    include MyModule
end

obj.my_method               #          
obj.singleton_methods   # => [:my_method]

クラス/オブジェクトを くだけでなく、 なObject#extendメソッドも できます.
module MyModule
    def my_method; 'hello'; end
end

obj = Object.new
obj.extend MyModule
obj.my_method       # => 'hello'

class MyClass
    extend MyModule
end

MyClass.my_method   # => 'hello'


ライブラリなど、 できないインタフェースが く されており、このインタフェースを しているすべての に しい を したい は、インタフェースを しているすべての を つけて つ つ することが になります.Rubyはこの を するための の を してくれた.
メソッド
aliasを するとRubyメソッドに を けることができます.トップレベルの ドメインに まれていない り、Module#alias_を することができます.methodメソッドは、トップレベルの ドメインではaliasキーワードしか できません.
class MyClass
    def my_method; 'my_method()'; end
    alias_method :m, :my_method
end

obj = MyClass.new
obj.my_method       # => 'my_method()'
obj.m               # => 'my_method'

def my_method2
  'hello'
end

alias :m2 :my_method2

m2                  # => 'hello'

のメソッドが を した に された 、 は されていないコードを します.
class String
    alias_method :real_length, :length

    def length
        real_length > 5 ? 'long' : 'short'
    end
end

"War and Peace".length      # => "long"
"War and Peace".real_length     # => 13

なる で する
エイリアスの り し
Aメソッドに Bを し、Aメソッドを して しく した を し、 にAでBメソッド( された のAメソッドで、 した「 されていないコードを す」 を した)を び すことができます.しかし、この にはサルのパッチの があります.すべての のこの は されましたが、 たちはいくつかの でこの を する があるかもしれません.

refinementsは、クラスの が ドメインにのみ を え、サルパッチのようにグローバルに を ぼす を する を します. のコードを て、Stringクラスにcamelizeメソッドを しましたが、Fooでしか できません.
module Camelize
  refine String do
    def camelize
      dup.gsub(/_([a-z])/) { $1.upcase }
    end
  end
end

class Foo
  using Camelize

  def camelize_string(str)
    str.camelize
  end
end

Foo.new.camelize_string('blah_blah_blah')   # => "blahBlahBlah"
'blah_blah_blah'.camelize   # => NoMethodError

Fooにrefinementが されている 、 camelizeを できるようになりましたが、Fooの では できません.
refineでsuperを すると、 の をメソッドにパッケージできます.
module StringRefinement
    refine String do
        def length
            super > 5 ? 'long' : 'short'
        end
    end
end

using StringRefinement

"War and Peace".length          # => "long"

これにより、この が な にのみ され、String#lengthの がどこでも されて しないことが されます.
Module#prepend
Module#prependの はincludeと ていますが、 まれているモジュールを チェーンの に するだけで、メソッドを すときに まれるモジュールのメソッドが に つかり、superを じてクラスの のメソッドを び すことができます.
module ExplicitString
    def length
        super > 5 ? 'long' : 'short'
    end
end

String class_eval do
    prepend ExplicitString
end

"War and Peace".length      # => 'long'

この が も で,サルパッチも の も しなくてもよい. は くの を き こし、 にはこの を ることができます. のメソッド び しはキャッシュして に することができますが、refineはメソッド を にしなければなりません.さらに いことに、どのブロックが び されたときもrefineされる があります.これは いです.
この はここまでにしましょう~~