DSL / Textual Polishing


Textual Polishing

一言要約

静的言語のコンパイルや動的言語の処理実行の前に行われる単純なテキスト置換

要約

仕組み

パーサ実行前に行われるテキスト置換。

3 hours ago => 3.hours.ago

ファウラー自身は動的言語で多く見てきた。
静的言語の場合はコンパイル前のビルドプロセスで実行。

内部DSLのテクニックであるが、外部DSLでも有用なケースがある。
字句解析前などで見つけづらいもの(インデントや改行)を研磨する場合など。

使いどころ

ファウラー自身は慎重派。
ちょっとしたものでは大げさすぎで、たくさん使うものではとても複雑になるということなので。
また、正規表現は間違えやすいので注意。

内部DSLのノイズキャラクタに対する代替案として、構文の色分けをサポートするエディタを使える。
背景色に近い色にして目立たなくすると。

多くの研磨をやっているなら、いっそのこと外部DSLを使ったほうがよい。
学習曲線が上がってしまえばそっちの方が楽、とのこと。


割引ルール(Ruby)

シンプルな割引ルールで、3000ドルを超える注文の場合は 3% 割り引くといったものを考える。

rule = DiscountBuilder.percent(3).when.minimum(30000).content

これを別ファイルとして分割すると次のようになる。

processing code...
    input = File.readlines("rules.rb")
    rules = []
    input.each do |line|
      builder = DiscountBuilder.new
      builder.instance_eval(line)
      rules << builder.content if builder.has_rule?
    end

rules.rb...
  percent(3).when.minimum(30000)
  # more rules....

コメントなどのようにルールがない場合は無視する必要があるので、builder.has_rule?によるチェックは必須。

続いてドメインエキスパートが次のようなものを好んだ場合。

3% if  value at least $30000

次のようになる。

class DiscountRulePolisher...
  def polish aString
    @buffer = aString
    process_percent
    process_value_at_least
    process_if
    replace_spaces
    return @buffer
  end
  • "3%"を"percent(3)"に変換
    class DiscountRulePolisher...
      def process_percent 
        @buffer = @buffer.gsub(/\b(\d+)%\s+/, 'percent(\1) ')
      end&br; 
    エクスプレッションの境界は \b だが、 "%" は単語の境界ではないので定義に注意。
  • "at least"は同じように処理
    class DiscountRulePolisher...
      def process_value_at_least 
        @buffer = @buffer.gsub(/\bvalue\s+at\s+least\s+\$?(\d+)\b/, 'minimum(\1)')
      end
  • "if"の処理
    Rubyのキーワードなので置換する。
    class DiscountRulePolisher...
      def process_if 
        @buffer = @buffer.gsub(/\bif\b/, 'when')
      end&br;
    "when"に限らず"my_if"や"_if"でもよい。
  • 空白をメソッドコールのドットで置換
    class DiscountRulePolisher...
      def replace_spaces 
        @buffer = @buffer.strip.gsub(/ +/, ".")
      end

ファウラーへのフィードバック

担当者のつぶやき

翻訳難しいっす。。。

みんなの突っ込み