mustermann19 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +10 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +22 -0
  7. data/README.md +1081 -0
  8. data/Rakefile +6 -0
  9. data/bench/capturing.rb +57 -0
  10. data/bench/regexp.rb +21 -0
  11. data/bench/simple_vs_sinatra.rb +23 -0
  12. data/bench/template_vs_addressable.rb +26 -0
  13. data/internals.md +64 -0
  14. data/lib/mustermann.rb +61 -0
  15. data/lib/mustermann/ast/compiler.rb +168 -0
  16. data/lib/mustermann/ast/expander.rb +134 -0
  17. data/lib/mustermann/ast/node.rb +160 -0
  18. data/lib/mustermann/ast/parser.rb +137 -0
  19. data/lib/mustermann/ast/pattern.rb +84 -0
  20. data/lib/mustermann/ast/transformer.rb +129 -0
  21. data/lib/mustermann/ast/translator.rb +108 -0
  22. data/lib/mustermann/ast/tree_renderer.rb +29 -0
  23. data/lib/mustermann/ast/validation.rb +43 -0
  24. data/lib/mustermann/caster.rb +117 -0
  25. data/lib/mustermann/equality_map.rb +48 -0
  26. data/lib/mustermann/error.rb +6 -0
  27. data/lib/mustermann/expander.rb +206 -0
  28. data/lib/mustermann/extension.rb +52 -0
  29. data/lib/mustermann/identity.rb +19 -0
  30. data/lib/mustermann/mapper.rb +98 -0
  31. data/lib/mustermann/pattern.rb +182 -0
  32. data/lib/mustermann/rails.rb +17 -0
  33. data/lib/mustermann/regexp_based.rb +30 -0
  34. data/lib/mustermann/regular.rb +26 -0
  35. data/lib/mustermann/router.rb +9 -0
  36. data/lib/mustermann/router/rack.rb +50 -0
  37. data/lib/mustermann/router/simple.rb +144 -0
  38. data/lib/mustermann/shell.rb +29 -0
  39. data/lib/mustermann/simple.rb +38 -0
  40. data/lib/mustermann/simple_match.rb +30 -0
  41. data/lib/mustermann/sinatra.rb +22 -0
  42. data/lib/mustermann/template.rb +48 -0
  43. data/lib/mustermann/to_pattern.rb +45 -0
  44. data/lib/mustermann/version.rb +3 -0
  45. data/mustermann.gemspec +31 -0
  46. data/spec/expander_spec.rb +105 -0
  47. data/spec/extension_spec.rb +296 -0
  48. data/spec/identity_spec.rb +83 -0
  49. data/spec/mapper_spec.rb +83 -0
  50. data/spec/mustermann_spec.rb +65 -0
  51. data/spec/pattern_spec.rb +49 -0
  52. data/spec/rails_spec.rb +522 -0
  53. data/spec/regexp_based_spec.rb +8 -0
  54. data/spec/regular_spec.rb +36 -0
  55. data/spec/router/rack_spec.rb +39 -0
  56. data/spec/router/simple_spec.rb +32 -0
  57. data/spec/shell_spec.rb +109 -0
  58. data/spec/simple_match_spec.rb +10 -0
  59. data/spec/simple_spec.rb +237 -0
  60. data/spec/sinatra_spec.rb +574 -0
  61. data/spec/support.rb +5 -0
  62. data/spec/support/coverage.rb +16 -0
  63. data/spec/support/env.rb +15 -0
  64. data/spec/support/expand_matcher.rb +27 -0
  65. data/spec/support/match_matcher.rb +39 -0
  66. data/spec/support/pattern.rb +39 -0
  67. data/spec/template_spec.rb +815 -0
  68. data/spec/to_pattern_spec.rb +20 -0
  69. 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
@@ -0,0 +1,3 @@
1
+ module Mustermann
2
+ VERSION ||= '0.3.1'
3
+ end
@@ -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