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

Tsukasa OISHI

 作ったあるメソッドを使うとき、 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が使えない(未定義でエラーになる)。