Skip to content
A tiny vendorable router that makes it easy to try routes from a number of different modular Sinatra applications.
Ruby
Branch: master
Clone or download
Latest commit 334060c Jul 1, 2017
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib/sinatra Fix warnings that show up in more modern Ruby May 17, 2017
test/sinatra Fix minitest require order (prevents warning) Nov 19, 2013
.gitignore
.travis.yml Activate Travis May 17, 2017
CHANGELOG Fix spelling of "constraint" May 18, 2017
Gemfile add rack-test and Gemfile Nov 19, 2013
README.md Sytax error Jul 1, 2017
Rakefile Add release infrastructure May 17, 2017
sinatra-router.gemspec Bump version to 0.2.4 May 17, 2017

README.md

sinatra-router

A tiny vendorable router that makes it easy to try routes from a number of different modular Sinatra applications.

The motivation behind the project is to provide an easy way of composing a larger application that's been split out into a number of discrete Sinatra apps for purposes of code modularity and isolation.

In your Gemfile:

gem 'sinatra-router'

Now as part of a builder or rackup (i.e. config.ru):

module API
  class Apps < Sinatra::Base
    get "/apps" do
      200
    end
  end

  class Users < Sinatra::Base
    get "/users" do
      200
    end
  end
end

# config.ru
run Sinatra::Router do
  mount API::Apps     # /apps
  mount API::Users    # /users
end

Or mount it as middleware:

use Sinatra::Router do
  mount API::Apps
  mount API::Users
end
run Sinatra::Application

Why not just mount Sinatra apps as middleware?

An alternative is to just mount Sinatra apps as middleware, which is supported by Sinatra out of the box:

run Rack::Builder.new {
  use API::Apps
  use API::Users
}

This does get you most of the way there, but may have undesireable side effects. For example, a request always gets passed through each middleware, whether that middleware can handle the route or not. So before filters in all your apps will be run until one app in the stack successfully handles the request. This can make it somewhat more difficult to modularize your app.

Conditional Routing

Add routing conditions with arguments or blocks:

run Sinatra::Router do
  with_conditions(lambda { |e| e["HTTP_X_VERSION"] == "2" }) do
    mount API::V2::Apps
    mount API::V2::Users
  end

  mount API::V1::Users, lambda { |e| e["HTTP_X_VERSION"] == "1" }
end

Or extend the router class to create your own concise DSL:

module API
  class Router < Sinatra::Router
    def version(version, &block)
      condition = lambda { |e| version.to_s == e["HTTP_X_VERSION"] }
      if block
        with_conditions(condition, &block)
      else
        condition
      end
    end
  end
end

# config.ru
run API::Router do
  version 2 do
    mount API::V2::Apps
    mount API::V2::Users
  end

  mount API::V1::Users, version(1)
end

Passing and X-Cascade

Sinatra and sinatra-router support Rack's X-Cascade standard so that modules are able to transparently pass from one to the other as if they were part of the same application:

module API
  module V1
    class Apps < Sinatra::Base
      get "/apps" do
        # drops through to AppsV2 GET / unless request is version 1
        pass unless version == 1
        200
      end
    end
  end

  module V2
    class Apps < Sinatra::Base
      get "/apps" do
        200
      end
    end
  end
end

# config.ru
run Sinatra::Router do
  mount API::V1::Apps
  mount API::V2::Apps
end

Development

Run the tests:

ruby test/sinatra/router_test.rb

Release

  1. Update the version in json_schema.gemspec as appropriate for semantic versioning and add details to CHANGELOG.

  2. Run the release task:

    bundle exec rake release
    
You can’t perform that action at this time.