Time to Read

2分

なんか twitter でそういう声があったのでざっくり試したよ。

Rack::URLMap を使う

なんと! Rack::URLMap は、 env["PATH_INFO"] をよしなにしてはくれません。/sub サブディレクトリにマウントしたアプリケ~ションにも、 /sub が付いたまま渡ります。なので、自分で削除する Rack Middleware 書いたった。

app1.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
require 'rubygems'
require 'bundler/setup'
Bundler.require
 
class SubApp < Sinatra::Base
  get '/' do
    "Sub::Index!"
  end
 
  get '/foo' do
    "Sub::Foo!"
  end
end
 
class App < Sinatra::Base
  get '/' do
    "Index!"
  end
 
  get '/hoge' do
    "Hoge!"
  end
end
 
class PrefixDeleter
  def initialize(app, prefix)
    @app, @prefix = app, prefix
  end
 
  def call(env)
    env["PATH_INFO"].sub! /^#{@prefix}/, ''
    @app.call(env)
  end
end
 
App1 = Rack::Builder.app do
  map('/') { run App.new }
  map('/sub') do
    use PrefixDeleter, '/sub'
    run SubApp.new
  end
end

config.ru:

1
2
require './app1'
run App1

結論: ダ、ダサい。。。 Rack 組み込みのものは最低限の機能しかないんだな~と。

Padrino::Routing を使う

Padrino framework の一部、 Padrino::Routing を使う。このモジュールは padrino-core gems で定義されているので、 Gemfile 的には

1
gem 'padrino-core'

これだけ追記する。

app2.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require 'rubygems'
require 'bundler/setup'
Bundler.require
 
class App2 < Sinatra::Base
  register Padrino::Routing
 
  controller :index do
    get :index do
      "Index!"
    end
 
    get :hoge do
      "Hoge!"
    end
  end
 
  controller :sub do
    get :index do
      "Sub::Index!"
    end
 
    get :foo do
      "Sub::Foo!"
    end
  end
end
 
App2.run! if __FILE__ == $0

あと、さっきの URLMap の奴と違い、 ruby app2.rb で立ち上がる。 Sinatra 感出てる。

若干書き方がすっきりしたが、あくまでも「コントローラを導入する」拡張なので大仰な気がする。

Sinatra::Namespace ってのがあるじゃん!

sinatrarb.com 公式でもリンクされている。 README をみる限り、割と多機能。

Gemfile:

1
gem 'sinatra-namespace', require: 'sinatra/namespace'

app3.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require 'rubygems'
require 'bundler/setup'
Bundler.require
 
class App3 < Sinatra::Base
  register Sinatra::Namespace
 
  get '/' do
    "Index!"
  end
 
  get '/hoge' do
    "Hoge!"
  end
 
  namespace "/sub" do
    get do
      "Sub::Index!"
    end
 
    get '/foo' do
      "Sub::Foo!"
    end
  end
end
 
App3.run! if __FILE__ == $0

いいね~

Sinatra::Namespace は、ホスト名でも振り分けとか何とかしてくれたりするんで便利かもしれない。

あと、たとえば Padrino framework を便利だから使いたいけど、コントローラを導入するのは大仰だなあ、みたいな時に代用として使えると思う。 Padrino::Application を継承したあなたのアプリケーションに register Sinatra::Namespace 宣言を追記すれば大丈夫? かな?

おまけ、今回のテストケース

Gemfile:

1
2
3
group :test do
  gem 'rack-test', require: 'rack/test'
end

apps_testall.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
require 'minitest/unit'
['app1', 'app2', 'app3'].each {|app| require "./#{app}" }
Bundler.require :test
 
MiniTest::Unit.autorun
 
module AppTest
  module Base
    include Rack::Test::Methods
 
    def test_access_root
      get '/'
      assert last_response.body =~ /Index!/
    end
 
    def test_access_hoge
      get '/hoge'
      assert last_response.body =~ /Hoge!/
    end
 
    def test_access_sub
      get '/sub'
      assert last_response.body =~ /Sub::Index!/
    end
 
    def test_access_sub_foo
      get '/sub/foo'
      assert last_response.body =~ /Sub::Foo!/
    end
  end
 
  class AppTest < MiniTest::Unit::TestCase
    include Base
 
    def app
      App1
    end
  end
 
  class App2Test < MiniTest::Unit::TestCase
    include Base
 
    def app
      App2.new
    end
  end
 
  class App3Test < MiniTest::Unit::TestCase
    include Base
 
    def app
      App3.new
    end
  end
end

言い忘れていたけれど、全部 Ruby 1.9 系でないと動かないです、文法の差異を吸収すれば動くかも。