mustermann 1.0.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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