soraなりの日々 - fc2 -

こころにひっかかったもの

[ruby] method_missing について理解する

今日は rbc[ruby business commons] の勉強会のご様子。

[[Riminder]福岡でのRails勉強会は明日です]
http://rubybizcommons.blogspot.com/2009/03/riminderrails.html

まー、その。。。
出勤日だったので行けなかったんだけど、
ust してたから合間合間で覗き見しておりました(^^;
(やっぱ ust 中継良いですね!)

で、みなさんが勉強してる中、一人映像見て
モンモンとするのイヤだったんで勉強してみた!


rubymethod_missing について

このエントリーをはてなブックマークに追加

method_missing とは:
 呼び出したメソッドがなかった場合、この method_missing メソッドが
 呼び出される
 呼び出しに失敗したメソッドの名前が第一引数に入り、引数が第二引数に入る

railsplugin とか見てると良く使ってるヤツありますね。
アレです。アレ。

今日やってた rbc の勉強会ではショッピングカートシステムを作ってるよう
なので、無理矢理カートクラス作ってみた!(^^;
(マジで無理矢理。。)

やりたかったのは、カートの中のアイテムに入る id でメソッドを定義したかった。
けど、できなかったんだけどね。。。

例:
 cart.1
 cart.2

できんかったんやけどね(くどい!)。
結局、get_XX とか del_XX とか使えないものになってしまった・・・orz

suxk_cart.rb:
# 商品一個一個
class CartItem
  attr_reader :product_id, :quantity

  def initialize(product_id, quantity=1)
    @product_id = product_id  # 商品の id
    @quantity   = quantity  # 商品数
  end

  # 商品数をインクリメント
  def increase(num=1)
    @quantity += num
  end

  # 商品数をデクリメント
  def decrease(num=1)
    @quantity -= num
    @quantity  = 0 if @quantity < 0
    @quantity
  end
end

# 使えないカート
class SuxkCart
  attr_reader :items

  def initialize
    @items = []
  end

  # 全合計
  def total
    sum = @items.inject(0) do |x, item|
            x + item.quantity
          end unless @items.blank?
  end

  def clear
    @items = []
  end

  # ここが本題
  def method_missing(method_name, *args)
    # "get_XX" の実装
    if method_name.to_s =~ /\Aget_(\d+)\z/
      class_name_i = method_name.to_s.split("_")[1].to_i
      self.class.module_eval <<-EOF
                                 def #{method_name}(*args)
                                   item = find(#{class_name_i})
                                   if item == nil
                                     item = CartItem.new(#{class_name_i},*args)
                                     @items << item
                                   end
                                   item
                                 end
                                EOF
    elsif method_name.to_s =~ /\Adel_(\d+)\z/
    # "del_XX" の実装
      class_name_i = method_name.to_s.split("_")[1].to_i
      self.class.module_eval <<-EOF
                                 def #{method_name}(*args)
                                   delete(#{class_name_i})
                                   undef #{method_name}
                                   undef get_#{class_name_i}  # ※
                                 end
                                EOF
    else
      raise NameError  # ※
    end
    send(method_name, *args)  # 定義したメソッドの実行
  rescue NameError => e  # ※
    #
  end

private
  # @items から商品検索
  def find(product_id)
    return nil if @items.size <= 0
    finded = nil
    @items.each do |item|
      next if item.product_id != product_id
      finded = item
      break
    end
    finded
  end

  # @items から商品を削除
  def delete(product_id)
    newitems = []
    @items.each do |item|
      next if item.product_id == product_id
      newitems << item
    end unless @items.size <= 0
    @items = newitems
  end

end

cart = SuxkCart.new  # 使えないカートを生成
puts cart.get_1.increase(10)  # 10 作るついでに 10 個注文
puts cart.get_1.decrease(10) # 10 個はさすがに注文やめる
puts cart.del_1  # やっぱりやめた
cart.get_2  # 2 の商品にする
cart.get_1  # ※
cart.del_3
  # ※
cart.items.each do |item|
  puts "product_id = #{item.product_id} quantity = #{item.quantity}"
end

実行結果:
$ ruby suxk_cart.rb
11
1
nil
product_id = 2 quantity = 1
product_id = 1 quantity = 1

もっと railsplugin にあるように便利な使い方があるはずだ!!
けど、勉強になった。なった(^^;


※ 2009/03/29 AM04:37 update
 まるっきりバグと言う訳ではないが、
 当初の予定の動きでは無かったので追加
 (それをバグというか。。。)
このエントリーをはてなブックマークに追加

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://sora2hs.blog70.fc2.com/tb.php/443-6548fe94
この記事にトラックバックする(FC2ブログユーザー)

やって良かったなぁ、と思えること

3/28土曜日の福岡行きは ・3/25木曜日のGLTが終わった後に飛行機の予約を...

  • 2009/03/31(火) 15:18:00 |
  • さむしんぐにゅぅ