自分で DSL 処理を描きたくなった時に、どうやるのが良いのかあまり自明じゃなかったので、DSL っぽいことができる有名な ruby のフレームワークに対して、それぞれがどのように DSL を評価しているのかを調べてみた際の、メモ。
Rake
rake/lib/rake/dsl_definition.rb
# self はトップレベルオブジェクト、つまり main
self.extend Rake::DSL
main
オブジェクトに対する特異メソッドとして、もろもろの DSL メソッドを追加する方式。
Itamae
itamae/lib/itamae/recipe.rb
def load(vars = {})
context = EvalContext.new(self, vars)
context.instance_eval(File.read(path), path, 1)
end
File を read しながら、評価用オブジェクト上で instance_eval
する方式。
Rspec
rspec-core/lib/rspec/core/dsl.rb
# @private
def self.expose_example_group_alias_globally(method_name)
change_global_dsl do
remove_method(method_name) if method_defined?(method_name)
define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) }
end
end
# @private
def self.change_global_dsl(&changes)
(class << top_level; self; end).class_exec(&changes)
Module.class_exec(&changes)
end
end
end
end
# Capture main without an eval.
::RSpec::Core::DSL.top_level = self
main
オブジェクトの特異クラスに対して define_method
などを行っている
Thor
class CLI < Thor
desc 'foo', 'foooo'
def foo
puts :foo
end
end
クラスを継承させるスタイル。