mustermann 1.0.3 → 3.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0f9565d31c5b29a990840eb04f933be370358de6
4
- data.tar.gz: 5f4dc020bd50b6d11f787dc555e0fcab6252d486
2
+ SHA256:
3
+ metadata.gz: ea330a8bf09165e676628df2668f0afa5d8fd32372bb7b07ed6a286cf274f74c
4
+ data.tar.gz: 58b0252ef3ba1d5bd06b3dceab50c06ba011d98b9e2bc54c6caa314443fa722b
5
5
  SHA512:
6
- metadata.gz: 7bf3d61ccac65e24d83b5551188ae5d9836b7664dad722fd077264d9b1b6e3097bc57b474c09acc3b250c009bd7852378b3049ee5630c28706335111ec651e60
7
- data.tar.gz: b4ab4965dd93dae14ddf44c60116711a488ead67e0bf1109582c79ed86bf81c70315c1b9b4b6258978093515f4813e8f01d0aeca302de883289d65ccbcf5e870
6
+ metadata.gz: f9e8638a1cb73cf39256c6cb422d4d1a4601a2d9348aba8361d31083103e0495a843d595b579f08d2614ba1b665b41959a79eaffd869348551a2cb558c1cf1ea
7
+ data.tar.gz: 5b569063e6dd7d3beb3bdd983d4f5c30a9d12ce7b5e06d0f1f215e56004ba678011df824d26a94d5dc136d957c7835e5bcc38081cbbda2d838cfe78b3ca00ec7
data/README.md CHANGED
@@ -377,30 +377,7 @@ mapper['/foo/bar'] # => "/foo/bar"
377
377
  <a name="-sinatra-integration"></a>
378
378
  ## Sinatra Integration
379
379
 
380
- All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
381
-
382
- ``` ruby
383
- require 'sinatra'
384
- require 'mustermann'
385
-
386
- get Mustermann.new('/:foo') do
387
- params[:foo]
388
- end
389
- ```
390
-
391
- In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
392
-
393
- ``` ruby
394
- require 'sinatra'
395
- require 'mustermann'
396
-
397
- register Mustermann
398
-
399
- # this will use Mustermann rather than the built-in pattern matching
400
- get '/:slug(.ext)?' do
401
- params[:slug]
402
- end
403
- ```
380
+ Mustermann is used in Sinatra by default since version 2.0, for previous versions an [extension](https://github.com/sinatra/mustermann-sinatra-extension) is available.
404
381
 
405
382
  ### Configuration
406
383
 
@@ -40,7 +40,7 @@ module Mustermann
40
40
 
41
41
  # @!visibility private
42
42
  def translate(**options)
43
- return pattern(options) if options[:no_captures]
43
+ return pattern(**options) if options[:no_captures]
44
44
  "(?<#{name}>#{translate(no_captures: true, **options)})"
45
45
  end
46
46
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mustermann/ast/translator'
3
3
  require 'mustermann/ast/compiler'
4
+ require 'ruby2_keywords'
4
5
 
5
6
  module Mustermann
6
7
  module AST
@@ -11,11 +12,11 @@ module Mustermann
11
12
  class Expander < Translator
12
13
  raises ExpandError
13
14
 
14
- translate Array do |*args|
15
+ translate(Array, &-> (*args) do
15
16
  inject(t.pattern) do |pattern, element|
16
17
  t.add_to(pattern, t(element, *args))
17
18
  end
18
- end
19
+ end.ruby2_keywords)
19
20
 
20
21
  translate :capture do |**options|
21
22
  t.for_capture(node, **options)
@@ -92,11 +93,11 @@ module Mustermann
92
93
  # @see Mustermann::Pattern#expand
93
94
  # @!visibility private
94
95
  def expand(values)
95
- values = values.each_with_object({}){ |(key, value), new_hash|
96
+ adjusted = values.each_with_object({}){ |(key, value), new_hash|
96
97
  new_hash[value.instance_of?(Array) ? [key] * value.length : key] = value }
97
- keys, pattern, filters = mappings.fetch(values.keys.flatten.sort) { error_for(values) }
98
- filters.each { |key, filter| values[key] &&= escape(values[key], also_escape: filter) }
99
- pattern % (values[keys] || values.values_at(*keys))
98
+ keys, pattern, filters = mappings.fetch(adjusted.keys.flatten.sort) { error_for(values) }
99
+ filters.each { |key, filter| adjusted[key] &&= escape(adjusted[key], also_escape: filter) }
100
+ pattern % (adjusted[keys] || adjusted.values_at(*keys))
100
101
  end
101
102
 
102
103
  # @see Mustermann::Pattern#expandable?
@@ -122,7 +123,9 @@ module Mustermann
122
123
 
123
124
  # @see Mustermann::AST::Translator#expand
124
125
  # @!visibility private
125
- def escape(string, *args)
126
+ ruby2_keywords def escape(string, *args)
127
+ return super unless string.respond_to?(:=~)
128
+
126
129
  # URI::Parser is pretty slow, let's not send every string to it, even if it's unnecessary
127
130
  string =~ /\A\w*\Z/ ? string : super
128
131
  end
@@ -137,7 +140,7 @@ module Mustermann
137
140
  # @!visibility private
138
141
  def add_to(list, result)
139
142
  list << [[], ""] if list.empty?
140
- list.inject([]) { |l, (k1, p1, f1)| l + result.map { |k2, p2, f2| [k1+k2, p1+p2, **f1, **f2] } }
143
+ list.inject([]) { |l, (k1, p1, f1)| l + result.map { |k2, p2, f2| [k1+k2, p1+p2, f1.merge(f2)] } }
141
144
  end
142
145
  end
143
146
  end
@@ -35,8 +35,8 @@ module Mustermann
35
35
  # Helper for creating a new instance and calling #parse on it.
36
36
  # @return [Mustermann::AST::Node]
37
37
  # @!visibility private
38
- def self.parse(*args, &block)
39
- new(*args).tap { |n| n.parse(&block) }
38
+ def self.parse(payload = nil, **options, &block)
39
+ new(payload, **options).tap { |n| n.parse(&block) }
40
40
  end
41
41
 
42
42
  # @!visibility private
@@ -108,7 +108,7 @@ module Mustermann
108
108
  # @see Mustermann::AST::Node#parse
109
109
  # @!visibility private
110
110
  def parse
111
- self.payload ||= ""
111
+ self.payload ||= String.new
112
112
  super
113
113
  end
114
114
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mustermann/ast/node'
3
3
  require 'forwardable'
4
+ require 'ruby2_keywords'
4
5
  require 'strscan'
5
6
 
6
7
  module Mustermann
@@ -64,7 +65,7 @@ module Mustermann
64
65
  # @param [Symbol] type node type
65
66
  # @return [Mustermann::AST::Node]
66
67
  # @!visibility private
67
- def node(type, *args, &block)
68
+ ruby2_keywords def node(type, *args, &block)
68
69
  type = Node[type] unless type.respond_to? :new
69
70
  start = pos
70
71
  node = block ? type.parse(*args, &block) : type.new(*args)
@@ -153,7 +154,7 @@ module Mustermann
153
154
  def read_brackets(open, close, char: nil, escape: ?\\, quote: false, **options)
154
155
  result = String.new
155
156
  escape = false if escape.nil?
156
- while current = getch
157
+ while (current = getch)
157
158
  case current
158
159
  when close then return result
159
160
  when open then result << open << read_brackets(open, close) << close
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mustermann/ast/node'
3
3
  require 'mustermann/error'
4
+ require 'ruby2_keywords'
4
5
  require 'delegate'
5
6
 
6
7
  module Mustermann
@@ -36,7 +37,7 @@ module Mustermann
36
37
 
37
38
  # shorthand for translating a nested object
38
39
  # @!visibility private
39
- def t(*args, &block)
40
+ ruby2_keywords def t(*args, &block)
40
41
  return translator unless args.any?
41
42
  translator.translate(*args, &block)
42
43
  end
@@ -109,7 +110,7 @@ module Mustermann
109
110
 
110
111
  # Start the translation dance for a (sub)tree.
111
112
  # @!visibility private
112
- def translate(node, *args, &block)
113
+ ruby2_keywords def translate(node, *args, &block)
113
114
  result = decorator_for(node).translate(*args, &block)
114
115
  result = result.node while result.is_a? NodeTranslator
115
116
  result
@@ -119,7 +120,7 @@ module Mustermann
119
120
  # @!visibility private
120
121
  def escape(char, parser: URI::DEFAULT_PARSER, escape: parser.regexp[:UNSAFE], also_escape: nil)
121
122
  escape = Regexp.union(also_escape, escape) if also_escape
122
- char =~ escape ? parser.escape(char, Regexp.union(*escape)) : char
123
+ char.to_s =~ escape ? parser.escape(char, Regexp.union(*escape)) : char
123
124
  end
124
125
  end
125
126
  end
@@ -102,9 +102,9 @@ module Mustermann
102
102
  end
103
103
 
104
104
  # @!visibility private
105
- def patterns_from(pattern, options = nil)
105
+ def patterns_from(pattern, **options)
106
106
  return pattern.patterns if pattern.is_a? Composite and pattern.operator == self.operator
107
- [options ? Mustermann.new(pattern, **options) : pattern]
107
+ [options.empty? && pattern.is_a?(Pattern) ? pattern : Mustermann.new(pattern, **options)]
108
108
  end
109
109
 
110
110
  private :with_matching, :patterns_from
@@ -40,7 +40,7 @@ module Mustermann
40
40
 
41
41
  # Should not be used directly.
42
42
  # @!visibility private
43
- def initialize(*)
43
+ def initialize(*, **)
44
44
  super
45
45
  AST::Validation.validate(combined_ast) if respond_to? :expand
46
46
  end
@@ -1,50 +1,3 @@
1
1
  # frozen_string_literal: true
2
- require 'sinatra/version'
3
- fail "no need to load the Mustermann extension for #{::Sinatra::VERSION}" if ::Sinatra::VERSION >= '2.0.0'
4
2
 
5
- require 'mustermann'
6
-
7
- module Mustermann
8
- # Sinatra 1.x extension switching default pattern parsing over to Mustermann.
9
- #
10
- # @example With classic Sinatra application
11
- # require 'sinatra'
12
- # require 'mustermann'
13
- #
14
- # register Mustermann
15
- # get('/:id', capture: /\d+/) { ... }
16
- #
17
- # @example With modular Sinatra application
18
- # require 'sinatra/base'
19
- # require 'mustermann'
20
- #
21
- # class MyApp < Sinatra::Base
22
- # register Mustermann
23
- # get('/:id', capture: /\d+/) { ... }
24
- # end
25
- #
26
- # @see file:README.md#Sinatra_Integration "Sinatra Integration" in the README
27
- module Extension
28
- def compile!(verb, path, block, except: nil, capture: nil, pattern: { }, **options)
29
- if path.respond_to? :to_str
30
- pattern[:except] = except if except
31
- pattern[:capture] = capture if capture
32
-
33
- if settings.respond_to? :pattern and settings.pattern?
34
- pattern.merge! settings.pattern do |key, local, global|
35
- next local unless local.is_a? Hash
36
- next global.merge(local) if global.is_a? Hash
37
- Hash.new(global).merge! local
38
- end
39
- end
40
-
41
- path = Mustermann.new(path, **pattern)
42
- condition { params.merge! path.params(captures: Array(params[:captures]), offset: -1) }
43
- end
44
-
45
- super(verb, path, block, options)
46
- end
47
-
48
- private :compile!
49
- end
50
- end
3
+ fail "Mustermann extension for Sinatra has been extracted into its own gem. More information at https://github.com/sinatra/mustermann-sinatra-extension"
@@ -40,11 +40,7 @@ module Mustermann
40
40
  #
41
41
  # @example map before options
42
42
  # require 'mustermann/mapper'
43
- # Mustermann::Mapper.new("/:foo" => "/:foo.html", type: :rails)
44
- #
45
- # @example map after options
46
- # require 'mustermann/mapper'
47
- # Mustermann::Mapper.new(type: :rails, "/:foo" => "/:foo.html")
43
+ # Mustermann::Mapper.new({"/:foo" => "/:foo.html"}, type: :rails)
48
44
  def initialize(map = {}, additional_values: :ignore, **options, &block)
49
45
  @map = []
50
46
  @options = options
@@ -56,7 +56,7 @@ module Mustermann
56
56
  end
57
57
 
58
58
  @map ||= EqualityMap.new
59
- @map.fetch([string, options]) { super(string, options) { options } }
59
+ @map.fetch([string, options]) { super(string, **options) { options } }
60
60
  end
61
61
 
62
62
  supported_options :uri_decode, :ignore_unknown_options
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Mustermann
3
- VERSION ||= '1.0.3'
3
+ VERSION ||= '3.0.0'
4
4
  end
data/lib/mustermann.rb CHANGED
@@ -75,8 +75,8 @@ module Mustermann
75
75
  end
76
76
  end
77
77
 
78
- @mutex ||= Mutex.new
79
- @types ||= {}
78
+ @mutex = Mutex.new
79
+ @types = {}
80
80
 
81
81
  # Maps a type to its factory.
82
82
  #
@@ -120,7 +120,6 @@ module Mustermann
120
120
  def self.extend_object(object)
121
121
  return super unless defined? ::Sinatra::Base and object.is_a? Class and object < ::Sinatra::Base
122
122
  require 'mustermann/extension'
123
- object.register Extension
124
123
  end
125
124
  end
126
125
 
data/mustermann.gemspec CHANGED
@@ -10,8 +10,9 @@ Gem::Specification.new do |s|
10
10
  s.summary = %q{Your personal string matching expert.}
11
11
  s.description = %q{A library implementing patterns that behave like regular expressions.}
12
12
  s.license = 'MIT'
13
- s.required_ruby_version = '>= 2.2.0'
13
+ s.required_ruby_version = '>= 2.6.0'
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+
17
+ s.add_runtime_dependency('ruby2_keywords', '~> 0.0.1')
17
18
  end
@@ -64,6 +64,8 @@ describe Mustermann::Expander do
64
64
  example { expander.expand(a: ?a).should be == '/a' }
65
65
  example { expander.expand(a: ?a, b: ?b).should be == '/a' }
66
66
  example { expect { expander.expand(b: ?b) }.to raise_error(Mustermann::ExpandError) }
67
+ example { expect { expander.expand(b: ?b, c: []) }.to raise_error(Mustermann::ExpandError) }
68
+ example { expect { expander.expand(b: ?b, c: [], d: ?d) }.to raise_error(Mustermann::ExpandError) }
67
69
  end
68
70
 
69
71
  context :append do
data/spec/mapper_spec.rb CHANGED
@@ -19,21 +19,14 @@ describe Mustermann::Mapper do
19
19
  end
20
20
 
21
21
  context 'accepts mappings followed by options' do
22
- subject(:mapper) { Mustermann::Mapper.new("/foo" => "/bar", additional_values: :raise) }
23
- its(:to_h) { should be == { Mustermann.new("/foo") => Mustermann::Expander.new("/bar") } }
24
- example { mapper['/foo'].should be == '/bar' }
25
- example { mapper['/fox'].should be == '/fox' }
26
- end
27
-
28
- context 'accepts options followed by mappings' do
29
- subject(:mapper) { Mustermann::Mapper.new(additional_values: :raise, "/foo" => "/bar") }
22
+ subject(:mapper) { Mustermann::Mapper.new({ "/foo" => "/bar" }, additional_values: :raise) }
30
23
  its(:to_h) { should be == { Mustermann.new("/foo") => Mustermann::Expander.new("/bar") } }
31
24
  example { mapper['/foo'].should be == '/bar' }
32
25
  example { mapper['/fox'].should be == '/fox' }
33
26
  end
34
27
 
35
28
  context 'allows specifying type' do
36
- subject(:mapper) { Mustermann::Mapper.new(additional_values: :raise, type: :rails, "/foo" => "/bar") }
29
+ subject(:mapper) { Mustermann::Mapper.new({ "/foo" => "/bar" }, additional_values: :raise, type: :rails) }
37
30
  its(:to_h) { should be == { Mustermann.new("/foo", type: :rails) => Mustermann::Expander.new("/bar", type: :rails) } }
38
31
  example { mapper['/foo'].should be == '/bar' }
39
32
  example { mapper['/fox'].should be == '/fox' }
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'support'
3
3
  require 'mustermann'
4
- require 'mustermann/extension'
5
4
  require 'sinatra/base'
6
5
 
7
6
  describe Mustermann do
@@ -70,11 +69,9 @@ describe Mustermann do
70
69
  describe :extend_object do
71
70
  context 'special behavior for Sinatra only' do
72
71
  example { Object .new.extend(Mustermann).should be_a(Mustermann) }
73
- example { Object .new.extend(Mustermann).should_not be_a(Mustermann::Extension) }
74
72
  example { Class .new.extend(Mustermann).should be_a(Mustermann) }
75
- example { Class .new.extend(Mustermann).should_not be_a(Mustermann::Extension) }
76
- example { Sinatra .new.extend(Mustermann).should_not be_a(Mustermann) }
77
- example { Sinatra .new.extend(Mustermann).should be_a(Mustermann::Extension) }
73
+
74
+ example { expect { Sinatra.new.extend(Mustermann) }.to raise_error(RuntimeError, "Mustermann extension for Sinatra has been extracted into its own gem. More information at https://github.com/sinatra/mustermann-sinatra-extension") }
78
75
  end
79
76
  end
80
77
 
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mustermann
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
8
8
  - Zachary Scott
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-08-16 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2022-07-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby2_keywords
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.0.1
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.0.1
14
28
  description: A library implementing patterns that behave like regular expressions.
15
29
  email: sinatrarb@googlegroups.com
16
30
  executables: []
@@ -62,7 +76,6 @@ files:
62
76
  - spec/concat_spec.rb
63
77
  - spec/equality_map_spec.rb
64
78
  - spec/expander_spec.rb
65
- - spec/extension_spec.rb
66
79
  - spec/identity_spec.rb
67
80
  - spec/mapper_spec.rb
68
81
  - spec/mustermann_spec.rb
@@ -76,7 +89,7 @@ homepage: https://github.com/sinatra/mustermann
76
89
  licenses:
77
90
  - MIT
78
91
  metadata: {}
79
- post_install_message:
92
+ post_install_message:
80
93
  rdoc_options: []
81
94
  require_paths:
82
95
  - lib
@@ -84,16 +97,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
97
  requirements:
85
98
  - - ">="
86
99
  - !ruby/object:Gem::Version
87
- version: 2.2.0
100
+ version: 2.6.0
88
101
  required_rubygems_version: !ruby/object:Gem::Requirement
89
102
  requirements:
90
103
  - - ">="
91
104
  - !ruby/object:Gem::Version
92
105
  version: '0'
93
106
  requirements: []
94
- rubyforge_project:
95
- rubygems_version: 2.6.8
96
- signing_key:
107
+ rubygems_version: 3.0.3.1
108
+ signing_key:
97
109
  specification_version: 4
98
110
  summary: Your personal string matching expert.
99
111
  test_files:
@@ -102,7 +114,6 @@ test_files:
102
114
  - spec/concat_spec.rb
103
115
  - spec/equality_map_spec.rb
104
116
  - spec/expander_spec.rb
105
- - spec/extension_spec.rb
106
117
  - spec/identity_spec.rb
107
118
  - spec/mapper_spec.rb
108
119
  - spec/mustermann_spec.rb
@@ -1,297 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'support'
3
- require 'mustermann/extension'
4
- require 'sinatra/base'
5
- require 'rack/test'
6
-
7
- describe Mustermann::Extension do
8
- include Rack::Test::Methods
9
-
10
- subject :app do
11
- Sinatra.new do
12
- set :environment, :test
13
- register Mustermann
14
- end
15
- end
16
-
17
- it 'sets up the extension' do
18
- app.should be_a(Mustermann::Extension)
19
- end
20
-
21
- context 'uses Sinatra-style patterns by default' do
22
- before { app.get('/:slug(.:extension)?') { params[:slug] } }
23
- example { get('/foo') .body.should be == 'foo' }
24
- example { get('/foo.') .body.should be == 'foo.' }
25
- example { get('/foo.bar') .body.should be == 'foo' }
26
- example { get('/a%20b') .body.should be == 'a b' }
27
- end
28
-
29
- describe :except do
30
- before { app.get('/auth/*', except: '/auth/login') { 'ok' } }
31
- example { get('/auth/dunno').should be_ok }
32
- example { get('/auth/login').should_not be_ok }
33
- end
34
-
35
- describe :capture do
36
- context 'global' do
37
- before do
38
- app.set(:pattern, capture: { ext: %w[png jpg gif] })
39
- app.get('/:slug(.:ext)?') { params[:slug] }
40
- end
41
-
42
- example { get('/foo.bar').body.should be == 'foo.bar' }
43
- example { get('/foo.png').body.should be == 'foo' }
44
- end
45
-
46
- context 'route local' do
47
- before do
48
- app.set(:pattern, nil)
49
- app.get('/:id', capture: /\d+/) { 'ok' }
50
- end
51
-
52
- example { get('/42').should be_ok }
53
- example { get('/foo').should_not be_ok }
54
- end
55
-
56
- context 'global and route local' do
57
- context 'global is a hash' do
58
- before do
59
- app.set(:pattern, capture: { id: /\d+/ })
60
- app.get('/:id(.:ext)?', capture: { ext: 'png' }) { ?a }
61
- app.get('/:id', capture: { id: 'foo' }) { ?b }
62
- app.get('/:id', capture: :alpha) { ?c }
63
- end
64
-
65
- example { get('/20') .body.should be == ?a }
66
- example { get('/20.png') .body.should be == ?a }
67
- example { get('/foo') .body.should be == ?b }
68
- example { get('/bar') .body.should be == ?c }
69
- end
70
-
71
- context 'global is not a hash' do
72
- before do
73
- app.set(:pattern, capture: /\d+/)
74
- app.get('/:slug(.:ext)?', capture: { ext: 'png' }) { params[:slug] }
75
- app.get('/:slug', capture: :alpha) { 'ok' }
76
- end
77
-
78
- example { get('/20.png').should be_ok }
79
- example { get('/foo.png').should_not be_ok }
80
- example { get('/foo').should be_ok }
81
-
82
- example { get('/20.png') .body.should be == '20' }
83
- example { get('/42') .body.should be == '42' }
84
- example { get('/foo') .body.should be == 'ok' }
85
- end
86
- end
87
- end
88
-
89
- describe :pattern do
90
- describe :except do
91
- before { app.get('/auth/*', pattern: { except: '/auth/login' }) { 'ok' } }
92
- example { get('/auth/dunno').should be_ok }
93
- example { get('/auth/login').should_not be_ok }
94
- end
95
-
96
- describe :capture do
97
- context 'route local' do
98
- before do
99
- app.set(:pattern, nil)
100
- app.get('/:id', pattern: { capture: /\d+/ }) { 'ok' }
101
- end
102
-
103
- example { get('/42').should be_ok }
104
- example { get('/foo').should_not be_ok }
105
- end
106
-
107
- context 'global and route local' do
108
- context 'global is a hash' do
109
- before do
110
- app.set(:pattern, capture: { id: /\d+/ })
111
- app.get('/:id(.:ext)?', pattern: { capture: { ext: 'png' }}) { ?a }
112
- app.get('/:id', pattern: { capture: { id: 'foo' }}) { ?b }
113
- app.get('/:id', pattern: { capture: :alpha }) { ?c }
114
- end
115
-
116
- example { get('/20') .body.should be == ?a }
117
- example { get('/20.png') .body.should be == ?a }
118
- example { get('/foo') .body.should be == ?b }
119
- example { get('/bar') .body.should be == ?c }
120
- end
121
-
122
- context 'global is not a hash' do
123
- before do
124
- app.set(:pattern, capture: /\d+/)
125
- app.get('/:slug(.:ext)?', pattern: { capture: { ext: 'png' }}) { params[:slug] }
126
- app.get('/:slug', pattern: { capture: :alpha }) { 'ok' }
127
- end
128
-
129
- example { get('/20.png').should be_ok }
130
- example { get('/foo.png').should_not be_ok }
131
- example { get('/foo').should be_ok }
132
-
133
- example { get('/20.png') .body.should be == '20' }
134
- example { get('/42') .body.should be == '42' }
135
- example { get('/foo') .body.should be == 'ok' }
136
- end
137
- end
138
- end
139
-
140
- describe :greedy do
141
- context 'default' do
142
- before { app.get('/:name.:ext') { params[:name] }}
143
- example { get('/foo.bar') .body.should be == 'foo' }
144
- example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
145
- end
146
-
147
- context 'enabled' do
148
- before { app.get('/:name.:ext', pattern: { greedy: true }) { params[:name] }}
149
- example { get('/foo.bar') .body.should be == 'foo' }
150
- example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
151
- end
152
-
153
- context 'disabled' do
154
- before { app.get('/:name.:ext', pattern: { greedy: false }) { params[:name] }}
155
- example { get('/foo.bar') .body.should be == 'foo' }
156
- example { get('/foo.bar.baz') .body.should be == 'foo' }
157
- end
158
-
159
- context 'global' do
160
- before do
161
- app.set(:pattern, greedy: false)
162
- app.get('/:name.:ext') { params[:name] }
163
- end
164
-
165
- example { get('/foo.bar') .body.should be == 'foo' }
166
- example { get('/foo.bar.baz') .body.should be == 'foo' }
167
- end
168
- end
169
-
170
- describe :space_matches_plus do
171
- context 'default' do
172
- before { app.get('/foo bar') { 'ok' }}
173
- example { get('/foo%20bar') .should be_ok }
174
- example { get('/foo+bar') .should be_ok }
175
- end
176
-
177
- context 'enabled' do
178
- before { app.get('/foo bar', pattern: { space_matches_plus: true }) { 'ok' }}
179
- example { get('/foo%20bar') .should be_ok }
180
- example { get('/foo+bar') .should be_ok }
181
- end
182
-
183
- context 'disabled' do
184
- before { app.get('/foo bar', pattern: { space_matches_plus: false }) { 'ok' }}
185
- example { get('/foo%20bar') .should be_ok }
186
- example { get('/foo+bar') .should_not be_ok }
187
- end
188
-
189
- context 'global' do
190
- before do
191
- app.set(:pattern, space_matches_plus: false)
192
- app.get('/foo bar') { 'ok' }
193
- end
194
-
195
- example { get('/foo%20bar') .should be_ok }
196
- example { get('/foo+bar') .should_not be_ok }
197
- end
198
- end
199
-
200
- describe :uri_decode do
201
- context 'default' do
202
- before { app.get('/&') { 'ok' }}
203
- example { get('/&') .should be_ok }
204
- example { get('/%26') .should be_ok }
205
- end
206
-
207
- context 'enabled' do
208
- before { app.get('/&', pattern: { uri_decode: true }) { 'ok' }}
209
- example { get('/&') .should be_ok }
210
- example { get('/%26') .should be_ok }
211
- end
212
-
213
- context 'disabled' do
214
- before { app.get('/&', pattern: { uri_decode: false }) { 'ok' }}
215
- example { get('/&') .should be_ok }
216
- example { get('/%26') .should_not be_ok }
217
- end
218
-
219
- context 'global' do
220
- before do
221
- app.set(:pattern, uri_decode: false)
222
- app.get('/&') { 'ok' }
223
- end
224
-
225
- example { get('/&') .should be_ok }
226
- example { get('/%26') .should_not be_ok }
227
- end
228
- end
229
- end
230
-
231
- describe :type do
232
- describe :identity do
233
- before do
234
- app.set(:pattern, type: :identity)
235
- app.get('/:foo') { 'ok' }
236
- end
237
-
238
- example { get('/:foo').should be_ok }
239
- example { get('/foo').should_not be_ok }
240
- end
241
-
242
- describe :rails do
243
- before do
244
- app.set(:pattern, type: :rails)
245
- app.get('/:slug(.:extension)') { params[:slug] }
246
- end
247
-
248
- example { get('/foo') .body.should be == 'foo' }
249
- example { get('/foo.') .body.should be == 'foo.' }
250
- example { get('/foo.bar') .body.should be == 'foo' }
251
- example { get('/a%20b') .body.should be == 'a b' }
252
- end
253
-
254
- describe :shell do
255
- before do
256
- app.set(:pattern, type: :shell)
257
- app.get('/{foo,bar}') { 'ok' }
258
- end
259
-
260
- example { get('/foo').should be_ok }
261
- example { get('/bar').should be_ok }
262
- end
263
-
264
- describe :simple do
265
- before do
266
- app.set(:pattern, type: :simple)
267
- app.get('/(a)') { 'ok' }
268
- end
269
-
270
- example { get('/(a)').should be_ok }
271
- example { get('/a').should_not be_ok }
272
- end
273
-
274
- describe :simple do
275
- before do
276
- app.set(:pattern, type: :template)
277
- app.get('/foo{/segments*}{.ext}') { "%p %p" % [params[:segments], params[:ext]] }
278
- end
279
-
280
- example { get('/foo/a.png').should be_ok }
281
- example { get('/foo/a').should_not be_ok }
282
-
283
- example { get('/foo/a.png').body.should be == '["a"] "png"' }
284
- example { get('/foo/a/b.png').body.should be == '["a", "b"] "png"' }
285
- end
286
- end
287
-
288
- context 'works with filters' do
289
- before do
290
- app.before('/auth/*', except: '/auth/login') { halt 'auth required' }
291
- app.get('/auth/login') { 'please log in' }
292
- end
293
-
294
- example { get('/auth/dunno').body.should be == 'auth required' }
295
- example { get('/auth/login').body.should be == 'please log in' }
296
- end
297
- end