artanis

Version, currently 0.1.01 version
  • 0.1.0latestMar 31, 2016

github.com/ysbaddaden/artanis

Sinatra-like DSL for the Crystal language (abusing macros)

59 stars
1 dependent
License: MIT

Installation

# Add this to your shard.yml
dependencies:
  artanis:
    github: ysbaddaden/artanis
    version: ~> 0.1.0

Then run:

shards install

shard.yml

Crystal
no constraint declared

Dependencies

Development Dependencies

  • minitest*github: ysbaddaden/minitest.crdev

README

# 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](https://github.com/ysbaddaden/artanis/tree/master/src/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](https://github.com/ysbaddaden/artanis/tree/master/src/application.cr)
for more details.

## Usage

```crystal
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