プラグイン作成中に覚えたこと

クラスメソッドの定義方法いろいろ

class Test
  def Test.foo
    'クラスメソッドfoo'
  end
end

# または...

class Test
  def self.foo
    'クラスメソッドfoo'
  end
end

# または...

class Test
  class << self
    def foo
      'クラスメソッドfoo'
    end
  end
end

# または...

class Test
end

class << Test
  def foo
    'クラスメソッドfoo'
  end
end

# または...

module Bar
  def foo
    'クラスメソッドfoo'
  end
end

class Test
  extend(Bar)
end
  • 書き方はいろいろあるが、すべて、同じクラスメソッドfooを追加していることになる。実行すると以下の結果になる。
Test.foo
=> "クラスメソッドfoo"

includeとextend

  • includeはモジュールをインスタンスメソッドとして追加する。
  • extendはモジュールをクラスメソッドとして追加する。*1
module Bar
  def foo
    'メソッドfoo'
  end
end

class Test
  extend Bar
end
# extendした場合、クラスメソッドとして追加される。

Test.foo
=> "メソッドfoo"



class Test2
  include Bar
end
# includeした場合...

Test2.foo
NoMethodError: undefined method 'foo' for Test2:Class
# クラスメソッドとしては呼び出すことができない。

Test2.new.foo
=> "メソッドfoo"
# インスタンスメソッドとして呼び出し可能。
  • includeでクラスメソッドも追加してみる。
# includeするということは、Test2クラスのクラスメソッドappend_featuresを実行することである。
# よって、クラスメソッドappend_featuresをオーバーライドして、以下のようなモジュールを作成しておけば、includeした時、同時にクラスメソッドの追加も可能になる。

module Bar
  def self.append_features(base)
    super
    base.extend(ClassMethods)
  end

  module ClassMethods
    def foo
      'クラスメソッドfoo'
    end
  end
end

class Test
  include Bar
end

Test.foo
=> "クラスメソッドfoo"

# ちなみに、extendを実行するということは、インスタンスメソッドextend_objectを実行することである。

define_method

  • メソッドの中で動的にメソッドを定義する。(メソッドを定義するメソッドを作る。例えば、auto_complete_for :bank, :nameのauto_complete_forは、auto_complete_for_bank_nameというメソッドを追加するメソッドと言える。)
module Bar
  def self.append_features(base)
    super
    base.extend(ClassMethods)
  end

  module ClassMethods
    def make_foo
      define_method('foo') do
        'クラスメソッドfoo'
      end
    end
  end
end

class Test
  include Bar
end

Test.new.foo
NoMethodError: undefined method 'foo' for #<Test2:0x259c1c>
# includeしただけではfooメソッドは未定義の状態。

Test.make_foo
Test.new.foo
=> "クラスメソッドfoo"
# このようにmake_fooメソッドを呼び出すことで、fooメソッドが定義される。
# よって、make_fooメソッドは、fooメソッドを定義するメソッドと言える。

procとlambda

  • コードブロックをオブジェクトとして取り扱う場合に利用。
code = proc {puts 'オブジェクト化されたコードブロック'}
code.call
オブジェクト化されたコードブロック

# 以下のように書くこともできる。(同じ動作になる。)

code2 = Proc.new {puts 'オブジェクト化されたコードブロック2'}
code3 = lambda {puts 'オブジェクト化されたコードブロック3'}

code2.call
オブジェクト化されたコードブロック2

code3.call
オブジェクト化されたコードブロック3
  • コードブロックを受け取るメソッドの定義方法いろいろ
# コードブロックを指定しないでProcインスタンスを作ると、fooメソッドを呼び出したときのコードブロックを受け取ることになる。
def foo
  a = Proc.new
  a.call('コードブロックを受け取って実行する')
end

foo {|s| puts s}
コードブロックを受け取って実行する

# 以下のように書くこともできる。(同じ動作になる。)

def foo2
  proc.call('コードブロックを受け取って実行する2')
end

def foo3
  lambda.call('コードブロックを受け取って実行する3')
end

# 引数最後の変数の前に「&」を付けることで、その変数にはコードブロックが代入される。
def foo4(&p)
  p.call('コードブロックを受け取って実行する4')
end

def foo5
  yield('コードブロックを受け取って実行する5')
end

foo2 {|s| puts s}
コードブロックを受け取って実行する2

foo3 {|s| puts s}
コードブロックを受け取って実行する3

foo4 {|s| puts s}
コードブロックを受け取って実行する4

foo5 {|s| puts s}
コードブロックを受け取って実行する5

*1:Rubyではクラスもオブジェクト。クラスはClassのインスタンス。extendはインスタンス(オブジェクト)にモジュールのメソッドを追加する。(特異メソッドとして)