0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

メタプログラミングruby DSL編のメモ

0
Last updated at Posted at 2015-05-08

DSL の完成品の前段階で、そもそもの DSL 文を ruby が拾えるようになる瞬間

# オブジェクトの能力値の判定する DSL文 を作る。
# 最初は判定オブジェクトの存在さえも気にせずに書き下してよい。

def dsl_sample()
  # watchman も setup も作っていなくても、あとから環境を与えられるのが ruby。
  watchman "you are tough" do
    @strength > 100
  end
  watchman "you are feeble" do
    @strength < 50
  end
  watchman "you are rapid" do
    @agility > 100
  end
  watchman "you are slow" do
    @agility < 50
  end
  watchman "upper limit" do
    @level > 20
  end
  watchman "lower limit" do
    @level < 5
  end
  setup do
    @strength = 80
    @agility = 120
  end
  setup do
    @level = 30
  end
end

# 適当に作った、watchman, setup を動かす。すべてのブロックは、環境(ローカル変数、インスタンス変数、self)が与えられて動くので、それを後から自在に与えればよい。

# 無駄な名前空間汚染を防ぐ。
lambda {
  # ファイルの読み込み後、全 DSL を格納する配列とハッシュを用意する。あとで適切な実行行うため。
  setups = []
  events = {}

  # def キーワードを使わないことが、スコープゲートの回避
  Kernel.module_eval do
    # setup と watchman を拾うために、Kernel にメソッドを作る。setups と events に格納したいのでスコープゲートを回避する define_method を使う。
    define_method :setup do |&block|
      setups << block
    end
    define_method :watchman do |event_name, &block|
      events[event_name] = block
    end
  end
}.call

dsl_sample() # 動く!DSLのブロックは実行されていない。

任意の each 文を実装する場面

event毎に調査していき、「環境」を新しく作り setup をかまして、調査したい場合、以下のようなメインコードになったとする。Cleanroom 技術を使っている。


each_event do |event_name, event|
  environment = Cleanroom.new
  each_setup do |setup|
    environment.instance_eval(&setup)
  end
  puts event_name if environment.instance_eval(&event)
end

each_event と each_setup を動かすために Kernel の define_method を追加する。


lambda {
  setups = []
  events = {}

  Kernel.module_eval do
    define_method :setup do |&block|
      setups << block
    end
    define_method :watchman do |event_name, &block|
      events[event_name] = block
    end
    # スコープに setup が渡るようにする。
    # block.call setup を作れば、第1引数に setup が渡ってくる。
    define_method :each_setup do |&block|
      setups.each do |setup|
        block.call setup
      end
    end
    # 第1引数に イベント名、第2引数にイベントProc が渡ってくるようにする。
    define_method :each_event do |&block|
      # すでに Proc が渡ってくるので、& は不必要
      events.each_pair do |entry_name, event|
        block.call entry_name, event
      end
    end
  end

  class Cleanroom
  end
}.call

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?