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が使えない(未定義でエラーになる)。