こんにちは。ECナビでインフラエンジニアやってます岩本(@masaki925)です。

弊社では週に1度ランチタイムを利用して「メタプログラミングRuby読書会」というものを実施しています。

メタプログラミングRubyメタプログラミングRuby
著者:Paolo Perrotta
アスキー・メディアワークス(2010-08-28)
販売元:Amazon.co.jp
クチコミを見る


今回は、その中で上がった個人的に面白かった話題について簡単に紹介しようと思います。


・オブジェクト指向における多態性について


僕はRubyという言語に興味を持ってはいるのですが、正直「オブジェクト指向」というものがどういうものなのか明確に理解できていません。

そのことを告白すると、先輩エンジニアのみなさんがオブジェクト指向について説明してくれて、その中で「多態性(polymorphism)」というキーワードが上がりました。


そしてその多態性について先輩エンジニアの@unlearned さんが以下のような趣旨で、PHPのコードを書いて説明してくれました。
// 会員のグレードごとに異なるボーナスポイントの付与率を表示するコードの例
<?php
abstract Class Member {
function print_bonus() { print("your bonus is " . $this->bonus . "%.\n"); }
}

Class Gold extends Member { var $bonus = 15; }
Class Silver extends Member{ var $bonus = 10; }

// データベースからユーザのグレードを取得したつもり
//$grade = $db->get_grade(user_id);
$grade1 = "Gold";
$grade2 = "Silver";

// グレード(文字列) からオブジェクトを生成
$member1 = new $grade1;
$member2 = new $grade2;

// ボーナス付与率を表示
$member1->print_bonus(); // your bonus is 15%.
$member2->print_bonus(); // your bonus is 10%.
ということで、
多態性とは
 同じ動作を、違うオブジェクトがすることで、異なった振舞いをさせることができるようになる性質のこと
というように理解できました。
(多態性のメリットに関してはそこら中に情報があると思いますので今回は省きます。)


・文字列からのオブジェクト生成
@unlearned さんのおかげで僕の疑問は晴れたのですが、@unlearned さんは説明を終えただけでは満足せず、説明に用いた下記のようなクラスの生成の仕方はRubyではどのように実現するのか?という疑問を読書会メンバーに向けて投げかけました。
//generate Object by String in php
$obj = new "MyClass";

みんなすぐには回答に辿り着かず、ググったり手を動かしたりしてみて、どうやらObject.const_getを使うらしい。ということがわかりました。

でも読書会の時間内ではなぜそうなるのか?という回答を得ることができず、その場はそれで解散になりました。


・ActiveSupport における文字列からのオブジェクト生成

その後、@tadasy さんがRailsのActiveSupportについて調べてくれました。

ActiveSupportではconstantizeという名前でStringクラスを拡張していました。
"MyClass".constantize.new
実際にソースを見てみると、
StringクラスをInflectorというモジュールを利用して拡張していました。

※便宜上、実際のソースから少し書き換えています。

参考:/usr/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/inflector.rb
//generate Object by String in php
module Inflector
    def constantize(class_name)
      Object.const_get(class_name)  # 注目!
    end
参考:/usr/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/string/inflections.rb
//generate Object by String in php
require 'active_support/inflector' unless defined?(ActiveSupport::Inflector)

module String
def constantize
Inflector.constantize(self)
end
受け取った文字列をObject.const_get に渡してクラスへの参照を取り出すことで、クラス.new を実現していました。


また、FAQにも同様の方法が紹介されていました。
6.15 クラス名からクラスを得ることはできますか - FAQ::クラス、モジュール - Rubyリファレンスマニュアル


上記の調査と、本の内容を照らし合わせると、見事に@unlearned さんの疑問を晴らすことができました!
※ページ番号は「メタプログラミングRuby」に関するものです。
  • クラス名はただの定数である。(p.49)
  • クラス名(定数) は、クラスへの参照を持つ。(p.53)
  • クラス.new メソッドを実行するには、クラスへの参照が必要。(p.53)
  • クラス名(文字列) からクラスへの参照を得るには、Object.const_get メソッドを用いる (ActiveSupport, FAQ)

#generate Object by String in ruby!!
obj = Object.const_get("MyClass").new


・メタプログラミングって楽しい!
今回の読書会では、ふとした疑問をきっかけに自分だけでは気が付かなかった点に気付くことができ、
本の内容、Rubyという言語についてより深く理解できました。

引き続きこういった読書会などの機会は積極的に利用し、
少しずつでもメタプログラミングの魔術を自分のものにして、「ビックリすること」ができるようになりたいです!


・宣伝
読書会の発起人である@bash0C7 さんを中心として、「東京Ruby会議05」の開催を予定しています。
tork05_logo
そちらでも、メタプログラミングをテーマとしたセッションを行う予定(あくまで予定ですが。。) ですので、ご興味ある方はぜひご参加ください。

よろしくお願いします:)