hp12c
07 June 2011

内部DSLを使って、RubyのWebフレームワークを書こう!

Sinatraのようなベース言語の構文を使って実現するDSLを内部DSLといいます。前回僕が書いたChinatraは一見内部DSLにみえるけど、ベース言語とは異なる構文を使った外部DSLです。

でも時代の風は内部DSLに向いています。

the 0.8 true language

言語ワークベンチ

自分も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してみませんか?

関連記事:

  1. SinatraはDSLなんかじゃない、Ruby偽装を使ったマインドコントロールだ!

  2. Sinatraに別構文があってもいいじゃないか!

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! }
  1. R./(Rクラスのクラスメソッド/)を引数付きで呼んでいる。引数には、:hello#/(シンボル:helloのインスタンスメソッド/)をブロック付きで呼んだ返り値が渡される


Please enable JavaScript to view the comments powered by Disqus. blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS



AltStyle によって変換されたページ (->オリジナル) /