Sinatraのようなベース言語の構文を使って実現するDSLを内部DSLといいます。前回僕が書いたChinatraは一見内部DSLにみえるけど、ベース言語とは異なる構文を使った外部DSLです。
でも時代の風は内部DSLに向いています。
自分もRubyの構文のユルさとメタプログラミングを使ってSinatraのような内部DSLを書いてみたい。
そしてRubyistをマインドコントロールしたい..
そんなわけで...
Sinatraと別の構文を持ったWebフレームワーク「Shynatra」をRubyで書きました:-)
ShynatraはRubyで下記のような最小労力で、手早くウェブアプリケーションを作成するためのDSLです。
# myapp.rb
require "shynatra"
R/:hello./ {
'Hello world!'
}
R はCRUDのRです。/:hello
の部分はパス(/hello)になります。先頭のコロンはパラメータを表しているわけではありません。./
はパスの終端子です。気に入らなくても省略はできません。それでも気に入らない人は.|
か ._
を使いましょう。
Rubyに慣れていない方のために一言付け加えますが、これはよく使われる由緒正しきRubyの構文です..解説はこちら1 。
ルートパスの指定にはnilを使います。
R/nil./ {
@message = message "Shynatra"
haml :index
}
H/nil./ {
def message(app)
"Welcome to #{app}!"
end
}
SinatraのHelpersに対応するのはHです。Hを使うときも無意味に/nil./
します
ええ、nilするんです。
パスの区切りは_(アンダースコア)を使います。つまり/foo/bar
は以下のように指定します。
R/:foo_bar./ {
"You are in: '/foo/bar'"
}
当然、パス名にアンダースコアを含めることはできません。
名前付きパラメータは@
を前置します。
R/:@name./ {
"Hello, " + params[:name]
}
この場合もコロンは必須です。忘れてはいけません。
名前付きパラメータをルート以外で使いたいこともあるでしょう。そのときは6を使います。
R/:foo_6name./ {
"You are in: '/foo/:name' with '#{params[:name]}'"
}
R/:foo_6bar_baz_6name./ {
"You are in '/foo/:bar/baz/:name' with :bar => #{params[:bar]}, :name => #{params[:name]}"
}
ええ、数字の6です。冗談じゃなく。
解説は以上です。CRUDのC U D も多分動くと思います。詳細はコードを見てくださいね。
Rubyの内部DSLであなたも**natraしてみませんか?
関連記事:
require "sinatra"
module Shynatra
module Path
def parse(sym)
'/' + sym.to_s.gsub('_', '/').gsub('@', ':').gsub('/6', '/:')
end
end
CRUD = "CRUDH".each_char
M_TBL = Hash[CRUD.zip %w(post get put delete helpers)]
CRUD.each do |name|
klass = Class.new do
extend Path
def self./(body)
path, args, blk = body
Sinatra::Application.send(M_TBL[self.to_s[/\w+$/]], parse(path), *args, &blk)
end
end
const_set(name, klass)
end
end
[Symbol, NilClass].each do |klass|
klass.class_eval do
%w(| / _).each do |name|
define_method(name) { |*args, &blk| [self, args, blk] }
end
end
end
include Shynatra
END { Sinatra::Application.run! }