この記事はCrystal Advent Calendarの18日目の記事です。
CrystalのWeb Application Frameworkをあさっていて、好きなフレームワークが見つからなかったので、
こういうのが欲しいな。書きたいな。あるいはだれか書いてほしいなって感じのやつです。
名前はとりあえずWavesとしていますが変えるかも。
require 'waves'
class MyApp::Middleware::Sample
include Waves::Middleware
on_req do |c|
# you can hook on request (e.g. authorization
end
on_res do |c|
# you can hook on request
end
end
class MyApp::Context
include Waves::Context
BASE_DIR = "./template"
view(:json) do |c|
Waves::View::JSON.new
end
view(:html) do |c|
Waves::View::ECR.new(c, BASE_DIR)
end
end
class MyApp::Controller::Root
include Waves::Controller
def index(c) # c is context object
foo = c.req.parameters["foo"] # => Str|Array(Str)
c.view(:html).render("path/to/index.ecr", { :foo => foo }) do |res|
res.cookies.set("foo", "bar", { :secure => true }) # optional
res.headers["X-MyApp-Header"] = "Hoge" # optional
end
end
end
class MyApp::Router
include Waves::Router
get "/", MyApp::Controller::Root, :index
end
class MyApp::Server
include Waves::Server
context MyApp::Context
router MyApp::Router
middlewares do
enable MyApp::Middleware::Sample
end
end
MyApp::Server.new(8080).run()
疎結合なパーツを作って関連付けることで大きなものを作るという感じが好きなのです。
View
レスポンスとして返すデータを整形するやつです。
デフォルトでよくあるやつは定義されますが独自のものを使うこともできます。
Middleware
リクエストやレスポンスにフックする君です。
Cotnext
ユーザーからのリクエストごとに生成されるコンテキストです。
コンテキストがRequest/Responseなど、リクエストのコンテキストを表現するオブジェクトを持ちます。
また、コンテキストに依存する各機能への移譲も行います。
PerlのAmon2が分かる人向けに言えばAmon2::Webに近いといえます。
Controller
エンドポイントで行われる処理を定義します。
Contextオブジェクトが渡されます。
こんな具合でContextとおなじインターフェースを作ればmockできるようにしてテストしやすくしたいです。
Router
ルーティングを定義します。
get
とかをmacroとして提供してコンパイル時にルーティングテーブルが構築されて楽しい感じにしたいです。
Server
アプリケーションを構成する要素を結合してWebサーバーをコンパイル時に実装します。
コンパイル時に実装できると楽しい。
課題
マクロ芸の能力がまだたりない
まとめ
こんな感じの疎結合でテストしやすいWAFができて、かつMySQLがCrystalから使えるようになって、かつPerl5のDBIみたいなやつが提供されると、個人的にはCrystalでWebアプリ書くのだいぶ良い選択肢になるので、
そういう世界にしたいし、ぼくはそこを目指したいなという気持ちでいっぱいです。
まだまだyak shavingがたくさんひつようそうで道のりが長そうですが、のんびり頑張っていきたい。