ブロック構文を利用することで、 Rails や RSpec などでおなじみの、次のようなクラスの設定用メソッドなどを作成することができます。
Hoge.configure do |config|
config.something = "configured!"
end
この記事では、こういうメソッドをどうやって実装すればよいかを2分で説明したいと思います。(割と単純なので2分くらいしかもちません)
基本
基本は単純です。
module Hoge
class << self
attr_accessor :something
def configure
yield self
end
end
end
Hoge.configure do |config|
config.something = "configured!"
end
puts Hoge.something
まず、クラス変数と something
とクラスメソッド configure
を用意します。
class << self
はそういうイディオムだと思ってください。
(詳細は長くなるので Rubyist Magazine - Ruby 初級者のための class << self の話 (または特異クラスとメタクラス) などを閲覧いただけるとありがたいです)
で、自分自身を引数にとって yield
を呼んでやればおしまいです。
これで、 Hoge.configure
のブロックパラメータである config
には Hoge
自身が渡されます。
ね、簡単でしょ?
なお、本記事においてここから先はすべて蛇足となります。
設定クラス or 設定モジュールを用意しておく
個人的には基本的なやつで十分だと思いますが、設定だけ別のモジュールやクラスに置きたくなることがあるかと思いますが、これも割と単純です。
module Hoge
class << self
def configure
yield config
end
def config
@config ||= Config.new
end
end
class Config
attr_accessor :something
end
end
Hoge.configure do |config|
config.something = "configured!"
end
puts Hoge.config.something
と、このように、 yield
の引数に設定クラス (or モジュール) のインスタンスを渡してやるだけです。
ブロック以外の形でも設定できるようにしたい!
本当にする必要があるのでしょうか?
とりあえず Kernel.#block_given?
を使えば実現できます。この Kernel.#block_given?
は現在のメソッド呼び出しにブロックが渡されていれば真を返します。
module Hoge
class << self
attr_accessor :something
def configure k = nil, v = nil
if block_given?
yield self
elsif !k.nil?
self.send(k.to_s + "=", v)
end
end
end
end
Hoge.configure do |config|
config.something = "configure form block"
end
puts Hoge.something
Hoge.configure :something, "configure from method"
puts Hoge.something
こんな感じでいかがでしょうか?
あたりまえではありますが、柔軟にすればするほど、できることも実装も複雑になります。
設定をブロック構文によるこの形をサポートしつつ、設定ファイルもサポートしたうえで、コマンドライン引数からもできるようにしたい
これをしたのが RSpec です。
ですが、 RSpec.configure
に限って言えば実装はとても単純です。
def self.configure
yield configuration if block_given?
end
抜粋したソース のように、ブロックが渡されていれば yield
を、そうでなければ単純に無視する形となっています。