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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
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(options = {}, &block)
@options = options
@map = []
@default = @options.delete(: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)
options = patterns.last.is_a?(Hash) ? patterns.pop : {}
call = options.delete(:call) || Proc.new
patterns.each do |pattern|
pattern = Mustermann.new(pattern.to_str, @options.merge(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
|