mustermann19 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +1 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +1081 -0
- data/Rakefile +6 -0
- data/bench/capturing.rb +57 -0
- data/bench/regexp.rb +21 -0
- data/bench/simple_vs_sinatra.rb +23 -0
- data/bench/template_vs_addressable.rb +26 -0
- data/internals.md +64 -0
- data/lib/mustermann.rb +61 -0
- data/lib/mustermann/ast/compiler.rb +168 -0
- data/lib/mustermann/ast/expander.rb +134 -0
- data/lib/mustermann/ast/node.rb +160 -0
- data/lib/mustermann/ast/parser.rb +137 -0
- data/lib/mustermann/ast/pattern.rb +84 -0
- data/lib/mustermann/ast/transformer.rb +129 -0
- data/lib/mustermann/ast/translator.rb +108 -0
- data/lib/mustermann/ast/tree_renderer.rb +29 -0
- data/lib/mustermann/ast/validation.rb +43 -0
- data/lib/mustermann/caster.rb +117 -0
- data/lib/mustermann/equality_map.rb +48 -0
- data/lib/mustermann/error.rb +6 -0
- data/lib/mustermann/expander.rb +206 -0
- data/lib/mustermann/extension.rb +52 -0
- data/lib/mustermann/identity.rb +19 -0
- data/lib/mustermann/mapper.rb +98 -0
- data/lib/mustermann/pattern.rb +182 -0
- data/lib/mustermann/rails.rb +17 -0
- data/lib/mustermann/regexp_based.rb +30 -0
- data/lib/mustermann/regular.rb +26 -0
- data/lib/mustermann/router.rb +9 -0
- data/lib/mustermann/router/rack.rb +50 -0
- data/lib/mustermann/router/simple.rb +144 -0
- data/lib/mustermann/shell.rb +29 -0
- data/lib/mustermann/simple.rb +38 -0
- data/lib/mustermann/simple_match.rb +30 -0
- data/lib/mustermann/sinatra.rb +22 -0
- data/lib/mustermann/template.rb +48 -0
- data/lib/mustermann/to_pattern.rb +45 -0
- data/lib/mustermann/version.rb +3 -0
- data/mustermann.gemspec +31 -0
- data/spec/expander_spec.rb +105 -0
- data/spec/extension_spec.rb +296 -0
- data/spec/identity_spec.rb +83 -0
- data/spec/mapper_spec.rb +83 -0
- data/spec/mustermann_spec.rb +65 -0
- data/spec/pattern_spec.rb +49 -0
- data/spec/rails_spec.rb +522 -0
- data/spec/regexp_based_spec.rb +8 -0
- data/spec/regular_spec.rb +36 -0
- data/spec/router/rack_spec.rb +39 -0
- data/spec/router/simple_spec.rb +32 -0
- data/spec/shell_spec.rb +109 -0
- data/spec/simple_match_spec.rb +10 -0
- data/spec/simple_spec.rb +237 -0
- data/spec/sinatra_spec.rb +574 -0
- data/spec/support.rb +5 -0
- data/spec/support/coverage.rb +16 -0
- data/spec/support/env.rb +15 -0
- data/spec/support/expand_matcher.rb +27 -0
- data/spec/support/match_matcher.rb +39 -0
- data/spec/support/pattern.rb +39 -0
- data/spec/template_spec.rb +815 -0
- data/spec/to_pattern_spec.rb +20 -0
- metadata +301 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
module Router
|
5
|
+
# Simple pattern based router that allows matching a string to a given callback.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# require 'mustermann/router/simple'
|
9
|
+
#
|
10
|
+
# router = Mustermann::Router::Simple.new do
|
11
|
+
# on ':name/:sub' do |string, params|
|
12
|
+
# params['sub']
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# on 'foo' do
|
16
|
+
# "bar"
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# router.call("foo") # => "bar"
|
21
|
+
# router.call("a/b") # => "b"
|
22
|
+
# router.call("bar") # => nil
|
23
|
+
class Simple
|
24
|
+
# Default value for when no pattern matches
|
25
|
+
attr_accessor :default
|
26
|
+
|
27
|
+
# @example with a default value
|
28
|
+
# require 'mustermann/router/simple'
|
29
|
+
#
|
30
|
+
# router = Mustermann::Router::Simple.new(default: 42)
|
31
|
+
# router.on(':name', capture: :digit) { |string| string.to_i }
|
32
|
+
# router.call("23") # => 23
|
33
|
+
# router.call("example") # => 42
|
34
|
+
#
|
35
|
+
# @example block with implicit receiver
|
36
|
+
# require 'mustermann/router/simple'
|
37
|
+
#
|
38
|
+
# router = Mustermann::Router::Simple.new do
|
39
|
+
# on('/foo') { 'foo' }
|
40
|
+
# on('/bar') { 'bar' }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# @example block with explicit receiver
|
44
|
+
# require 'mustermann/router/simple'
|
45
|
+
#
|
46
|
+
# router = Mustermann::Router::Simple.new(type: :rails) do |r|
|
47
|
+
# r.on('/foo') { 'foo' }
|
48
|
+
# r.on('/bar') { 'bar' }
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# @param default value to be returned if nothing matches
|
52
|
+
# @param options [Hash] pattern options
|
53
|
+
# @return [Mustermann::Router::Simple] new router instance
|
54
|
+
def initialize(options = {}, &block)
|
55
|
+
@options = options
|
56
|
+
@map = []
|
57
|
+
@default = @options.delete(:default)
|
58
|
+
|
59
|
+
block.arity == 0 ? instance_eval(&block) : yield(self) if block
|
60
|
+
end
|
61
|
+
|
62
|
+
# @example
|
63
|
+
# require 'mustermann/router/simple'
|
64
|
+
#
|
65
|
+
# router = Mustermann::Router::Simple.new
|
66
|
+
# router.on(':a/:b') { 42 }
|
67
|
+
# router['foo/bar'] # => <#Proc:...>
|
68
|
+
# router['foo_bar'] # => nil
|
69
|
+
#
|
70
|
+
# @return [#call, nil] callback for given string, if a pattern matches
|
71
|
+
def [](string)
|
72
|
+
string = string_for(string) unless string.is_a? String
|
73
|
+
@map.detect { |p,v| p === string }[1]
|
74
|
+
end
|
75
|
+
|
76
|
+
# @example
|
77
|
+
# require 'mustermann/router/simple'
|
78
|
+
#
|
79
|
+
# router = Mustermann::Router::Simple.new
|
80
|
+
# router['/:name'] = proc { |string, params| params['name'] }
|
81
|
+
# router.call('/foo') # => "foo"
|
82
|
+
#
|
83
|
+
# @param pattern [String, Mustermann::Pattern] matcher
|
84
|
+
# @param callback [#call] callback to call on match
|
85
|
+
# @see #on
|
86
|
+
def []=(pattern, callback)
|
87
|
+
on(pattern, call: callback)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @example with block
|
91
|
+
# require 'mustermann/router/simple'
|
92
|
+
#
|
93
|
+
# router = Mustermann::Router::Simple.new
|
94
|
+
#
|
95
|
+
# router.on(':a/:b') { 42 }
|
96
|
+
# router.call('foo/bar') # => 42
|
97
|
+
# router.call('foo_bar') # => nil
|
98
|
+
#
|
99
|
+
# @example with callback option
|
100
|
+
# require 'mustermann/router/simple'
|
101
|
+
#
|
102
|
+
# callback = proc { 42 }
|
103
|
+
# router = Mustermann::Router::Simple.new
|
104
|
+
#
|
105
|
+
# router.on(':a/:b', call: callback)
|
106
|
+
# router.call('foo/bar') # => 42
|
107
|
+
# router.call('foo_bar') # => nil
|
108
|
+
#
|
109
|
+
# @param patterns [Array<String, Pattern>]
|
110
|
+
# @param call [#call] callback object, need to hand in block if missing
|
111
|
+
# @param options [Hash] pattern options
|
112
|
+
def on(*patterns)
|
113
|
+
options = patterns.last.is_a?(Hash) ? patterns.pop : {}
|
114
|
+
call = options.delete(:call) || Proc.new
|
115
|
+
patterns.each do |pattern|
|
116
|
+
pattern = Mustermann.new(pattern.to_str, @options.merge(options)) if pattern.respond_to? :to_str
|
117
|
+
@map << [pattern, call]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Finds the matching callback and calls `call` on it with the given input and the params.
|
122
|
+
# @return the callback's return value
|
123
|
+
def call(input)
|
124
|
+
@map.each do |pattern, callback|
|
125
|
+
catch(:pass) do
|
126
|
+
next unless params = pattern.params(string_for(input))
|
127
|
+
return invoke(callback, input, params, pattern)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
@default
|
131
|
+
end
|
132
|
+
|
133
|
+
def invoke(callback, input, params, pattern)
|
134
|
+
callback.call(input, params)
|
135
|
+
end
|
136
|
+
|
137
|
+
def string_for(input)
|
138
|
+
input.to_str
|
139
|
+
end
|
140
|
+
|
141
|
+
private :invoke, :string_for
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'mustermann/pattern'
|
2
|
+
require 'mustermann/simple_match'
|
3
|
+
|
4
|
+
module Mustermann
|
5
|
+
# Matches strings that are identical to the pattern.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Mustermann.new('/*.*', type: :shell) === '/bar' # => false
|
9
|
+
#
|
10
|
+
# @see Mustermann::Pattern
|
11
|
+
# @see file:README.md#shell Syntax description in the README
|
12
|
+
class Shell < Pattern
|
13
|
+
# @param (see Mustermann::Pattern#initialize)
|
14
|
+
# @return (see Mustermann::Pattern#initialize)
|
15
|
+
# @see (see Mustermann::Pattern#initialize)
|
16
|
+
def initialize(string, options = {})
|
17
|
+
@flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
|
18
|
+
@flags |= File::FNM_EXTGLOB if defined? File::FNM_EXTGLOB
|
19
|
+
super(string, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param (see Mustermann::Pattern#===)
|
23
|
+
# @return (see Mustermann::Pattern#===)
|
24
|
+
# @see (see Mustermann::Pattern#===)
|
25
|
+
def ===(string)
|
26
|
+
File.fnmatch? @string, unescape(string), @flags
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'mustermann/regexp_based'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
# Sinatra 1.3 style pattern implementation.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# Mustermann.new('/:foo', type: :simple) === '/bar' # => true
|
8
|
+
#
|
9
|
+
# @see Mustermann::Pattern
|
10
|
+
# @see file:README.md#simple Syntax description in the README
|
11
|
+
class Simple < RegexpBased
|
12
|
+
supported_options :greedy, :space_matches_plus
|
13
|
+
|
14
|
+
def compile(options = {})
|
15
|
+
greedy = options.fetch(:greedy, true)
|
16
|
+
uri_decode = options.fetch(:uri_decode, true)
|
17
|
+
space_matches_plus = options.fetch(:space_matches_plus, true)
|
18
|
+
pattern = @string.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c, uri_decode, space_matches_plus) }
|
19
|
+
pattern.gsub!(/((:\w+)|\*)/) do |match|
|
20
|
+
match == "*" ? "(?<splat>.*?)" : "(?<#{$2[1..-1]}>[^/?#]+#{?? unless greedy})"
|
21
|
+
end
|
22
|
+
/\A#{Regexp.new(pattern)}\Z/
|
23
|
+
rescue SyntaxError, RegexpError => error
|
24
|
+
type = error.message["invalid group name"] ? CompileError : ParseError
|
25
|
+
raise type, error.message, error.backtrace
|
26
|
+
end
|
27
|
+
|
28
|
+
def encoded(char, uri_decode, space_matches_plus)
|
29
|
+
return Regexp.escape(char) unless uri_decode
|
30
|
+
parser = URI::Parser.new
|
31
|
+
encoded = Regexp.union(parser.escape(char), parser.escape(char, /./).downcase, parser.escape(char, /./).upcase)
|
32
|
+
encoded = Regexp.union(encoded, encoded('+', true, true)) if space_matches_plus and char == " "
|
33
|
+
encoded
|
34
|
+
end
|
35
|
+
|
36
|
+
private :compile, :encoded
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mustermann
|
2
|
+
# Fakes MatchData for patterns that do not support capturing.
|
3
|
+
# @see http://ruby-doc.org/core-2.0/MatchData.html MatchData
|
4
|
+
class SimpleMatch
|
5
|
+
# @api private
|
6
|
+
def initialize(string)
|
7
|
+
@string = string.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [String] the string that was matched against
|
11
|
+
def to_s
|
12
|
+
@string.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<String>] empty array for imitating MatchData interface
|
16
|
+
def names
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Array<String>] empty array for imitating MatchData interface
|
21
|
+
def captures
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [nil] imitates MatchData interface
|
26
|
+
def [](*args)
|
27
|
+
captures[*args]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mustermann/ast/pattern'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
# Sinatra 2.0 style pattern implementation.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# Mustermann.new('/:foo') === '/bar' # => true
|
8
|
+
#
|
9
|
+
# @see Mustermann::Pattern
|
10
|
+
# @see file:README.md#sinatra Syntax description in the README
|
11
|
+
class Sinatra < AST::Pattern
|
12
|
+
on(nil, ??, ?)) { |c| unexpected(c) }
|
13
|
+
on(?*) { |c| scan(/\w+/) ? node(:named_splat, buffer.matched) : node(:splat) }
|
14
|
+
on(?() { |c| node(:group) { read unless scan(?)) } }
|
15
|
+
on(?:) { |c| node(:capture) { scan(/\w+/) } }
|
16
|
+
on(?\\) { |c| node(:char, expect(/./)) }
|
17
|
+
|
18
|
+
suffix ?? do |char, element|
|
19
|
+
node(:optional, element)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'mustermann/ast/pattern'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
# URI template pattern implementation.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# Mustermann.new('/{foo}') === '/bar' # => true
|
8
|
+
#
|
9
|
+
# @see Mustermann::Pattern
|
10
|
+
# @see file:README.md#template Syntax description in the README
|
11
|
+
# @see http://tools.ietf.org/html/rfc6570 RFC 6570
|
12
|
+
class Template < AST::Pattern
|
13
|
+
on ?{ do |char|
|
14
|
+
variable = proc do
|
15
|
+
match = expect(/(?<name>\w+)(?:\:(?<prefix>\d{1,4})|(?<explode>\*))?/)
|
16
|
+
node(:variable, match[:name], prefix: match[:prefix], explode: match[:explode])
|
17
|
+
end
|
18
|
+
|
19
|
+
operator = buffer.scan(/[\+\#\.\/;\?\&\=\,\!\@\|]/)
|
20
|
+
expression = node(:expression, [variable[]], operator: operator) { variable[] if scan(?,) }
|
21
|
+
expression if expect(?})
|
22
|
+
end
|
23
|
+
|
24
|
+
on(?}) { |c| unexpected(c) }
|
25
|
+
|
26
|
+
# @!visibility private
|
27
|
+
def compile(*args)
|
28
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
29
|
+
@split_params = {}
|
30
|
+
super(*args, options.merge(:split_params => @split_params))
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
def map_param(key, value)
|
35
|
+
return super unless variable = @split_params[key]
|
36
|
+
value = value.split variable[:separator]
|
37
|
+
value.map! { |e| e.sub(/\A#{key}=/, '') } if variable[:parametric]
|
38
|
+
value.map! { |e| super(key, e) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def always_array?(key)
|
43
|
+
@split_params.include? key
|
44
|
+
end
|
45
|
+
|
46
|
+
private :compile, :map_param, :always_array?
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
# Mixin for adding {#to_pattern} ducktyping to objects.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# require 'mustermann/to_pattern'
|
8
|
+
#
|
9
|
+
# class Foo
|
10
|
+
# include Mustermann::ToPattern
|
11
|
+
#
|
12
|
+
# def to_s
|
13
|
+
# ":foo/:bar"
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Foo.new.to_pattern # => #<Mustermann::Sinatra:":foo/:bar">
|
18
|
+
#
|
19
|
+
# By default included into {String}, {Symbol}, {Regexp} and {Mustermann::Pattern}.
|
20
|
+
module ToPattern
|
21
|
+
# Converts the object into a {Mustermann::Pattern}.
|
22
|
+
#
|
23
|
+
# @example converting a string
|
24
|
+
# ":name.png".to_pattern # => #<Mustermann::Sinatra:":name.png">
|
25
|
+
#
|
26
|
+
# @example converting a string with options
|
27
|
+
# "/*path".to_pattern(type: :rails) # => #<Mustermann::Rails:"/*path">
|
28
|
+
#
|
29
|
+
# @example converting a regexp
|
30
|
+
# /.*/.to_pattern # => #<Mustermann::Regular:".*">
|
31
|
+
#
|
32
|
+
# @example converting a pattern
|
33
|
+
# Mustermann.new("foo").to_pattern # => #<Mustermann::Sinatra:"foo">
|
34
|
+
#
|
35
|
+
# @param [Hash] options The options hash.
|
36
|
+
# @return [Mustermann::Pattern] pattern corresponding to object.
|
37
|
+
def to_pattern(options = {})
|
38
|
+
Mustermann.new(self, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
append_features String
|
42
|
+
append_features Regexp
|
43
|
+
append_features Mustermann::Pattern
|
44
|
+
end
|
45
|
+
end
|
data/mustermann.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "mustermann/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mustermann19"
|
6
|
+
s.version = Mustermann::VERSION
|
7
|
+
s.authors = ["Konstantin Haase", "namusyaka"]
|
8
|
+
s.email = "namusyaka@gmail.com"
|
9
|
+
s.homepage = "https://github.com/namusyaka/mustermann"
|
10
|
+
s.summary = %q{use patterns like regular expressions}
|
11
|
+
s.description = %q{library implementing patterns that behave like regular expressions for use in Ruby 1.9}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.extra_rdoc_files = %w[README.md internals.md]
|
17
|
+
s.require_path = 'lib'
|
18
|
+
s.required_ruby_version = '>= 1.9.2'
|
19
|
+
|
20
|
+
s.add_dependency 'enumerable-lazy' if RUBY_VERSION < '2.0.0'
|
21
|
+
s.add_development_dependency 'rspec' #, '~> 2.14'
|
22
|
+
s.add_development_dependency 'rspec-its'
|
23
|
+
s.add_development_dependency 'addressable'
|
24
|
+
s.add_development_dependency 'sinatra', '~> 1.4'
|
25
|
+
s.add_development_dependency 'rack-test'
|
26
|
+
s.add_development_dependency 'rake'
|
27
|
+
s.add_development_dependency 'yard'
|
28
|
+
#s.add_development_dependency 'redcarpet'
|
29
|
+
s.add_development_dependency 'simplecov'
|
30
|
+
s.add_development_dependency 'coveralls'
|
31
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/expander'
|
3
|
+
|
4
|
+
describe Mustermann::Expander do
|
5
|
+
it 'expands a pattern' do
|
6
|
+
expander = Mustermann::Expander.new("/:foo.jpg")
|
7
|
+
expander.expand(foo: 42).should be == "/42.jpg"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'expands multiple patterns' do
|
11
|
+
expander = Mustermann::Expander.new << "/:foo.:ext" << "/:foo"
|
12
|
+
expander.expand(foo: 42, ext: 'jpg').should be == "/42.jpg"
|
13
|
+
expander.expand(foo: 23).should be == "/23"
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'supports setting pattern options' do
|
17
|
+
expander = Mustermann::Expander.new(type: :rails) << "/:foo(.:ext)" << "/:bar"
|
18
|
+
expander.expand(foo: 42, ext: 'jpg').should be == "/42.jpg"
|
19
|
+
expander.expand(foo: 42).should be == "/42"
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'supports combining different pattern styles' do
|
23
|
+
expander = Mustermann::Expander.new << Mustermann.new("/:foo(.:ext)", type: :rails) << Mustermann.new("/:bar", type: :sinatra)
|
24
|
+
expander.expand(foo: 'pony', ext: 'jpg').should be == '/pony.jpg'
|
25
|
+
expander.expand(bar: 23).should be == "/23"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'ignores nil values' do
|
29
|
+
expander = Mustermann::Expander.new << Mustermann.new("/:foo(.:ext)?")
|
30
|
+
expander.expand(foo: 'pony', ext: nil).should be == '/pony'
|
31
|
+
end
|
32
|
+
|
33
|
+
describe :additional_values do
|
34
|
+
context "illegal value" do
|
35
|
+
example { expect { Mustermann::Expander.new(additional_values: :foo) }.to raise_error(ArgumentError) }
|
36
|
+
example { expect { Mustermann::Expander.new('/').expand(:foo, a: 10) }.to raise_error(ArgumentError) }
|
37
|
+
end
|
38
|
+
|
39
|
+
context :raise do
|
40
|
+
subject(:expander) { Mustermann::Expander.new('/:a', additional_values: :raise) }
|
41
|
+
example { expander.expand(a: ?a).should be == '/a' }
|
42
|
+
example { expect { expander.expand(a: ?a, b: ?b) }.to raise_error(Mustermann::ExpandError) }
|
43
|
+
example { expect { expander.expand(b: ?b) }.to raise_error(Mustermann::ExpandError) }
|
44
|
+
end
|
45
|
+
|
46
|
+
context :ignore do
|
47
|
+
subject(:expander) { Mustermann::Expander.new('/:a', additional_values: :ignore) }
|
48
|
+
example { expander.expand(a: ?a).should be == '/a' }
|
49
|
+
example { expander.expand(a: ?a, b: ?b).should be == '/a' }
|
50
|
+
example { expect { expander.expand(b: ?b) }.to raise_error(Mustermann::ExpandError) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context :append do
|
54
|
+
subject(:expander) { Mustermann::Expander.new('/:a', additional_values: :append) }
|
55
|
+
example { expander.expand(a: ?a).should be == '/a' }
|
56
|
+
example { expander.expand(a: ?a, b: ?b).should be == '/a?b=b' }
|
57
|
+
example { expect { expander.expand(b: ?b) }.to raise_error(Mustermann::ExpandError) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe :cast do
|
62
|
+
subject(:expander) { Mustermann::Expander.new('/:a(/:b)?') }
|
63
|
+
|
64
|
+
example { expander.cast { "FOOBAR" }.expand(a: "foo") .should be == "/FOOBAR" }
|
65
|
+
example { expander.cast { |v| v.upcase }.expand(a: "foo") .should be == "/FOO" }
|
66
|
+
example { expander.cast { |v| v.upcase }.expand(a: "foo", b: "bar") .should be == "/FOO/BAR" }
|
67
|
+
example { expander.cast(:a) { |v| v.upcase }.expand(a: "foo", b: "bar") .should be == "/FOO/bar" }
|
68
|
+
example { expander.cast(:a, :b) { |v| v.upcase }.expand(a: "foo", b: "bar") .should be == "/FOO/BAR" }
|
69
|
+
example { expander.cast(Integer) { |k,v| "#{k}_#{v}" }.expand(a: "foo", b: 42) .should be == "/foo/b_42" }
|
70
|
+
|
71
|
+
example do
|
72
|
+
expander.cast(:a) { |v| v.upcase }
|
73
|
+
expander.cast(:b) { |v| v.downcase }
|
74
|
+
expander.expand(a: "fOo", b: "bAr").should be == "/FOO/bar"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe :== do
|
79
|
+
example { Mustermann::Expander.new('/foo') .should be == Mustermann::Expander.new('/foo') }
|
80
|
+
example { Mustermann::Expander.new('/foo') .should_not be == Mustermann::Expander.new('/bar') }
|
81
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should be == Mustermann::Expander.new('/foo', type: :rails) }
|
82
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should_not be == Mustermann::Expander.new('/foo', type: :sinatra) }
|
83
|
+
end
|
84
|
+
|
85
|
+
describe :hash do
|
86
|
+
example { Mustermann::Expander.new('/foo') .hash.should be == Mustermann::Expander.new('/foo').hash }
|
87
|
+
example { Mustermann::Expander.new('/foo') .hash.should_not be == Mustermann::Expander.new('/bar').hash }
|
88
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .hash.should be == Mustermann::Expander.new('/foo', type: :rails).hash }
|
89
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .hash.should_not be == Mustermann::Expander.new('/foo', type: :sinatra).hash }
|
90
|
+
end
|
91
|
+
|
92
|
+
describe :eql? do
|
93
|
+
example { Mustermann::Expander.new('/foo') .should be_eql Mustermann::Expander.new('/foo') }
|
94
|
+
example { Mustermann::Expander.new('/foo') .should_not be_eql Mustermann::Expander.new('/bar') }
|
95
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should be_eql Mustermann::Expander.new('/foo', type: :rails) }
|
96
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should_not be_eql Mustermann::Expander.new('/foo', type: :sinatra) }
|
97
|
+
end
|
98
|
+
|
99
|
+
describe :equal? do
|
100
|
+
example { Mustermann::Expander.new('/foo') .should_not be_equal Mustermann::Expander.new('/foo') }
|
101
|
+
example { Mustermann::Expander.new('/foo') .should_not be_equal Mustermann::Expander.new('/bar') }
|
102
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should_not be_equal Mustermann::Expander.new('/foo', type: :rails) }
|
103
|
+
example { Mustermann::Expander.new('/foo', type: :rails) .should_not be_equal Mustermann::Expander.new('/foo', type: :sinatra) }
|
104
|
+
end
|
105
|
+
end
|