Artanis
Crystal's metaprogramming macros to build a Sinatra-like DSL for the Crystal language.
Rationale
The DSL doesn't stock blocks to be invoked later on, but rather produces actual
methods using macros (match and the sugar get, post, etc.) where special
chars like /, ., ( and ) are replaced as _SLASH_, _DOT_, _LPAREN_
and _RPAREN_. Also, :param segments are replaced to _PARAM_.
Eventually methods look like:
get "/posts" | def match_GET__SLASH_posts
get "/posts.xml" | def match_GET__SLASH_posts_DOT_xml
Please read dsl.cr for more details.
Eventually a call method is generated. It iterates over the class methods,
selects the generated match_* methods and generates a big case statement,
transforming the method names back to regular expressions, and eventually
calling the method with matched data (if any).
Please read application.cr for more details.
Usage
require "http/server"
require "artanis"
class App < Artanis::Application
get "/" do
"ROOT"
end
get "/forbidden" do
403
end
get "/posts/:id.:format" do
p params["id"]
p params["format"]
"post"
end
get "/posts/:post_id/comments/:id(.:format)" do |post_id, id, format|
p params["format"]?
200
end
end
server = HTTP::Server.new(9292) do |context|
App.call(context)
end
puts "Listening on http://0.0.0.0:9292"
server.listen
Benchmark
Running wrk against the above example (pointless hello world) gives the following results (TL;DR 12µs per request):
$ wrk -c 1000 -t 2 -d 5 http://localhost:9292/fast
Running 5s test @ http://localhost:9292/fast
2 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 12.26ms 14.48ms 423.28ms 99.05%
Req/Sec 41.17k 2.93k 48.65k 76.00%
409663 requests in 5.01s, 25.79MB read
Requests/sec: 81722.08
Transfer/sec: 5.14MB
A better benchmark is available in test/dsl_bench.cr which monitors some
limits of the generated Crystal code, like going over all routes to find nothing
takes an awful lot of time, since it must build/execute a regular expression
against EVERY routes to eventually... find nothing.
$ crystal run --release test/dsl_bench.cr
get root: 0.84 µs
get param: 1.51 µs
get params (block args): 2.18 µs
get many params: 3.75 µs
get many params (block args): 2.59 µs
not found (method): 0.73 µs
not found (path): 15.93 µs
Keep in mind these numbers tell nothing about reality. They only measure how
fast the generated Application.call(request) method is in predefined cases.
License
Licensed under the MIT License