Permalink
Browse files

remove routers

  • Loading branch information...
rkh committed Nov 29, 2014
1 parent 500a603 commit 387b1f8a6a76594a0025421e85f4f2c957e28b66
View
@@ -42,7 +42,6 @@ pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
These features are included in the library, but not loaded by default
* **[Mapper](#-mapper):** A simple tool for mapping one string to another based on patterns.
* **[Routers](#-routers):** Model execution flow based on pattern matching. Comes with a simple Rack router.
* **[Sinatra Integration](#-sinatra-integration):** Mustermann can be used as a [Sinatra](http://www.sinatrarb.com/) extension. Sinatra 2.0 and beyond will use Mustermann by default.
<a name="-pattern-types"></a>
@@ -375,47 +374,6 @@ mapper['/foo.xml'] # => "/foo/view.xml"
mapper['/foo/bar'] # => "/foo/bar"
```
<a name="-routers"></a>
## Routers
Mustermann comes with basic router implementations that will call certain callbacks depending on the input.
### Simple Router
The simple router chooses callbacks based on an input string.
``` ruby
require 'mustermann/router/simple'
router = Mustermann::Router::Simple.new(default: 42)
router.on(':name', capture: :digit) { |string| string.to_i }
router.call("23") # => 23
router.call("example") # => 42
```
### Rack Router
This is not a full replacement for Rails, Sinatra, Cuba, etc, as it only cares about path based routing.
``` ruby
require 'mustermann/router/rack'
router = Mustermann::Router::Rack.new do
on '/' do |env|
[200, {'Content-Type' => 'text/plain'}, ['Hello World!']]
end
on '/:name' do |env|
name = env['mustermann.params']['name']
[200, {'Content-Type' => 'text/plain'}, ["Hello #{name}!"]]
end
on '/something/*', call: SomeApp
end
# in a config.ru
run router
```
<a name="-sinatra-integration"></a>
## Sinatra Integration
@@ -1,9 +0,0 @@
require 'mustermann/router/simple'
require 'mustermann/router/rack'
module Mustermann
# @see Mustermann::Router::Simple
# @see Mustermann::Router::Rack
module Router
end
end
@@ -1,47 +0,0 @@
require 'mustermann/router/simple'
module Mustermann
module Router
# Simple pattern based router that allows matching paths to a given Rack application.
#
# @example config.ru
# router = Mustermann::Rack.new do
# on '/' do |env|
# [200, {'Content-Type' => 'text/plain'}, ['Hello World!']]
# end
#
# on '/:name' do |env|
# name = env['mustermann.params']['name']
# [200, {'Content-Type' => 'text/plain'}, ["Hello #{name}!"]]
# end
#
# on '/something/*', call: SomeApp
# end
#
# # in a config.ru
# run router
class Rack < Simple
def initialize(env_prefix: "mustermann", params_key: "#{env_prefix}.params", pattern_key: "#{env_prefix}.pattern", **options, &block)
@params_key, @pattern_key = params_key, pattern_key
options[:default] = [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless options.include? :default
super(**options, &block)
end
def invoke(callback, env, params, pattern)
params_was, pattern_was = env[@params_key], env[@pattern_key]
env[@params_key], env[@pattern_key] = params, pattern
response = callback.call(env)
response[1].each { |k,v| throw :pass if k.downcase == 'x-cascade' and v == 'pass' }
response
ensure
env[@params_key], env[@pattern_key] = params_was, pattern_was
end
def string_for(env)
env['PATH_INFO']
end
private :invoke, :string_for
end
end
end
@@ -1,142 +0,0 @@
require 'mustermann'
module Mustermann
module Router
# Simple pattern based router that allows matching a string to a given callback.
#
# @example
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new do
# on ':name/:sub' do |string, params|
# params['sub']
# end
#
# on 'foo' do
# "bar"
# end
# end
#
# router.call("foo") # => "bar"
# router.call("a/b") # => "b"
# router.call("bar") # => nil
class Simple
# Default value for when no pattern matches
attr_accessor :default
# @example with a default value
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new(default: 42)
# router.on(':name', capture: :digit) { |string| string.to_i }
# router.call("23") # => 23
# router.call("example") # => 42
#
# @example block with implicit receiver
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new do
# on('/foo') { 'foo' }
# on('/bar') { 'bar' }
# end
#
# @example block with explicit receiver
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new(type: :rails) do |r|
# r.on('/foo') { 'foo' }
# r.on('/bar') { 'bar' }
# end
#
# @param default value to be returned if nothing matches
# @param options [Hash] pattern options
# @return [Mustermann::Router::Simple] new router instance
def initialize(default: nil, **options, &block)
@options = options
@map = []
@default = default
block.arity == 0 ? instance_eval(&block) : yield(self) if block
end
# @example
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new
# router.on(':a/:b') { 42 }
# router['foo/bar'] # => <#Proc:...>
# router['foo_bar'] # => nil
#
# @return [#call, nil] callback for given string, if a pattern matches
def [](string)
string = string_for(string) unless string.is_a? String
@map.detect { |p,v| p === string }[1]
end
# @example
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new
# router['/:name'] = proc { |string, params| params['name'] }
# router.call('/foo') # => "foo"
#
# @param pattern [String, Mustermann::Pattern] matcher
# @param callback [#call] callback to call on match
# @see #on
def []=(pattern, callback)
on(pattern, call: callback)
end
# @example with block
# require 'mustermann/router/simple'
#
# router = Mustermann::Router::Simple.new
#
# router.on(':a/:b') { 42 }
# router.call('foo/bar') # => 42
# router.call('foo_bar') # => nil
#
# @example with callback option
# require 'mustermann/router/simple'
#
# callback = proc { 42 }
# router = Mustermann::Router::Simple.new
#
# router.on(':a/:b', call: callback)
# router.call('foo/bar') # => 42
# router.call('foo_bar') # => nil
#
# @param patterns [Array<String, Pattern>]
# @param call [#call] callback object, need to hand in block if missing
# @param options [Hash] pattern options
def on(*patterns, call: Proc.new, **options)
patterns.each do |pattern|
pattern = Mustermann.new(pattern.to_str, **options, **@options) if pattern.respond_to? :to_str
@map << [pattern, call]
end
end
# Finds the matching callback and calls `call` on it with the given input and the params.
# @return the callback's return value
def call(input)
@map.each do |pattern, callback|
catch(:pass) do
next unless params = pattern.params(string_for(input))
return invoke(callback, input, params, pattern)
end
end
@default
end
def invoke(callback, input, params, pattern)
callback.call(input, params)
end
def string_for(input)
input.to_str
end
private :invoke, :string_for
end
end
end
@@ -1,39 +0,0 @@
require 'mustermann/router/rack'
describe Mustermann::Router::Rack do
include Rack::Test::Methods
subject(:app) { Mustermann::Router::Rack.new }
context 'matching' do
before { app.on('/foo') { [418, {'Content-Type' => 'text/plain'}, 'bar'] } }
example { get('/foo').status.should be == 418 }
example { get('/bar').status.should be == 404 }
end
context "params" do
before { app.on('/:name') { |e| [200, {'Content-Type' => 'text/plain'}, e['mustermann.params']['name']] } }
example { get('/foo').body.should be == 'foo' }
example { get('/bar').body.should be == 'bar' }
end
context 'X-Cascade: pass' do
before do
app.on('/') { [200, { 'X-Cascade' => 'pass' }, ['a']] }
app.on('/') { [200, { 'x-cascade' => 'pass' }, ['b']] }
app.on('/') { [200, { 'Content-Type' => 'text/plain' }, ['c']] }
app.on('/') { [200, { 'Content-Type' => 'text/plain' }, ['d']] }
end
example { get('/').body.should be == 'c' }
end
context 'throw :pass' do
before do
app.on('/') { throw :pass }
app.on('/') { [200, { 'Content-Type' => 'text/plain' }, ['b']] }
app.on('/') { [200, { 'Content-Type' => 'text/plain' }, ['c']] }
end
example { get('/').body.should be == 'b' }
end
end
@@ -1,32 +0,0 @@
require 'mustermann/router/simple'
describe Mustermann::Router::Simple do
describe :initialize do
context "with implicit receiver" do
subject(:router) { Mustermann::Router::Simple.new { on('/foo') { 'bar' } } }
example { router.call('/foo').should be == 'bar' }
end
context "with explicit receiver" do
subject(:router) { Mustermann::Router::Simple.new { |r| r.on('/foo') { 'bar' } } }
example { router.call('/foo').should be == 'bar' }
end
context "with default" do
subject(:router) { Mustermann::Router::Simple.new(default: 'bar') }
example { router.call('/foo').should be == 'bar' }
end
end
describe :[]= do
subject(:router) { Mustermann::Router::Simple.new }
before { router['/:name'] = proc { |*a| a } }
example { router.call('/foo').should be == ['/foo', "name" => 'foo'] }
end
describe :[] do
subject(:router) { Mustermann::Router::Simple.new }
before { router.on('/x') { 42 } }
example { router['/x'].call.should be == 42 }
end
end

0 comments on commit 387b1f8

Please sign in to comment.