Routing Tree Web Toolkit "Roda" を動かしてみる

 先日開催された Railsdm はチケットが即完売してしまって残念ながら行けませんでした。

railsdm.github.io

 何か試してブログに書けそうなネタはないかなーとセッションの資料を見ていたら Roda という Web Framework が紹介されていました。

docs.google.com

 セッションでは Rails と組み合わせて使ってみるという内容でしたが、シンプルに Roda を動かしてみるサンプルはあまり多くなかったようなので、試してみました。

Roda とは

 詳しくは本家サイトや上記 Railsdm のセッション資料等をご覧いただければと思いますが、 Rails のようなフルスタックのフレームワークとは違い、ルーティング機能を提供するシンプルなフレームワークで、 Sinatra に近いイメージです。様々なプラグインが用意されていますので、必要に応じてプラグインを追加して機能追加していく形になります。ルーティングをツリー構造で定義できる点が Sinatra と違う点になります。

roda.jeremyevans.net

Roda のインストール

 Roda の GitHub リポジトリはこちらで、 gem のインストール方法やシンプルなアプリケーションのサンプルが紹介されています。

github.com

 Roda のインストールは gem をインストールするだけです。今回は Bundler を使ってインストールしてみますので、下記のように Gemfile を用意します。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "roda"

 そして bundle install します。

$ bundle install
Fetching gem metadata from https://rubygems.org/...............
Resolving dependencies...
Using bundler 1.17.2
Using rack 2.0.6
Fetching roda 3.18.0
Installing roda 3.18.0
Bundle complete! 1 Gemfile dependency, 3 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

サンプルアプリケーション

 とりあえず GitHub で紹介されているサンプルアプリケーションを実装してみます。下記の内容で config.ru ファイルを用意します。

require "roda"

class App < Roda
  route do |r|
    # GET / request
    r.root do
      r.redirect "/hello"
    end

    # /hello branch
    r.on "hello" do
      # Set variable for all routes in /hello branch
      @greeting = 'Hello'

      # GET /hello/world request
      r.get "world" do
        "#{@greeting} world!"
      end

      # /hello request
      r.is do
        # GET /hello request
        r.get do
          "#{@greeting}!"
        end

        # POST /hello request
        r.post do
          puts "Someone said #{@greeting}!"
          r.redirect
        end
      end
    end
  end
end

run App.freeze.app

動作確認

 Roda は Rack ベースなので、動かすには rackup するだけです。今回は環境としては Cloud9 で EC2 インスタンス上で動作させています。

$ rackup
Puma starting in single mode...
* Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:9292
Use Ctrl-C to stop

 9292ポートでアプリケーションが実行されるので、 curl でリクエストを投げてみると、下記のようにレスポンスが返ります。

$ curl localhost:9292/hello
Hello!

 ログには下記のように出力されます。

127.0.0.1 - - [30/Mar/2019:12:55:59 +0000] "GET /hello HTTP/1.1" 200 6 0.0005

サンプルアプリの内容

 Roda ではリクエストのルーティングはルーティングツリーと呼ばれる構成で管理されています。 config.ru で定義した内容がルーティングツリーになりますので、その設定内容を簡単に説明します。

 まず r.root はルートパス / への GET リクエストのみマッチします。

# GET / request
r.root do
  r.redirect '/hello'
end

 curl では下記のようにリクエストを投げます。

$ curl localhost:9292/
127.0.0.1 - - [30/Mar/2019:13:09:27 +0000] "GET / HTTP/1.1" 302 - 0.0011

 /hello というリクエストについては下記のブロックで定義しています。

# /hello branch
r.on 'hello' do
 ・・・
end

 その中でさらに下層のパスへの設定も行います。 /hello/world への GET リクエストについては下記のように定義します。

r.get 'world' do
  "#{@greeting} world!"
end

 リクエストを投げると下記のように動作します。

$ curl localhost:9292/hello/world
Hello world!
127.0.0.1 - - [30/Mar/2019:13:22:26 +0000] "GET /hello/world HTTP/1.1" 200 12 0.0013

 /hello の下層のパスがないリクエストの場合は下記の設定がマッチします。

# /hello request
r.is do
 ・・・
end

 /hello に GET リクエストが投げられた場合の設定は下記の内容になります。

r.get do
  "#{@greeting}!"
end
$ curl localhost:9292/hello
Hello!
127.0.0.1 - - [30/Mar/2019:13:28:37 +0000] "GET /hello HTTP/1.1" 200 6 0.0014

 また、 POST リクエストの場合は下記のブロックの設定になります。

# POST /hello request
r.post do
  puts "Someone said #{@greeting}!"
  r.redirect
end
$ curl -X POST localhost:9292/hello
Someone said Hello!
127.0.0.1 - - [30/Mar/2019:13:30:27 +0000] "POST /hello HTTP/1.1" 302 - 0.0013

まとめ

 今回はとりあえずサンプルをそのまま動かしただけでしたが、公式サイトには他にも様々な Matcher の書き方が紹介されています。 Rails を使っていると基本的な技術要素というよりは "Railsの使い方" に視点が行きがちだと思いますが、こういったコア機能のみ提供しているシンプルなフレームワークをベースにアプリケーションを作っていくとなると、 Rack や各個別の技術要素を改めて意識するきっかけになりそうで良いですね。