おおいしつかさ


旅行とバイクとドライブと料理と宇宙が好き。
Ubie Discoveryのプログラマ。
Share:  このエントリーをはてなブックマークに追加

Rubyの名前空間に苦しむ。そしてちょっと勉強。

 作ったあるメソッドを使うとき、 attr_accessor みたいな書き方をしたかったのだけど、Rubyの名前空間にちょっとハマってしまった。復習しておこうっと。

 それはともかく、attr_accessor はどうやって定義されているのかと思ってちょっと調べてみると、 Moduleクラスのプライベートインスタンスメソッドとして定義されていた。ClassクラスはModuleクラスのサブクラスだからか。なので、同じようにやってみる。
test.rb

class Module  
  private  

  def set_special_url(args)  
    define_method :special_url do  
      args  
    end  
  end  
end  

として、こいつをrequireしてやると

#!/usr/bin/ruby  
require 'test'  

class Lesson  
  set_special_url :controller => 'test', :action => 'login'  

  def index  
    puts special_url[:controller]  
    puts special_url[:action]  
  end  
end  

Lesson.new.index  

実行結果は、

test  
login  

となった。なるほど。

 次にRailsでやってみる。
 ジャンプ先のURLを一度設定しておけば、どこでもそれが使えるという機能と、ステートを保持するセッションを持ち、それをインスタンス変数としてアクセスできる機能を持つpluginを作ってみる。

module Spoon  
  private  

  def say  
    @word ||= session[:word] || "I'm thinking nothing"  
  end  

  def mind=(word)  
    session[:word] = word  
    @word = word  
  end  

  def thinking(word)  
    self.mind = word  
  end  

  class ::Module  
    private  

    def set_jump_url(args)  
      define_method :jump_url do  
        args  
      end  
    end  
  end  
end  

こんな感じ。
 Moduleクラスをオーバライドしているのは前述のとおり。::をModuleの頭につけてあげないと、Spoon::Moduleクラスの定義になってしまうので注意。
 sayメソッドとmindメソッドは、それぞれ@wordへのアクセサになっている。sessionを使っているのは、アクセスされるたびにインスタンスが生成されるので、@workがそのたびに異なるものになってしまうため。だったらセッションだけでもいい気もするけど、こっちのほうがシンプルだと思う。
 thinkingメソッドは、mindメソッドを確実に呼ぶために定義した。selfレシーバをつけてあげないと、mindがメソッドなのかローカル変数なのか、区別がつきにくい(というかコントローラのアクションの中ではローカル変数と判断される)のだ。このあたりは、acts_as_authenticatedをもろに参考にしました。

で、ApplicationControllerでは

class ApplicationController < ActionController::Base  
  include Spoon  
  set_jump_url :controller => 'test', :action => 'index'  
end  

 こうしてやる。これで、どのコントローラでも、redirect_to jump_url みたいな感じで、設定したジャンプ先のURLが使えるようになる。
 それからふたつのコントローラを

class TestController < ApplicationController  
  def index  
    render :text => say  
  end  

  def set_mind  
    thinking "I'm hungry..."  
    redirect_to jump_url  
  end  
end  
class KaeruController < ApplicationController  
  def index  
    redirect_to jump_url  
  end  
end  

 こんな感じで定義する。
 もしjump_urlをtestコントローラだけで使いたいときは、set_jump_urlをtestコントローラだけで使えばいい。そうすれば、kaeruコントローラではjump_urlが使えない(未定義でエラーになる)。