mustermann-rails 0.4.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 +7 -0
- data/README.md +103 -0
- data/lib/mustermann/rails.rb +43 -0
- data/lib/mustermann/versions.rb +46 -0
- data/mustermann-rails.gemspec +18 -0
- data/spec/rails_spec.rb +640 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8a27c18c01c4a2db8f22534ae77959dead448a8d
|
4
|
+
data.tar.gz: fa2947e67a353ffec312ab8f8524c036b00558dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84a6f98b7795648592d926b759a92516e383778c479672f4754bcc6a37e3dae73e45435c4bbad08276cc4688cf87abd268c6e9621b48070f783ab58add2e4a68
|
7
|
+
data.tar.gz: 659bf9f1296199b8f152599228ceec43ce9a28a08177af7a720816acb5e2be8cdd5f8aaa2257d8dda1f41ff6c602bc6599f9541bd25f77d69bf0e814c5445eba
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Rails Syntax for Mustermann
|
2
|
+
|
3
|
+
This gem implements the `rails` pattern type for Mustermann. It is compatible with [Ruby on Rails](http://rubyonrails.org/), [Journey](https://github.com/rails/journey), the [http_router gem](https://github.com/joshbuddy/http_router), [Lotus](http://lotusrb.org/) and [Scalatra](http://www.scalatra.org/) (if [configured](http://www.scalatra.org/2.3/guides/http/routes.html#toc_248))</td>
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
**Supported options:**
|
8
|
+
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, `version`, and `ignore_unknown_options`.
|
9
|
+
|
10
|
+
**External documentation:**
|
11
|
+
[Ruby on Rails Guides: Routing](http://guides.rubyonrails.org/routing.html).
|
12
|
+
|
13
|
+
``` ruby
|
14
|
+
require 'mustermann'
|
15
|
+
|
16
|
+
pattern = Mustermann.new('/:example', type: :rails)
|
17
|
+
pattern === "/foo.bar" # => true
|
18
|
+
pattern === "/foo/bar" # => false
|
19
|
+
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
|
20
|
+
pattern.params("/foo/bar") # => nil
|
21
|
+
|
22
|
+
pattern = Mustermann.new('/:example(/:optional)', type: :rails)
|
23
|
+
pattern === "/foo.bar" # => true
|
24
|
+
pattern === "/foo/bar" # => true
|
25
|
+
pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
|
26
|
+
pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
|
27
|
+
|
28
|
+
pattern = Mustermann.new('/*example', type: :rails)
|
29
|
+
pattern === "/foo.bar" # => true
|
30
|
+
pattern === "/foo/bar" # => true
|
31
|
+
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
|
32
|
+
pattern.params("/foo/bar") # => { "example" => "foo/bar" }
|
33
|
+
```
|
34
|
+
|
35
|
+
## Rails Compatibility
|
36
|
+
|
37
|
+
Rails syntax changed over time. You can target different Ruby on Rails versions by setting the `version` option to the desired Rails version.
|
38
|
+
|
39
|
+
The default is `4.2`. Versions prior to `2.3` are not supported.
|
40
|
+
|
41
|
+
``` ruby
|
42
|
+
require 'mustermann'
|
43
|
+
Mustermann.new('/', type: :rails, version: "2.3")
|
44
|
+
Mustermann.new('/', type: :rails, version: "3.0.0")
|
45
|
+
|
46
|
+
require 'rails'
|
47
|
+
Mustermann.new('/', type: :rails, version: Rails::VERSION::STRING)
|
48
|
+
```
|
49
|
+
|
50
|
+
## Syntax
|
51
|
+
|
52
|
+
<table>
|
53
|
+
<thead>
|
54
|
+
<tr>
|
55
|
+
<th>Syntax Element</th>
|
56
|
+
<th>Description</th>
|
57
|
+
</tr>
|
58
|
+
</thead>
|
59
|
+
<tbody>
|
60
|
+
<tr>
|
61
|
+
<td><b>:</b><i>name</i></td>
|
62
|
+
<td>
|
63
|
+
Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
|
64
|
+
Capture behavior can be modified with tt>capture</tt> and <tt>greedy</tt> option.
|
65
|
+
</td>
|
66
|
+
</tr>
|
67
|
+
<tr>
|
68
|
+
<td><b>*</b><i>name</i></td>
|
69
|
+
<td>
|
70
|
+
Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
|
71
|
+
</td>
|
72
|
+
</tr>
|
73
|
+
<tr>
|
74
|
+
<td><b>(</b><i>expression</i><b>)</b></td>
|
75
|
+
<td>Enclosed <i>expression</i> is optional. Not available in 2.3 compatibility mode.</td>
|
76
|
+
</tr>
|
77
|
+
<tr>
|
78
|
+
<td><b>/</b></td>
|
79
|
+
<td>
|
80
|
+
Matches forward slash. Does not match URI encoded version of forward slash.
|
81
|
+
</td>
|
82
|
+
</tr>
|
83
|
+
<tr>
|
84
|
+
<td><b>\</b><i>x</i></td>
|
85
|
+
<td>
|
86
|
+
In 3.x compatibility mode and starting with 4.2:
|
87
|
+
Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.<br>
|
88
|
+
In 4.0 or 4.1 compatibility mode:
|
89
|
+
<b>\</b> is ignored, <i>x</i> is parsed normally.<br>
|
90
|
+
</td>
|
91
|
+
</tr>
|
92
|
+
<tr>
|
93
|
+
<td><b>|</b></td>
|
94
|
+
<td>
|
95
|
+
Starting with 3.2 compatibility mode, this will raise a `Mustermann::ParseError`. While Ruby on Rails happily parses this character, it will result in broken routes due to a buggy implementation.
|
96
|
+
</td>
|
97
|
+
</tr>
|
98
|
+
<tr>
|
99
|
+
<td><i>any other character</i></td>
|
100
|
+
<td>Matches exactly that character or a URI encoded version of it.</td>
|
101
|
+
</tr>
|
102
|
+
</tbody>
|
103
|
+
</table>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
require 'mustermann/ast/pattern'
|
3
|
+
require 'mustermann/versions'
|
4
|
+
|
5
|
+
module Mustermann
|
6
|
+
# Rails style pattern implementation.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Mustermann.new('/:foo', type: :rails) === '/bar' # => true
|
10
|
+
#
|
11
|
+
# @see Mustermann::Pattern
|
12
|
+
# @see file:README.md#rails Syntax description in the README
|
13
|
+
class Rails < AST::Pattern
|
14
|
+
extend Versions
|
15
|
+
register :rails
|
16
|
+
|
17
|
+
# first parser, no optional parts
|
18
|
+
version('2.3') do
|
19
|
+
on(nil) { |c| unexpected(c) }
|
20
|
+
on(?*) { |c| node(:named_splat) { scan(/\w+/) } }
|
21
|
+
on(?:) { |c| node(:capture) { scan(/\w+/) } }
|
22
|
+
end
|
23
|
+
|
24
|
+
# rack-mount
|
25
|
+
version('3.0', '3.1') do
|
26
|
+
on(?)) { |c| unexpected(c) }
|
27
|
+
on(?() { |c| node(:optional, node(:group) { read unless scan(?)) }) }
|
28
|
+
on(?\\) { |c| node(:char, expect(/./)) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# stand-alone journey
|
32
|
+
version('3.2') do
|
33
|
+
on(?|) { |c| raise ParseError, "the implementation of | is broken in ActionDispatch, cannot compile compatible pattern" }
|
34
|
+
on(?\\) { |c| node(:char, c) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# embedded journey, broken (ignored) escapes
|
38
|
+
version('4.0', '4.1') { on(?\\) { |c| read } }
|
39
|
+
|
40
|
+
# escapes got fixed in 4.2
|
41
|
+
version('4.2') { on(?\\) { |c| node(:char, expect(/./)) } }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Mustermann
|
2
|
+
# Mixin that adds support for multiple versions of the same type.
|
3
|
+
# @see Mustermann::Rails
|
4
|
+
# @!visibility private
|
5
|
+
module Versions
|
6
|
+
# Checks if class has mulitple versions available and picks one that matches the version option.
|
7
|
+
# @!visibility private
|
8
|
+
def new(*args, version: nil, **options)
|
9
|
+
return super(*args, **options) unless versions.any?
|
10
|
+
self[version].new(*args, **options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Hash] version to subclass mapping.
|
14
|
+
# @!visibility private
|
15
|
+
def versions
|
16
|
+
@versions ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Defines a new version.
|
20
|
+
# @!visibility private
|
21
|
+
def version(*list, inherit_from: nil, &block)
|
22
|
+
superclass = self[inherit_from] || self
|
23
|
+
subclass = Class.new(superclass, &block)
|
24
|
+
list.each { |v| versions[v] = subclass }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Resolve a subclass for a given version string.
|
28
|
+
# @!visibility private
|
29
|
+
def [](version)
|
30
|
+
return versions.values.last unless version
|
31
|
+
detected = versions.detect { |v,_| version.start_with?(v) }
|
32
|
+
raise ArgumentError, 'unsupported version %p' % version unless detected
|
33
|
+
detected.last
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def name
|
38
|
+
super || superclass.name
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def inspect
|
43
|
+
name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift File.expand_path("../../mustermann/lib", __FILE__)
|
2
|
+
require "mustermann/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mustermann-rails"
|
6
|
+
s.version = Mustermann::VERSION
|
7
|
+
s.author = "Konstantin Haase"
|
8
|
+
s.email = "konstantin.mailinglists@googlemail.com"
|
9
|
+
s.homepage = "https://github.com/rkh/mustermann"
|
10
|
+
s.summary = %q{Rails syntax for Mustermann}
|
11
|
+
s.description = %q{Adds Rails style patterns to Mustermman}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.required_ruby_version = '>= 2.1.0'
|
14
|
+
s.files = `git ls-files`.split("\n")
|
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) }
|
17
|
+
s.add_dependency 'mustermann', Mustermann::VERSION
|
18
|
+
end
|
data/spec/rails_spec.rb
ADDED
@@ -0,0 +1,640 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/rails'
|
3
|
+
|
4
|
+
describe Mustermann::Rails do
|
5
|
+
extend Support::Pattern
|
6
|
+
|
7
|
+
pattern '' do
|
8
|
+
it { should match('') }
|
9
|
+
it { should_not match('/') }
|
10
|
+
|
11
|
+
it { should expand.to('') }
|
12
|
+
it { should_not expand(a: 1) }
|
13
|
+
|
14
|
+
it { should generate_template('') }
|
15
|
+
|
16
|
+
it { should respond_to(:expand) }
|
17
|
+
it { should respond_to(:to_templates) }
|
18
|
+
end
|
19
|
+
|
20
|
+
pattern '/' do
|
21
|
+
it { should match('/') }
|
22
|
+
it { should_not match('/foo') }
|
23
|
+
|
24
|
+
it { should expand.to('/') }
|
25
|
+
it { should_not expand(a: 1) }
|
26
|
+
end
|
27
|
+
|
28
|
+
pattern '/foo' do
|
29
|
+
it { should match('/foo') }
|
30
|
+
it { should_not match('/bar') }
|
31
|
+
it { should_not match('/foo.bar') }
|
32
|
+
|
33
|
+
it { should expand.to('/foo') }
|
34
|
+
it { should_not expand(a: 1) }
|
35
|
+
end
|
36
|
+
|
37
|
+
pattern '/foo/bar' do
|
38
|
+
it { should match('/foo/bar') }
|
39
|
+
it { should_not match('/foo%2Fbar') }
|
40
|
+
it { should_not match('/foo%2fbar') }
|
41
|
+
|
42
|
+
it { should expand.to('/foo/bar') }
|
43
|
+
it { should_not expand(a: 1) }
|
44
|
+
end
|
45
|
+
|
46
|
+
pattern '/:foo' do
|
47
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
48
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
49
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
50
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
51
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
52
|
+
|
53
|
+
it { should_not match('/foo?') }
|
54
|
+
it { should_not match('/foo/bar') }
|
55
|
+
it { should_not match('/') }
|
56
|
+
it { should_not match('/foo/') }
|
57
|
+
|
58
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
59
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
60
|
+
example { pattern.params('').should be_nil }
|
61
|
+
|
62
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
63
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
64
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
65
|
+
|
66
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
67
|
+
it { should_not expand(bar: 'bar') }
|
68
|
+
it { should_not expand }
|
69
|
+
|
70
|
+
it { should generate_template('/{foo}') }
|
71
|
+
end
|
72
|
+
|
73
|
+
pattern '/föö' do
|
74
|
+
it { should match("/f%C3%B6%C3%B6") }
|
75
|
+
it { should expand.to("/f%C3%B6%C3%B6") }
|
76
|
+
it { should_not expand(a: 1) }
|
77
|
+
end
|
78
|
+
|
79
|
+
pattern "/:foo/:bar" do
|
80
|
+
it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
|
81
|
+
it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
|
82
|
+
it { should match('/user@example.com/name') .capturing foo: 'user@example.com', bar: 'name' }
|
83
|
+
it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
|
84
|
+
it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
|
85
|
+
|
86
|
+
it { should_not match('/foo%2Fbar') }
|
87
|
+
it { should_not match('/foo%2fbar') }
|
88
|
+
|
89
|
+
example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
|
90
|
+
example { pattern.params('').should be_nil }
|
91
|
+
|
92
|
+
it { should expand(foo: 'foo', bar: 'bar').to('/foo/bar') }
|
93
|
+
it { should_not expand(foo: 'foo') }
|
94
|
+
it { should_not expand(bar: 'bar') }
|
95
|
+
|
96
|
+
it { should generate_template('/{foo}/{bar}') }
|
97
|
+
end
|
98
|
+
|
99
|
+
pattern '/hello/:person' do
|
100
|
+
it { should match('/hello/Frank').capturing person: 'Frank' }
|
101
|
+
it { should expand(person: 'Frank') .to '/hello/Frank' }
|
102
|
+
it { should expand(person: 'Frank?') .to '/hello/Frank%3F' }
|
103
|
+
|
104
|
+
it { should generate_template('/hello/{person}') }
|
105
|
+
end
|
106
|
+
|
107
|
+
pattern '/?:foo?/?:bar?' do
|
108
|
+
it { should match('/?hello?/?world?').capturing foo: 'hello', bar: 'world' }
|
109
|
+
it { should_not match('/hello/world/') }
|
110
|
+
it { should expand(foo: 'hello', bar: 'world').to('/%3Fhello%3F/%3Fworld%3F') }
|
111
|
+
it { should generate_template('/?{foo}?/?{bar}?') }
|
112
|
+
end
|
113
|
+
|
114
|
+
pattern '/:foo_bar' do
|
115
|
+
it { should match('/hello').capturing foo_bar: 'hello' }
|
116
|
+
it { should expand(foo_bar: 'hello').to('/hello') }
|
117
|
+
it { should generate_template('/{foo_bar}') }
|
118
|
+
end
|
119
|
+
|
120
|
+
pattern '/*foo' do
|
121
|
+
it { should match('/') .capturing foo: '' }
|
122
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
123
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
124
|
+
|
125
|
+
it { should expand .to('/') }
|
126
|
+
it { should expand(foo: nil) .to('/') }
|
127
|
+
it { should expand(foo: '') .to('/') }
|
128
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
129
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
130
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
131
|
+
|
132
|
+
it { should generate_template('/{+foo}') }
|
133
|
+
end
|
134
|
+
|
135
|
+
pattern '/*splat' do
|
136
|
+
it { should match('/') .capturing splat: '' }
|
137
|
+
it { should match('/foo') .capturing splat: 'foo' }
|
138
|
+
it { should match('/foo/bar') .capturing splat: 'foo/bar' }
|
139
|
+
it { should generate_template('/{+splat}') }
|
140
|
+
end
|
141
|
+
|
142
|
+
pattern '/:foo/*bar' do
|
143
|
+
it { should match("/foo/bar/baz") .capturing foo: 'foo', bar: 'bar/baz' }
|
144
|
+
it { should match("/foo%2Fbar/baz") .capturing foo: 'foo%2Fbar', bar: 'baz' }
|
145
|
+
it { should match("/foo/") .capturing foo: 'foo', bar: '' }
|
146
|
+
it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', bar: 'h%20a%20y' }
|
147
|
+
it { should_not match('/foo') }
|
148
|
+
|
149
|
+
it { should expand(foo: 'foo') .to('/foo/') }
|
150
|
+
it { should expand(foo: 'foo', bar: 'bar') .to('/foo/bar') }
|
151
|
+
it { should expand(foo: 'foo', bar: 'foo/bar') .to('/foo/foo/bar') }
|
152
|
+
it { should expand(foo: 'foo/bar', bar: 'bar') .to('/foo%2Fbar/bar') }
|
153
|
+
|
154
|
+
it { should generate_template('/{foo}/{+bar}') }
|
155
|
+
end
|
156
|
+
|
157
|
+
pattern '/test$/' do
|
158
|
+
it { should match('/test$/') }
|
159
|
+
it { should expand.to('/test$/') }
|
160
|
+
end
|
161
|
+
|
162
|
+
pattern '/te+st/' do
|
163
|
+
it { should match('/te+st/') }
|
164
|
+
it { should_not match('/test/') }
|
165
|
+
it { should_not match('/teest/') }
|
166
|
+
it { should expand.to('/te+st/') }
|
167
|
+
end
|
168
|
+
|
169
|
+
pattern "/path with spaces" do
|
170
|
+
it { should match('/path%20with%20spaces') }
|
171
|
+
it { should match('/path%2Bwith%2Bspaces') }
|
172
|
+
it { should match('/path+with+spaces') }
|
173
|
+
it { should expand.to('/path%20with%20spaces') }
|
174
|
+
|
175
|
+
it { should generate_template('/path%20with%20spaces') }
|
176
|
+
end
|
177
|
+
|
178
|
+
pattern '/foo&bar' do
|
179
|
+
it { should match('/foo&bar') }
|
180
|
+
end
|
181
|
+
|
182
|
+
pattern '/*a/:foo/*b/*c' do
|
183
|
+
it { should match('/bar/foo/bling/baz/boom').capturing a: 'bar', foo: 'foo', b: 'bling', c: 'baz/boom' }
|
184
|
+
example { pattern.params('/bar/foo/bling/baz/boom').should be == { "a" => 'bar', "foo" => 'foo', "b" => 'bling', "c" => 'baz/boom' } }
|
185
|
+
it { should expand(a: 'bar', foo: 'foo', b: 'bling', c: 'baz/boom').to('/bar/foo/bling/baz/boom') }
|
186
|
+
it { should generate_template('/{+a}/{foo}/{+b}/{+c}') }
|
187
|
+
end
|
188
|
+
|
189
|
+
pattern '/test.bar' do
|
190
|
+
it { should match('/test.bar') }
|
191
|
+
it { should_not match('/test0bar') }
|
192
|
+
end
|
193
|
+
|
194
|
+
pattern '/:file.:ext' do
|
195
|
+
it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
|
196
|
+
it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
|
197
|
+
it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
|
198
|
+
|
199
|
+
it { should match('/pony%E6%AD%A3%2Ejpg') .capturing file: 'pony%E6%AD%A3', ext: 'jpg' }
|
200
|
+
it { should match('/pony%e6%ad%a3%2ejpg') .capturing file: 'pony%e6%ad%a3', ext: 'jpg' }
|
201
|
+
it { should match('/pony正%2Ejpg') .capturing file: 'pony正', ext: 'jpg' }
|
202
|
+
it { should match('/pony正%2ejpg') .capturing file: 'pony正', ext: 'jpg' }
|
203
|
+
it { should match('/pony正..jpg') .capturing file: 'pony正.', ext: 'jpg' }
|
204
|
+
|
205
|
+
it { should_not match('/.jpg') }
|
206
|
+
it { should expand(file: 'pony', ext: 'jpg').to('/pony.jpg') }
|
207
|
+
end
|
208
|
+
|
209
|
+
pattern '/:a(x)' do
|
210
|
+
it { should match('/a') .capturing a: 'a' }
|
211
|
+
it { should match('/xa') .capturing a: 'xa' }
|
212
|
+
it { should match('/axa') .capturing a: 'axa' }
|
213
|
+
it { should match('/ax') .capturing a: 'a' }
|
214
|
+
it { should match('/axax') .capturing a: 'axa' }
|
215
|
+
it { should match('/axaxx') .capturing a: 'axax' }
|
216
|
+
it { should expand(a: 'x').to('/xx') }
|
217
|
+
it { should expand(a: 'a').to('/ax') }
|
218
|
+
|
219
|
+
it { should generate_template('/{a}x') }
|
220
|
+
it { should generate_template('/{a}') }
|
221
|
+
end
|
222
|
+
|
223
|
+
pattern '/:user(@:host)' do
|
224
|
+
it { should match('/foo@bar') .capturing user: 'foo', host: 'bar' }
|
225
|
+
it { should match('/foo.foo@bar') .capturing user: 'foo.foo', host: 'bar' }
|
226
|
+
it { should match('/foo@bar.bar') .capturing user: 'foo', host: 'bar.bar' }
|
227
|
+
|
228
|
+
it { should expand(user: 'foo') .to('/foo') }
|
229
|
+
it { should expand(user: 'foo', host: 'bar') .to('/foo@bar') }
|
230
|
+
|
231
|
+
it { should generate_template('/{user}') }
|
232
|
+
it { should generate_template('/{user}@{host}') }
|
233
|
+
end
|
234
|
+
|
235
|
+
pattern '/:file(.:ext)' do
|
236
|
+
it { should match('/pony') .capturing file: 'pony', ext: nil }
|
237
|
+
it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
|
238
|
+
it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
|
239
|
+
it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
|
240
|
+
it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: 'jpg' }
|
241
|
+
it { should match('/pony.') .capturing file: 'pony.' }
|
242
|
+
it { should_not match('/.jpg') }
|
243
|
+
|
244
|
+
it { should expand(file: 'pony') .to('/pony') }
|
245
|
+
it { should expand(file: 'pony', ext: 'jpg') .to('/pony.jpg') }
|
246
|
+
|
247
|
+
it { should generate_template('/{file}') }
|
248
|
+
it { should generate_template('/{file}.{ext}') }
|
249
|
+
end
|
250
|
+
|
251
|
+
pattern '/:id/test.bar' do
|
252
|
+
it { should match('/3/test.bar') .capturing id: '3' }
|
253
|
+
it { should match('/2/test.bar') .capturing id: '2' }
|
254
|
+
it { should match('/2E/test.bar') .capturing id: '2E' }
|
255
|
+
it { should match('/2e/test.bar') .capturing id: '2e' }
|
256
|
+
it { should match('/%2E/test.bar') .capturing id: '%2E' }
|
257
|
+
end
|
258
|
+
|
259
|
+
pattern '/10/:id' do
|
260
|
+
it { should match('/10/test') .capturing id: 'test' }
|
261
|
+
it { should match('/10/te.st') .capturing id: 'te.st' }
|
262
|
+
end
|
263
|
+
|
264
|
+
pattern '/10.1/:id' do
|
265
|
+
it { should match('/10.1/test') .capturing id: 'test' }
|
266
|
+
it { should match('/10.1/te.st') .capturing id: 'te.st' }
|
267
|
+
end
|
268
|
+
|
269
|
+
pattern '/:foo.:bar/:id' do
|
270
|
+
it { should match('/10.1/te.st') .capturing foo: "10", bar: "1", id: "te.st" }
|
271
|
+
it { should match('/10.1.2/te.st') .capturing foo: "10.1", bar: "2", id: "te.st" }
|
272
|
+
end
|
273
|
+
|
274
|
+
pattern '/:a/:b(.)(:c)' do
|
275
|
+
it { should match('/a/b') .capturing a: 'a', b: 'b', c: nil }
|
276
|
+
it { should match('/a/b.c') .capturing a: 'a', b: 'b', c: 'c' }
|
277
|
+
it { should match('/a.b/c') .capturing a: 'a.b', b: 'c', c: nil }
|
278
|
+
it { should match('/a.b/c.d') .capturing a: 'a.b', b: 'c', c: 'd' }
|
279
|
+
it { should_not match('/a.b/c.d/e') }
|
280
|
+
|
281
|
+
it { should expand(a: ?a, b: ?b) .to('/a/b.') }
|
282
|
+
it { should expand(a: ?a, b: ?b, c: ?c) .to('/a/b.c') }
|
283
|
+
|
284
|
+
it { should generate_template('/{a}/{b}') }
|
285
|
+
it { should generate_template('/{a}/{b}.') }
|
286
|
+
it { should generate_template('/{a}/{b}.{c}') }
|
287
|
+
end
|
288
|
+
|
289
|
+
pattern '/:a(foo:b)' do
|
290
|
+
it { should match('/barfoobar') .capturing a: 'bar', b: 'bar' }
|
291
|
+
it { should match('/barfoobarfoobar') .capturing a: 'barfoobar', b: 'bar' }
|
292
|
+
it { should match('/bar') .capturing a: 'bar', b: nil }
|
293
|
+
it { should_not match('/') }
|
294
|
+
|
295
|
+
it { should expand(a: ?a) .to('/a') }
|
296
|
+
it { should expand(a: ?a, b: ?b) .to('/afoob') }
|
297
|
+
|
298
|
+
it { should generate_template('/{a}foo{b}') }
|
299
|
+
it { should generate_template('/{a}') }
|
300
|
+
it { should_not generate_template('/{a}foo') }
|
301
|
+
end
|
302
|
+
|
303
|
+
pattern '/fo(o)' do
|
304
|
+
it { should match('/fo') }
|
305
|
+
it { should match('/foo') }
|
306
|
+
it { should_not match('') }
|
307
|
+
it { should_not match('/') }
|
308
|
+
it { should_not match('/f') }
|
309
|
+
it { should_not match('/fooo') }
|
310
|
+
|
311
|
+
it { should expand.to('/foo') }
|
312
|
+
end
|
313
|
+
|
314
|
+
pattern '/foo?' do
|
315
|
+
it { should match('/foo?') }
|
316
|
+
it { should_not match('/foo\?') }
|
317
|
+
it { should_not match('/fo') }
|
318
|
+
it { should_not match('/foo') }
|
319
|
+
it { should_not match('') }
|
320
|
+
it { should_not match('/') }
|
321
|
+
it { should_not match('/f') }
|
322
|
+
it { should_not match('/fooo') }
|
323
|
+
|
324
|
+
it { should expand.to('/foo%3F') }
|
325
|
+
end
|
326
|
+
|
327
|
+
pattern '/:fOO' do
|
328
|
+
it { should match('/a').capturing fOO: 'a' }
|
329
|
+
end
|
330
|
+
|
331
|
+
pattern '/:_X' do
|
332
|
+
it { should match('/a').capturing _X: 'a' }
|
333
|
+
end
|
334
|
+
|
335
|
+
pattern '/:f00' do
|
336
|
+
it { should match('/a').capturing f00: 'a' }
|
337
|
+
end
|
338
|
+
|
339
|
+
pattern '/:foo(/:bar)/:baz' do
|
340
|
+
it { should match('/foo/bar/baz').capturing foo: 'foo', bar: 'bar', baz: 'baz' }
|
341
|
+
it { should expand(foo: ?a, baz: ?b) .to('/a/b') }
|
342
|
+
it { should expand(foo: ?a, baz: ?b, bar: ?x) .to('/a/x/b') }
|
343
|
+
end
|
344
|
+
|
345
|
+
pattern '/:foo', capture: /\d+/ do
|
346
|
+
it { should match('/1') .capturing foo: '1' }
|
347
|
+
it { should match('/123') .capturing foo: '123' }
|
348
|
+
|
349
|
+
it { should_not match('/') }
|
350
|
+
it { should_not match('/foo') }
|
351
|
+
end
|
352
|
+
|
353
|
+
pattern '/:foo', capture: /\d+/ do
|
354
|
+
it { should match('/1') .capturing foo: '1' }
|
355
|
+
it { should match('/123') .capturing foo: '123' }
|
356
|
+
|
357
|
+
it { should_not match('/') }
|
358
|
+
it { should_not match('/foo') }
|
359
|
+
end
|
360
|
+
|
361
|
+
pattern '/:foo', capture: '1' do
|
362
|
+
it { should match('/1').capturing foo: '1' }
|
363
|
+
|
364
|
+
it { should_not match('/') }
|
365
|
+
it { should_not match('/foo') }
|
366
|
+
it { should_not match('/123') }
|
367
|
+
end
|
368
|
+
|
369
|
+
pattern '/:foo', capture: 'a.b' do
|
370
|
+
it { should match('/a.b') .capturing foo: 'a.b' }
|
371
|
+
it { should match('/a%2Eb') .capturing foo: 'a%2Eb' }
|
372
|
+
it { should match('/a%2eb') .capturing foo: 'a%2eb' }
|
373
|
+
|
374
|
+
it { should_not match('/ab') }
|
375
|
+
it { should_not match('/afb') }
|
376
|
+
it { should_not match('/a1b') }
|
377
|
+
it { should_not match('/a.bc') }
|
378
|
+
end
|
379
|
+
|
380
|
+
pattern '/:foo(/:bar)', capture: :alpha do
|
381
|
+
it { should match('/abc') .capturing foo: 'abc', bar: nil }
|
382
|
+
it { should match('/a/b') .capturing foo: 'a', bar: 'b' }
|
383
|
+
it { should match('/a') .capturing foo: 'a', bar: nil }
|
384
|
+
|
385
|
+
it { should_not match('/1/2') }
|
386
|
+
it { should_not match('/a/2') }
|
387
|
+
it { should_not match('/1/b') }
|
388
|
+
it { should_not match('/1') }
|
389
|
+
it { should_not match('/1/') }
|
390
|
+
it { should_not match('/a/') }
|
391
|
+
it { should_not match('//a') }
|
392
|
+
end
|
393
|
+
|
394
|
+
pattern '/:foo', capture: ['foo', 'bar', /\d+/] do
|
395
|
+
it { should match('/1') .capturing foo: '1' }
|
396
|
+
it { should match('/123') .capturing foo: '123' }
|
397
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
398
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
399
|
+
|
400
|
+
it { should_not match('/') }
|
401
|
+
it { should_not match('/baz') }
|
402
|
+
it { should_not match('/foo1') }
|
403
|
+
end
|
404
|
+
|
405
|
+
pattern '/:foo:bar:baz', capture: { foo: :alpha, bar: /\d+/ } do
|
406
|
+
it { should match('/ab123xy-1') .capturing foo: 'ab', bar: '123', baz: 'xy-1' }
|
407
|
+
it { should match('/ab123') .capturing foo: 'ab', bar: '12', baz: '3' }
|
408
|
+
it { should_not match('/123abcxy-1') }
|
409
|
+
it { should_not match('/abcxy-1') }
|
410
|
+
it { should_not match('/abc1') }
|
411
|
+
end
|
412
|
+
|
413
|
+
pattern '/:foo', capture: { foo: ['foo', 'bar', /\d+/] } do
|
414
|
+
it { should match('/1') .capturing foo: '1' }
|
415
|
+
it { should match('/123') .capturing foo: '123' }
|
416
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
417
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
418
|
+
|
419
|
+
it { should_not match('/') }
|
420
|
+
it { should_not match('/baz') }
|
421
|
+
it { should_not match('/foo1') }
|
422
|
+
end
|
423
|
+
|
424
|
+
pattern '/:file(.:ext)', capture: { ext: ['jpg', 'png'] } do
|
425
|
+
it { should match('/pony') .capturing file: 'pony', ext: nil }
|
426
|
+
it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
|
427
|
+
it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
|
428
|
+
it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
|
429
|
+
it { should match('/pony.png') .capturing file: 'pony', ext: 'png' }
|
430
|
+
it { should match('/pony%2Epng') .capturing file: 'pony', ext: 'png' }
|
431
|
+
it { should match('/pony%2epng') .capturing file: 'pony', ext: 'png' }
|
432
|
+
it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: 'jpg' }
|
433
|
+
it { should match('/pony.jpg.png') .capturing file: 'pony.jpg', ext: 'png' }
|
434
|
+
it { should match('/pony.gif') .capturing file: 'pony.gif', ext: nil }
|
435
|
+
it { should match('/pony.') .capturing file: 'pony.', ext: nil }
|
436
|
+
it { should_not match('.jpg') }
|
437
|
+
end
|
438
|
+
|
439
|
+
pattern '/:file(:ext)', capture: { ext: ['.jpg', '.png', '.tar.gz'] } do
|
440
|
+
it { should match('/pony') .capturing file: 'pony', ext: nil }
|
441
|
+
it { should match('/pony.jpg') .capturing file: 'pony', ext: '.jpg' }
|
442
|
+
it { should match('/pony.png') .capturing file: 'pony', ext: '.png' }
|
443
|
+
it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: '.jpg' }
|
444
|
+
it { should match('/pony.jpg.png') .capturing file: 'pony.jpg', ext: '.png' }
|
445
|
+
it { should match('/pony.tar.gz') .capturing file: 'pony', ext: '.tar.gz' }
|
446
|
+
it { should match('/pony.gif') .capturing file: 'pony.gif', ext: nil }
|
447
|
+
it { should match('/pony.') .capturing file: 'pony.', ext: nil }
|
448
|
+
it { should_not match('/.jpg') }
|
449
|
+
end
|
450
|
+
|
451
|
+
pattern '/:a(@:b)', capture: { b: /\d+/ } do
|
452
|
+
it { should match('/a') .capturing a: 'a', b: nil }
|
453
|
+
it { should match('/a@1') .capturing a: 'a', b: '1' }
|
454
|
+
it { should match('/a@b') .capturing a: 'a@b', b: nil }
|
455
|
+
it { should match('/a@1@2') .capturing a: 'a@1', b: '2' }
|
456
|
+
end
|
457
|
+
|
458
|
+
pattern '/:a(b)', greedy: false do
|
459
|
+
it { should match('/ab').capturing a: 'a' }
|
460
|
+
end
|
461
|
+
|
462
|
+
pattern '/:file(.:ext)', greedy: false do
|
463
|
+
it { should match('/pony') .capturing file: 'pony', ext: nil }
|
464
|
+
it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
|
465
|
+
it { should match('/pony.png.jpg') .capturing file: 'pony', ext: 'png.jpg' }
|
466
|
+
end
|
467
|
+
|
468
|
+
pattern '/:controller(/:action(/:id(.:format)))' do
|
469
|
+
it { should match('/content').capturing controller: 'content' }
|
470
|
+
end
|
471
|
+
|
472
|
+
pattern '/fo(o)', uri_decode: false do
|
473
|
+
it { should match('/foo') }
|
474
|
+
it { should match('/fo') }
|
475
|
+
it { should_not match('/fo(o)') }
|
476
|
+
end
|
477
|
+
|
478
|
+
pattern '/foo/bar', uri_decode: false do
|
479
|
+
it { should match('/foo/bar') }
|
480
|
+
it { should_not match('/foo%2Fbar') }
|
481
|
+
it { should_not match('/foo%2fbar') }
|
482
|
+
end
|
483
|
+
|
484
|
+
pattern "/path with spaces", uri_decode: false do
|
485
|
+
it { should match('/path with spaces') }
|
486
|
+
it { should_not match('/path%20with%20spaces') }
|
487
|
+
it { should_not match('/path%2Bwith%2Bspaces') }
|
488
|
+
it { should_not match('/path+with+spaces') }
|
489
|
+
end
|
490
|
+
|
491
|
+
pattern "/path with spaces", space_matches_plus: false do
|
492
|
+
it { should match('/path%20with%20spaces') }
|
493
|
+
it { should_not match('/path%2Bwith%2Bspaces') }
|
494
|
+
it { should_not match('/path+with+spaces') }
|
495
|
+
end
|
496
|
+
|
497
|
+
context 'invalid syntax' do
|
498
|
+
example 'unexpected closing parenthesis' do
|
499
|
+
expect { Mustermann::Rails.new('foo)bar') }.
|
500
|
+
to raise_error(Mustermann::ParseError, 'unexpected ) while parsing "foo)bar"')
|
501
|
+
end
|
502
|
+
|
503
|
+
example 'missing closing parenthesis' do
|
504
|
+
expect { Mustermann::Rails.new('foo(bar') }.
|
505
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar"')
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
context 'invalid capture names' do
|
510
|
+
example 'empty name' do
|
511
|
+
expect { Mustermann::Rails.new('/:/') }.
|
512
|
+
to raise_error(Mustermann::CompileError, "capture name can't be empty: \"/:/\"")
|
513
|
+
end
|
514
|
+
|
515
|
+
example 'named splat' do
|
516
|
+
expect { Mustermann::Rails.new('/:splat/') }.
|
517
|
+
to raise_error(Mustermann::CompileError, "capture name can't be splat: \"/:splat/\"")
|
518
|
+
end
|
519
|
+
|
520
|
+
example 'named captures' do
|
521
|
+
expect { Mustermann::Rails.new('/:captures/') }.
|
522
|
+
to raise_error(Mustermann::CompileError, "capture name can't be captures: \"/:captures/\"")
|
523
|
+
end
|
524
|
+
|
525
|
+
example 'with capital letter' do
|
526
|
+
expect { Mustermann::Rails.new('/:Foo/') }.
|
527
|
+
to raise_error(Mustermann::CompileError, "capture name must start with underscore or lower case letter: \"/:Foo/\"")
|
528
|
+
end
|
529
|
+
|
530
|
+
example 'with integer' do
|
531
|
+
expect { Mustermann::Rails.new('/:1a/') }.
|
532
|
+
to raise_error(Mustermann::CompileError, "capture name must start with underscore or lower case letter: \"/:1a/\"")
|
533
|
+
end
|
534
|
+
|
535
|
+
example 'same name twice' do
|
536
|
+
expect { Mustermann::Rails.new('/:foo(/:bar)/:bar') }.
|
537
|
+
to raise_error(Mustermann::CompileError, "can't use the same capture name twice: \"/:foo(/:bar)/:bar\"")
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
context 'Regexp compatibility' do
|
542
|
+
describe :=== do
|
543
|
+
example('non-matching') { Mustermann::Rails.new("/") .should_not be === '/foo' }
|
544
|
+
example('matching') { Mustermann::Rails.new("/:foo") .should be === '/foo' }
|
545
|
+
end
|
546
|
+
|
547
|
+
describe :=~ do
|
548
|
+
example('non-matching') { Mustermann::Rails.new("/") .should_not be =~ '/foo' }
|
549
|
+
example('matching') { Mustermann::Rails.new("/:foo") .should be =~ '/foo' }
|
550
|
+
|
551
|
+
context 'String#=~' do
|
552
|
+
example('non-matching') { "/foo".should_not be =~ Mustermann::Rails.new("/") }
|
553
|
+
example('matching') { "/foo".should be =~ Mustermann::Rails.new("/:foo") }
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
describe :to_regexp do
|
558
|
+
example('empty pattern') { Mustermann::Rails.new('').to_regexp.should be == /\A(?-mix:)\Z/ }
|
559
|
+
|
560
|
+
context 'Regexp.try_convert' do
|
561
|
+
example('empty pattern') { Regexp.try_convert(Mustermann::Rails.new('')).should be == /\A(?-mix:)\Z/ }
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
context 'Proc compatibility' do
|
567
|
+
describe :to_proc do
|
568
|
+
example { Mustermann::Rails.new("/").to_proc.should be_a(Proc) }
|
569
|
+
example('non-matching') { Mustermann::Rails.new("/") .to_proc.call('/foo').should be == false }
|
570
|
+
example('matching') { Mustermann::Rails.new("/:foo") .to_proc.call('/foo').should be == true }
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
context "peeking" do
|
575
|
+
subject(:pattern) { Mustermann::Rails.new(":name") }
|
576
|
+
|
577
|
+
describe :peek_size do
|
578
|
+
example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
|
579
|
+
example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
|
580
|
+
example { pattern.peek_size("/foo bar") .should be_nil }
|
581
|
+
end
|
582
|
+
|
583
|
+
describe :peek_match do
|
584
|
+
example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
|
585
|
+
example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
|
586
|
+
example { pattern.peek_match("/foo bar") .should be_nil }
|
587
|
+
end
|
588
|
+
|
589
|
+
describe :peek_params do
|
590
|
+
example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
|
591
|
+
example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
|
592
|
+
example { pattern.peek_params("/foo bar") .should be_nil }
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
context 'version compatibility' do
|
597
|
+
context '2.3' do
|
598
|
+
pattern '(foo)', version: '2.3' do
|
599
|
+
it { should_not match("") }
|
600
|
+
it { should_not match("foo") }
|
601
|
+
it { should match("(foo)") }
|
602
|
+
end
|
603
|
+
|
604
|
+
pattern '\\:name', version: '2.3' do
|
605
|
+
it { should match('%5cfoo').capturing(name: 'foo') }
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
context '3.0' do
|
610
|
+
pattern '(foo)', version: '3.0' do
|
611
|
+
it { should match("") }
|
612
|
+
it { should match("foo") }
|
613
|
+
end
|
614
|
+
|
615
|
+
pattern '\\:name', version: '3.0' do
|
616
|
+
it { should match(':name') }
|
617
|
+
it { should_not match(':foo') }
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context '3.2' do
|
622
|
+
pattern '\\:name', version: '3.2' do
|
623
|
+
it { should match('%5cfoo').capturing(name: 'foo') }
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
context '4.0' do
|
628
|
+
pattern '\\:name', version: '4.0' do
|
629
|
+
it { should match('foo').capturing(name: 'foo') }
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
context '4.2' do
|
634
|
+
pattern '\\:name', version: '4.2' do
|
635
|
+
it { should match(':name') }
|
636
|
+
it { should_not match(':foo') }
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mustermann-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Konstantin Haase
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mustermann
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.0
|
27
|
+
description: Adds Rails style patterns to Mustermman
|
28
|
+
email: konstantin.mailinglists@googlemail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/mustermann/rails.rb
|
35
|
+
- lib/mustermann/versions.rb
|
36
|
+
- mustermann-rails.gemspec
|
37
|
+
- spec/rails_spec.rb
|
38
|
+
homepage: https://github.com/rkh/mustermann
|
39
|
+
licenses:
|
40
|
+
- MIT
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.1.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.4.3
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Rails syntax for Mustermann
|
62
|
+
test_files:
|
63
|
+
- spec/rails_spec.rb
|
64
|
+
has_rdoc:
|