メタプログラミングRuby 第三章 method_missing

前回、動的メソッドを説明しました。今回は method_missingをまとめていきます。

mehod_missing

メソッド呼び出しの際に見つからなかった際に呼び出されるメソッドです。 以下にコード例を書いておきます。

class TestMethod
end

obj = TestMethod.new
obj.ghost(2) # undefined method `ghost' for #<TestMethod:0x007fab87884cc8> (NoMethodError)

method_objectは BasicObject(Objectクラスのメソッド kernelメソッドを持たない) で定義されているのでほぼ全てのオブジェクトに対応しています。

使い方

“ドキュメント”を確認すると、 ↓のように書くようです。

def method_missing(method_name [, *args [, &block]])

重要な箇所は以下の点です。 - 第一引数は method名 (method_name) - 第二引数は 引数 or block (*args blocks)

コード例を以下に記載します。

class TestMethod
  def method_missing(method,input)
    puts method
    puts input
  end
end

obj = TestMethod.new
obj.ghost(2) # ghost  2

method_missingメソッドは メソッド名を引数に持つのでメソッドに応じた処理を 記述することも可能です。

レシーバに対応するメソッドがなく、method_missingで対応した処理を ゴーストメソッド と呼ぶ。

ひとまず、三章ブログはここで一段落としておきます。

動的プロキシ等、残った重要トピックは気が向いた時に追記していきます。

次回は 第四章ブロック やります!

メタプログラミングRuby 第三章メソッド 動的メソッド

動的メソッド

前回は 「動的ディスパッチ」をメインに説明しました。

今回は本丸である動的メソッドの説明を行います。

動的メソッド定義

動的メソッドとは動的にclassとモジュールにメソッドを追加出来ます。

使い方は簡単で「define_method メソッド名」を定義してブロックの中身に 処理コードをつければOKです。

class Hello
    define_method "hello" do
        puts "hello"
    end
end

obj = Hello.new
obj.hello # "hello"

上のコード見ても何言ってるのって感じですよね。 define_methodが効力を発揮するのは同じような処理をまとめるのに 適しています。

例として、たいやきの価格を求めるコードを書いてみます。

メソッドでかく

class Taiyaki
  def anko_price
    puts 120
  end
  def cheese_price
    puts 200
  end
  def cream_price
    puts 220
  end
end

Taiyaki.new.anko_price

今回は例として「anko・cheese・cream」の3つの味の価格を求めるコードです。 味が何十種類もあったらメソッドを作るの面倒ですよね。

動的メソッド

class Taiyaki_dynamic
  def initialize
    hash = {"anko" => 120,"cheese" => 200,"cream" => 220} #①味と価格対応ハッシュ
    hash.each do |key,val|
      Taiyaki_dyanamic.price(key,val) #②メソッド作成
    end
  end

  def self.price(name,price) #クラス・メソッド
    define_method "#{name}_price" do
      puts price
    end
  end
end

Taiyaki_dynamic.new.anko_price #120
Taiyaki_dynamic.new.cheese_price #200

今回はmethod作成を self.priceというクラスメソッドにまとめています。 そして initializeメソッド(②)で、クラスをnewした際各味の価格を呼び出すメソッドを作成します。 たいやきの味が増えた場合はhash(①)を増やすだけで良いのでメソッド追加より見通しがよくなります。

メタプログラミングRuby第三章 動的ディスパッチ

重複したプログラムをなくすには?

この際に解決する方法は 動的メソッド と  ゴーストメソッド があります。 今回は動的メソッドに絞って説明します。

動的ディスパッチ

動的メソッドの前に、動的ディスパッチを説明します。 動的ディスパッチとはメソッドを呼び出すことが出来る事です。

何がメリットなのか、わかりづらいのでコードを手順にして 説明していきます。

# 1、2、3と出力させる

class DyanamicEx
  def dyanamic_ex1
     p "1"
  end
  def dyanamic_ex2
     p "2"
  end
  def dyanamic_ex3
     p "3"
  end
end

obj = DyanamicEx.new
obj.dyanamic_ex1 # 1
obj.dyanamic_ex2 # 2
obj.dyanamic_ex3 # 3

3.times  do |i|
  obj = DyanamicEx.new
  # 動的ディスパッチ
  obj.send("dyanamic_ex#{i+1}")  #実行dynamic_ex1 ・・・ ex3
end

動的ディスパッチを用いると、動的にメソッドを実行してくれるので が簡素化されていますね。

メタプログラミングRuby第二章(2.2~)

クラスの真相

Classクラスのインスタンスはクラス!

クラスのメソッドはClassクラスのインスタンスメソッド

Classクラスとは?

クラス(ここでいうクラス名は大文字) はClassクラスのインスタンスである。

Stringクラスを例にとると
String.class #Class ←Classクラス
 

moduleとは?

全てのクラスはモジュールである。 Classはインスタンスメソッド(new、allocate、superclass)を追加したモジュールである。 モジュールの使い分けとしては共通的なものはまとめてることが多い。 そしてクラスからmix-inして使えば良い。

定数とは?

定数とは大文字で始まるもの クラス名は定数

定数と変数の違いはスコープ範囲が違う

定数はディレクトリが違えば、一緒ではない、 定数の参照はclass外(含Module)では コロン2つをつけて指定する。 クラス外で定数を定義した場合、暗黙的に Objectクラス の定数になる

module M
  class C
    A = "クラス内"
    p A
  end
  A = "モジュール内"
  p C::A #定数
  p A #AA
end

B = "モジュール外"

p M::C::A #クラス内
p M::A   #モジュール内

p Object::B #モジュール外
p B #モジュール外
p B.class #String
p B.class.superclass #Object

定数が違うと便利なこと

クラス名が衝突してもモジュールで囲えば名前衝突を回避出来る

メタプログラミングRuby二章(2.2途中まで)

第二章(途中まで)書きます。

オープンクラスとは?

既存のクラスを再定義することです。

以下のコードを見て下さい。

これはStringクラスのメソッドを拡張した例です。

class String
  def dokkiri!
    return "!"
  end
end
p "test".class  #String
p "test".dokkiri! #!

補足 オブジェクトとClass

僕はこの文読んだ時よくわからなかったです。 なんで、基本的なクラスの話を噛み砕いて説明します 上のソース例の「"test"」は文字列オブジェクトを示しています。 オブジェクトは様々なものありますが、 代表的なものは数値(1や100)、文字列、配列などがあります。

そしてオブジェクトはクラスに属しています。 直感的に言えば性質みたいなものでしょうか。 性質(クラス)に応じて文字列だから「大文字に変換」といった 性質(クラス)に合わせた機能(メソッド)が使えます。 オブジェクトが属するクラスを探すにはclassメソッドを使います。

今回用いたオープンクラスはclass Stringに属するメソッドを追加しました。

しかし、注意があります。

以下のコードを見て下さい。

p "test".capitalize #Test

class String
  def capitalize
    return self + "!"
  end
end

p "test".capitalize # "test!"

オープンクラスは既存のメソッド(capitalize)も変更出来るので 既存のプログラムの挙動が変わり、バグを引き起こすことがあります。 (モンキーパッチという) よって、取扱には十分注意です。

インスタンス変数とは?

インスタンス変数とはクラスをインスタンス化した際に用いる変数です。(先頭に@をつけます) インスタンス変数はクラスに属しているわけではなく、クラスのオブジェクトに紐付いています。

コード例を見てみます。

class Test
  def test
    @test = "test"
  end
end

obj = Test.new
p obj.instance_variables #インスタン変数なし

obj2 = Test.new
obj.test #@testを定義
p obj.instance_variables #@test

メソッド(ここではtest2)はクラスに属しているため、 クラス(Test)をインスタンス化(オブジェクト化)しただけでは、 メソッド(test)は呼び出されない。 直感的にいうと、Testクラスのインスタンス化はあくまでもメソッド(インスタンスメソッド)の リンクを使えるようにしただけといったところでしょうか。

インスタンス化してすぐにインスタンス変数を使うには 下のコードのようにinitializeメソッドを使う必要があります。

class Test2
  def initialize
    @test2 = "test2"
  end
end

obj3 = Test2.new
p obj3.instance_variables #@test2

自分の理解が不確かなところもあります。 もし、この記事をみている方で「違うぞ」と思った方は コメントお願い致します。

メタプログラミング1章 準備

メタプログラミングRuby第一章

第一章は分量も多くないため、頭の整理のため オブジェクト志向についてまとめておきます。

クラス

教科書通りの説明ですと、クラスとは「設計図」です。 よくある例ですと、たい焼きの型ですね オブジェクト志向は再利用を目的としています。

例えば、「たい焼き」を構成するものは 「おおきさ」「素材」「あんこ」「価格」 といったものがあると思います。

ここでは「たい焼き」の情報(素材・中身・個数)を 表示するプログラムを作ります。

今回はクラスを用いて表現してみます。

素材は共通的に用いるため、 クラス変数 を用います。 クラス変数は頭に「@@」をつけます。 代入可能でクラス内での共通的な定数です。

実行方法はclass.newでオブジェクト化して、メソッドを使用します。 initializeはオブジェクトを作成する際に実行した際、自動で呼び出す処理です。intializeは引数を指定することが出来ます。

オブジェクト化した際のメソッド「order」は インスタンスメソッドと呼ばれます。 インスタンスメソッドはクラスから作成されたオブジェクトしか 呼び出すことは出来ません。

class Taiyaki
  # (素材・中身・個数)
@@material = "komugi" #素材は決まっている。

  def  initialize(order=1,content="azuki") #初期設定
    @order_num = order
    @order_content = content
  end

  def order #インスタンスメソッド
    puts "素材は#{@@material}です。個数は\n#{@order_num}\n中身は#{@order_content}"
  end

end

puts Taiyaki.new.order
puts Taiyaki.new(2,"チーズ").order

メタプログラミングRubyを読む

はじめに

これからメタプログラミングRubyを読んでわかったことをまとめていきます。

編集方針(随時更新)

  • 基礎的なこともまとめる。 オライリー特有の「わかっているだろ?」的記述を出来る限り自分の 中で噛み砕いて説明