Posted at

Rake, Itamae, Rspec, Thor の DSL 評価の方式

自分で 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

クラスを継承させるスタイル。