ActiveSupport::Configurable の話
イントロ
よく設定ファイルで見るクラスを簡単に作れる module ActiveSupport::Configurable
の紹介
使い方は、rails/configurable.rb at master · rails/rails のコメント文を読んだほうが早い。
よくあるやつ
具体例を幾つか紹介。
引数が違ったり、ブロック引数有り/無しだったり、メソッド名が違う等の差はあれど、どれも似ている
vagrant
Vagrantfile を拝借
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "centos7" config.vm.network "private_network", ip: "192.168.33.10" .... end
rails
config/environments/development.rb
を拝借
Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false ... end
tapp
esminc/tapp より
Tapp.configure do |config| config.default_printer = :awesome_print config.report_caller = true end
rails_config
RailsConfig.setup do |config| config.const_name = 'AppSettings' config.use_env = true end
他にもあったけど、紙面の関係上省略。
使ってみる
インスタンスを使う
require 'active_support/configurable' class MyConfig include ActiveSupport::Configurable config_accessor :id, :name end c = MyConfig.new c.id = 'aaa' c.id #=> 'aaa' c.name = 'bbb' c.name #=> 'bbb' c.hoge #=> NoMethodError: undefined method `hoge'
config_accessor
でメソッド名を渡すと、(内部でゴニョゴニョしてるが)アクセサが作られるというだけ。
また、config
というインスタンスメソッドが定義されて、それを呼ぶと登録した設定が全て取れる。
c.config
#=> {:id=>"aaa", :name=>"bbb"}
クラスを使う
ActiveSupport::Configurable
を include したクラスでは、ブロックを渡すお馴染みのインターフェイスが提供される。
class MyConfig include ActiveSupport::Configurable config_accessor :id, :name end # みたことあるやつや! MyConfig.configure do |config| config.id = 'aaa' config.name = 'bbb' end MyConfig.id #=> 'aaa' MyConfig.name #=> "bbb" MyConfig.config #=> {:id=>"aaa", :name=>"bbb"}
とはいえ、これだとクラス自体が設定を持っていて、何か気持ち悪い。
応用
例えば以下のようにクラスを書いてみると、しっくりくる。
class MyApp class Config include ActiveSupport::Configurable config_accessor :id, :name end def self.configure(&block) yield config end def self.config @config ||= Config.new end end MyApp.configure do |config| config.id = 'aaa' config.name = 'bbb' end MyApp.config #=> <MyApp::Config:0x007f9b7b9ab870 @_config={:id=>"aaa", :name=>"bbb"}> config = MyApp.config config.id #=> 'aaa' config.name #=> 'bbb'
- 設定を保持するクラス(
MyApp::Config
)にActiveSupport::Configurable
を include させる- 更に
config_accessor
でアクセサを宣言する
- 更に
とするだけで
MyApp.config
とすれば設定インスタンスを参照可能- MyApp を参照できれば、誰でも
config
を知ることができる
- MyApp を参照できれば、誰でも
MyApp::Config
はアクセサを定義しているだけ
みたいな事ができ、馴染みのあるコードになってきた。
けどこれだけだと、config_acessor
の代わりに attr_accessor
でいいんじゃね?ってなる。
オプションを渡す
ところがどっこい config_accessor
にはオプションを渡すことができる。
instance_reader
と instance_writer
だ。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name, instance_reader: false end c = MyConfig.new c.id = 'aaa' c.id #=> "aaa" c.name = 'bbb' c.name #=> NoMethodError: undefined method `name'
instance_reader: false
を与えると、MyConfig#name
という reader のメソッドが提供されなくなるので、NoMethodError
になる。
instance_writer: false
はというと、その逆になる。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name, instance_writer: false end c = MyConfig.new c.id = 'aaa' c.id #=> "aaa" c.name = 'bbb' #=> NoMethodError: undefined method `name=' c.name #=> nil
今度は MyConfig#name=
というメソッドだけ提供されていないので、writer を呼ぼうとすると NoMethodError
になる。
ブロックを渡す
default 値を定義することもできて、ブロックを渡せば可能。
class MyConfig include ActiveSupport::Configurable config_accessor :id config_accessor :name do 'bbb' end end c = MyConfig.new c.name #=> "bbb"
まとめ
rails/configurable.rb at master · rails/rails
デフォルト値などを考えだすまでは attr_accessor
でいいんじゃないか...? って思いました。
けど使い方次第でしょうねー