Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

メタプログラミングRuby | 番外編 | Ghostableでゴーストメソッドを少しだけ楽に作成

概要

Ghostableでゴーストメソッドを少しだけ楽に作成

内容

ゴーストメソッドの定義を支援するモジュール=Ghostableを作成します。

今回想定するのはあるパターンの文字列を持つメソッド名のみゴーストメソッドとして扱います。

通常のゴーストメソッド

ソースコード
# encoding: utf-8

module Lawable
  def method_missing(method_name, *args, &block)
    lawnized_call method_name.to_s if method_name.to_s.include?("呼ぶ")
  end

  def lawnized_call(name)
    puts "create new method = #{name}"

    singleton_class.class_eval do
      define_method name do
      name.match /(.*?)[の|を|・].*/ 
        puts "#{$1}屋!!"
      end
    end
    method(name).call
  end
end

class Person
  include Lawable
end

lawnized_person = Person.new
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.ナミを呼ぶ
lawnized_person.鼻のウソップを呼ぶ
lawnized_person.ニコ・ロビンを呼ぶ
lawnized_person.黒足のサンジを呼ぶ
出力

※初回メソッド生成後は、作成したメソッドを利用するので
2回呼び出した場合はメソッド作成のログが出力されていない。

create new method = 麦わらのルフィを呼ぶ
麦わら屋!!
麦わら屋!!
create new method = ナミを呼ぶ
ナミ屋!!
create new method = 鼻のウソップを呼ぶ
鼻屋!!
create new method = ニコ・ロビンを呼ぶ
ニコ屋!!
create new method = 黒足のサンジを呼ぶ
黒足屋!!

Ghostableを利用したゴーストメソッド

ソースコード
# encoding: utf-8
require 'active_support'

module Ghostable
  extend ActiveSupport::Concern

  module ClassMethods
    def ghost_method(pattern, &proc)
      define_method :method_missing_exit? do |method_name|
        method_name.to_s =~ /#{pattern}/
      end
      define_method :method_missing_proc do |method_name|
        proc.call method_name
      end
    end
  end

  def method_missing(method_name, *args, &block)
    return unless method_missing_exit?(method_name)
    method_missing_proc(method_name, *args, &block)
  end
end

module Lawable
  include Ghostable
  ghost_method "呼ぶ$" do |method_name|
    puts "create new method = #{method_name}"
    define_method method_name do
      method_name.match /(.*?)[の|を|・].*/ 
      puts "#{$1}屋!!"
    end.call
  end
end

class Person
  include Lawable
end

lawnized_person = Person.new
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.ナミを呼ぶ
lawnized_person.鼻のウソップを呼ぶ
lawnized_person.ニコ・ロビンを呼ぶ
lawnized_person.黒足のサンジを呼ぶ
出力

同上

Ghostableを利用して別の処理も作成してみる

ソースコード
# encoding: utf-8
module Narutable
  include Ghostable
  ghost_method "呼ぶ$" do |method_name|
    puts "create new method = #{method_name}"
    define_method method_name do
      puts "#{method_name.to_s.gsub(/を呼ぶ/, '')}!だってばよ!"
    end.call
  end
end

class Person
  include Narutable
end

lawnized_person = Person.new
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.麦わらのルフィを呼ぶ
lawnized_person.ナミを呼ぶ
lawnized_person.鼻のウソップを呼ぶ
lawnized_person.ニコ・ロビンを呼ぶ
lawnized_person.黒足のサンジを呼ぶ
出力
create new method = 麦わらのルフィを呼ぶ
麦わらのルフィ!だってばよ!
麦わらのルフィ!だってばよ!
create new method = ナミを呼ぶ
ナミ!だってばよ!
create new method = 鼻のウソップを呼ぶ
鼻のウソップ!だってばよ!
create new method = ニコ・ロビンを呼ぶ
ニコ・ロビン!だってばよ!
create new method = 黒足のサンジを呼ぶ
黒足のサンジ!だってばよ!

2014/01/13 追記

自作のユーティリティgem 'tbpgr_utils'にGhostableモジュールを追加して公開しました。
この記事の例は、引数なし限定だったりと実用的ではありませんでしたが
リストで任意の数の引数を受け取り、ブロックの指定も可能にしました。
また、ゴーストメソッドの定義自体も複数同時に利用できるようにしました。

gem i tbpgr_utilsをして、require 'ghostable'して、GitHubの利用例のコードをコピペするだけで
お試し可能です。

公開記事はこちら
http://d.hatena.ne.jp/tbpg/20140112/1389538666
GitHubはこちら
https://github.com/tbpgr/tbpgr_utils
Rubygemsはこちら
http://rubygems.org/gems/tbpgr_utils