mustermann-contrib 3.1.1 → 4.0.0.alpha

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
2
  SHA256:
3
- metadata.gz: 4eef1534d55e9ec6be2c9bc3170114a40c2f0114b05102d4c0c4503daf97724a
4
- data.tar.gz: 4d4b0f26fb7834945887ef7c9ce16292ca2090a9585cad9579d03f4fbe2769d3
3
+ metadata.gz: ba766f2be34c126a5c87154b9e9ff3e6d5f5e2f8080f2b29a30850f5634fa91d
4
+ data.tar.gz: 3a62549ac2e49145dca087f9b082fb1957d50293fadb3ef5c84911cdb43b80ee
5
5
  SHA512:
6
- metadata.gz: e2cef5fe3354e2cf9c81a558138fb3ed563b6bc5fa3ec292c87dc8986e1ff55b970d1035824c3f8eb1ae322fde9a812051d3f49773dc8c2a49cda379700e3b09
7
- data.tar.gz: f028d0897f58ec0cf68206c01d22a271fb8a03ce49ec431b5c51957a9777c6284d2d5943ae1c934059c3a84a5e529d213aeca97637d9efad48d7e0820166aab9
6
+ metadata.gz: acc416d7cbeddec35cad710634f66a77dc9adbc89a80d493fd6b6e99336c266dca4bc6ee2a9d09fdbf2e358fa6666303016750bd455c620f06381a5d55cf2eb3
7
+ data.tar.gz: 8bccefca2caf7829bf3aa2d8a57fcadf7bed1e4a0ae0d17142a241ba1ffa94d0fd63f01a1cf44f6d2816a446ad048090e20bfe85fd26ff7211f5725170ea0518
data/LICENSE CHANGED
@@ -1,5 +1,4 @@
1
- Copyright (c) 2013-2017 Konstantin Haase
2
- Copyright (c) 2016-2017 Zachary Scott
1
+ Copyright (c) Konstantin Haase
3
2
 
4
3
  Permission is hereby granted, free of charge, to any person
5
4
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -27,177 +27,13 @@ end
27
27
  <a name="-mustermann-cake"></a>
28
28
  # CakePHP Syntax for Mustermann
29
29
 
30
- This gem implements the `cake` pattern type for Mustermann. It is compatible with [CakePHP](http://cakephp.org/) 2.x and 3.x.
31
-
32
- ## Overview
33
-
34
- **Supported options:**
35
- `capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
36
-
37
- **External documentation:**
38
- [CakePHP 2.0 Routing](http://book.cakephp.org/2.0/en/development/routing.html),
39
- [CakePHP 3.0 Routing](http://book.cakephp.org/3.0/en/development/routing.html)
40
-
41
- CakePHP patterns feature captures and unnamed splats. Captures are prefixed with a colon and splats are either a single asterisk (parsing segments into an array) or a double asterisk (parsing segments as a single string).
42
-
43
- ``` ruby
44
- require 'mustermann/cake'
45
-
46
- Mustermann.new('/:name/*', type: :cake).params('/a/b/c') # => { name: 'a', splat: ['b', 'c'] }
47
- Mustermann.new('/:name/**', type: :cake).params('/a/b/c') # => { name: 'a', splat: 'b/c' }
48
-
49
- pattern = Mustermann.new('/:name')
50
-
51
- pattern.respond_to? :expand # => true
52
- pattern.expand(name: 'foo') # => '/foo'
53
-
54
- pattern.respond_to? :to_templates # => true
55
- pattern.to_templates # => ['/{name}']
56
- ```
57
-
58
- ## Syntax
59
-
60
- <table>
61
- <thead>
62
- <tr>
63
- <th>Syntax Element</th>
64
- <th>Description</th>
65
- </tr>
66
- </thead>
67
- <tbody>
68
- <tr>
69
- <td><b>:</b><i>name</i></td>
70
- <td>
71
- Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
72
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
73
- </td>
74
- </tr>
75
- <tr>
76
- <td><b>*</b></td>
77
- <td>
78
- Captures anything in a non-greedy fashion. Capture is named splat.
79
- It is always an array of captures, as you can use it more than once in a pattern.
80
- </td>
81
- </tr>
82
- <tr>
83
- <td><b>**</b></td>
84
- <td>
85
- Captures anything in a non-greedy fashion. Capture is named splat.
86
- It is always an array of captures, as you can use it more than once in a pattern.
87
- The value matching a single <tt>**</tt> will be split at slashes when parsed into <tt>params</tt>.
88
- </td>
89
- </tr>
90
- <tr>
91
- <td><b>/</b></td>
92
- <td>
93
- Matches forward slash. Does not match URI encoded version of forward slash.
94
- </td>
95
- </tr>
96
- <tr>
97
- <td><i>any other character</i></td>
98
- <td>Matches exactly that character or a URI encoded version of it.</td>
99
- </tr>
100
- </tbody>
101
- </table>
30
+ See [docs/patterns/cake.md](../docs/patterns/cake.md).
102
31
 
103
32
 
104
33
  <a name="-mustermann-express"></a>
105
34
  # Express Syntax for Mustermann
106
35
 
107
- This gem implements the `express` pattern type for Mustermann. It is compatible with [Express](http://expressjs.com/) and [pillar.js](https://pillarjs.github.io/).
108
-
109
- ## Overview
110
-
111
- **Supported options:**
112
- `capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
113
-
114
- **External documentation:**
115
- [path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp),
116
- [live demo](http://forbeslindesay.github.io/express-route-tester/)
117
-
118
- Express patterns feature named captures (with repetition support via suffixes) that start with a colon and can have an optional regular expression constraint or unnamed captures that require a constraint.
119
-
120
- ``` ruby
121
- require 'mustermann/express'
122
-
123
- Mustermann.new('/:name/:rest+', type: :express).params('/a/b/c') # => { name: 'a', rest: 'b/c' }
124
-
125
- pattern = Mustermann.new('/:name', type: :express)
126
-
127
- pattern.respond_to? :expand # => true
128
- pattern.expand(name: 'foo') # => '/foo'
129
-
130
- pattern.respond_to? :to_templates # => true
131
- pattern.to_templates # => ['/{name}']
132
- ```
133
-
134
- ## Syntax
135
-
136
- <table>
137
- <thead>
138
- <tr>
139
- <th>Syntax Element</th>
140
- <th>Description</th>
141
- </tr>
142
- </thead>
143
- <tbody>
144
- <tr>
145
- <td><b>:</b><i>name</i></td>
146
- <td>
147
- Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
148
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
149
- </td>
150
- </tr>
151
- <tr>
152
- <td><b>:</b><i>name</i><b>+</b></td>
153
- <td>
154
- Captures one or more segments (with segments being separated by forward slashes).
155
- Capture is named <i>name</i>.
156
- Capture behavior can be modified with <tt>capture</tt> option.
157
- </td>
158
- </tr>
159
- <tr>
160
- <td><b>:</b><i>name</i><b>*</b></td>
161
- <td>
162
- Captures zero or more segments (with segments being separated by forward slashes).
163
- Capture is named <i>name</i>.
164
- Capture behavior can be modified with <tt>capture</tt> option.
165
- </td>
166
- </tr>
167
- <tr>
168
- <td><b>:</b><i>name</i><b>?</b></td>
169
- <td>
170
- Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
171
- Also matches an empty string.
172
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
173
- </td>
174
- </tr>
175
- <tr>
176
- <td><b>:</b><i>name</i><b>(</b><i>regexp</i><b>)</b></td>
177
- <td>
178
- Captures anything matching the <i>regexp</i> regular expression. Capture is named <i>name</i>.
179
- Capture behavior can be modified with <tt>capture</tt>.
180
- </td>
181
- </tr>
182
- <tr>
183
- <td><b>(</b><i>regexp</i><b>)</b></td>
184
- <td>
185
- Captures anything matching the <i>regexp</i> regular expression. Capture is named splat.
186
- Capture behavior can be modified with <tt>capture</tt>.
187
- </td>
188
- </tr>
189
- <tr>
190
- <td><b>/</b></td>
191
- <td>
192
- Matches forward slash. Does not match URI encoded version of forward slash.
193
- </td>
194
- </tr>
195
- <tr>
196
- <td><i>any other character</i></td>
197
- <td>Matches exactly that character or a URI encoded version of it.</td>
198
- </tr>
199
- </tbody>
200
- </table>
36
+ See [docs/patterns/express.md](../docs/patterns/express.md).
201
37
 
202
38
  <a name="-mustermann-fileutils"></a>
203
39
  # FileUtils for Mustermann
@@ -258,466 +94,72 @@ Mustermann::FileUtils.cp_r(':base.:ext' => ':base.bak.:ext')
258
94
  Mustermann::FileUtils.ln_s('lib/:name.rb' => 'bin/:name')
259
95
  ```
260
96
 
261
- <a name="-mustermann-flask"></a>
262
- # Flask Syntax for Mustermann
263
-
264
- This gem implements the `flask` pattern type for Mustermann. It is compatible with [Flask](http://flask.pocoo.org/) and [Werkzeug](http://werkzeug.pocoo.org/).
97
+ <a name="-mustermann-mapper"></a>
98
+ # Mapper for Mustermann
265
99
 
266
100
  ## Overview
267
101
 
268
- **Supported options:**
269
- `capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, `converters` and `ignore_unknown_options`
270
-
271
- **External documentation:**
272
- [Werkzeug: URL Routing](http://werkzeug.pocoo.org/docs/0.9/routing/)
102
+ `Mustermann::Mapper` transforms strings according to a set of pattern mappings. Each mapping pairs an input pattern (used to extract parameters) with one or more output patterns (used to expand the result). All mappings that match are applied in insertion order.
273
103
 
274
104
  ``` ruby
275
- require 'mustermann/flask'
276
-
277
- Mustermann.new('/<prefix>/<path:page>', type: :flask).params('/a/b/c') # => { prefix: 'a', page: 'b/c' }
278
-
279
- pattern = Mustermann.new('/<name>', type: :flask)
105
+ require 'mustermann/mapper'
280
106
 
281
- pattern.respond_to? :expand # => true
282
- pattern.expand(name: 'foo') # => '/foo'
283
-
284
- pattern.respond_to? :to_templates # => true
285
- pattern.to_templates # => ['/{name}']
107
+ mapper = Mustermann::Mapper.new("/:page(.:format)?" => ["/:page/view.:format", "/:page/view.html"])
108
+ mapper['/foo'] # => "/foo/view.html"
109
+ mapper['/foo.xml'] # => "/foo/view.xml"
110
+ mapper['/foo/bar'] # => "/foo/bar"
286
111
  ```
287
112
 
288
- ## Syntax
289
-
290
- <table>
291
- <thead>
292
- <tr>
293
- <th>Syntax Element</th>
294
- <th>Description</th>
295
- </tr>
296
- </thead>
297
- <tbody>
298
- <tr>
299
- <td><b>&lt;</b><i>name</i><b>&gt;</b></td>
300
- <td>
301
- Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
302
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
303
- </td>
304
- </tr>
305
- <tr>
306
- <td><b>&lt;</b><i>converter</i><b>:</b><i>name</i><b>&gt;</b></td>
307
- <td>
308
- Captures depending on the converter constraint. Capture is named <i>name</i>.
309
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
310
- See below.
311
- </td>
312
- </tr>
313
- <tr>
314
- <td><b>&lt;</b><i>converter</i><b>(</b><i>arguments</i><b>):</b><i>name</i><b>&gt;</b></td>
315
- <td>
316
- Captures depending on the converter constraint. Capture is named <i>name</i>.
317
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
318
- Arguments are separated by comma. An argument can be a simple string, a string enclosed
319
- in single or double quotes, or a key value pair (keys and values being separated by an
320
- equal sign). See below.
321
- </td>
322
- </tr>
323
- <tr>
324
- <td><b>/</b></td>
325
- <td>
326
- Matches forward slash. Does not match URI encoded version of forward slash.
327
- </td>
328
- </tr>
329
- <tr>
330
- <td><i>any other character</i></td>
331
- <td>Matches exactly that character or a URI encoded version of it.</td>
332
- </tr>
333
- </tbody>
334
- </table>
335
-
336
- ## Converters
337
-
338
- ### Builtin Converters
339
-
340
- #### `string`
341
-
342
- Possible arguments: `minlength`, `maxlength`, `length`
343
-
344
- Captures anything but a forward slash in a semi-greedy fashion.
345
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
346
-
347
- This is also the default converter.
348
-
349
- Examples:
350
-
351
- ```
352
- <name>
353
- <string:name>
354
- <string(minlength=3,maxlength=10):slug>
355
- <string(length=10):slug>
356
- ```
357
-
358
- #### `int`
359
-
360
- Possible arguments: `min`, `max`, `fixed_digits`
361
-
362
- Captures digits.
363
- Captured value will be converted to an Integer.
364
-
365
- Examples:
366
-
367
- ```
368
- <int:id>
369
- <int(min=1,max=5):page>
370
- <int(fixed_digits=16):uuid>
371
- ```
372
-
373
- #### `float`
374
-
375
- Possible arguments: `min`, `max`
376
-
377
- Captures digits with a dot.
378
- Captured value will be converted to an Float.
379
-
380
- Examples:
381
-
382
- ```
383
- <float:precision>
384
- <float(min=0,max=1):precision>
385
- ```
386
-
387
- #### `path`
388
-
389
- Captures anything in a non-greedy fashion.
390
-
391
- Example:
392
-
393
- ```
394
- <path:rest>
395
- ```
396
-
397
- #### `any`
398
-
399
- Possible arguments: List of accepted strings.
400
-
401
- Captures anything that matches one of the arguments exactly.
402
-
403
- Example:
404
-
405
- ```
406
- <any(home,search,contact):page>
407
- ```
408
-
409
- ### Custom Converters
410
-
411
- [Flask patterns](#-pattern-details-flask) support registering custom converters.
412
-
413
- A converter object may implement any of the following methods:
414
-
415
- * `convert`: Should return a block converting a string value to whatever value should end up in the `params` hash.
416
- * `constraint`: Should return a regular expression limiting which input string will match the capture.
417
- * `new`: Returns an object that may respond to `convert` and/or `constraint` as described above. Any arguments used for the converter inside the pattern will be passed to `new`.
113
+ You can also pass additional values at conversion time to supplement or override captured parameters:
418
114
 
419
115
  ``` ruby
420
- require 'mustermann/flask'
421
-
422
- SimpleConverter = Struct.new(:constraint, :convert)
423
- id_converter = SimpleConverter.new(/\d/, -> s { s.to_i })
424
-
425
- class NumConverter
426
- def initialize(base: 10)
427
- @base = Integer(base)
428
- end
429
-
430
- def convert
431
- -> s { s.to_i(@base) }
432
- end
433
-
434
- def constraint
435
- @base > 10 ? /[\da-#{(@base-1).to_s(@base)}]/ : /[0-#{@base-1}]/
436
- end
437
- end
438
-
439
- pattern = Mustermann.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>',
440
- type: :flask, converters: { id: id_converter, num: NumConverter})
441
-
442
- pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
116
+ mapper = Mustermann::Mapper.new("/:example" => "(/:prefix)?/:example.html")
117
+ mapper['/foo', prefix: 'en'] # => "/en/foo.html"
443
118
  ```
444
119
 
445
- ### Global Converters
446
-
447
- It is also possible to register a converter for all flask patterns, using `register_converter`:
448
-
449
- ``` ruby
450
- Mustermann::Flask.register_converter(:id, id_converter)
451
- Mustermann::Flask.register_converter(:num, NumConverter)
452
-
453
- pattern = Mustermann.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>', type: :flask)
454
- pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
455
- ```
120
+ ## Building a Mapper
456
121
 
457
- There is a handy syntax for quickly creating new converter classes: When you pass a block instead of a converter object, it will yield a generic converter with setters and getters for `convert` and `constraint`, and any arguments passed to the converter.
122
+ Mappings can be supplied as a hash, added via `[]=`, or built with a block:
458
123
 
459
124
  ``` ruby
460
- require 'mustermann/flask'
125
+ # Hash argument
126
+ mapper = Mustermann::Mapper.new("/:a" => "/:a.html", "/:a/:b" => "/:b/:a")
461
127
 
462
- Mustermann::Flask.register_converter(:id) do |converter|
463
- converter.constraint = /\d/
464
- converter.convert = -> s { s.to_i }
465
- end
128
+ # Block (zero-argument, returns a hash)
129
+ mapper = Mustermann::Mapper.new { { "/:a" => "/:a.html" } }
466
130
 
467
- Mustermann::Flask.register_converter(:num) do |converter, base: 10|
468
- converter.constraint = base > 10 ? /[\da-#{(@base-1).to_s(base)}]/ : /[0-#{base-1}]/
469
- converter.convert = -> s { s.to_i(base) }
131
+ # Block (one-argument, imperative)
132
+ mapper = Mustermann::Mapper.new do |m|
133
+ m["/:a"] = "/:a.html"
470
134
  end
471
135
 
472
- pattern = Mustermann.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>', type: :flask)
473
- pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
136
+ # Incremental
137
+ mapper = Mustermann::Mapper.new
138
+ mapper["/:a"] = "/:a.html"
474
139
  ```
475
140
 
476
- ### Subclassing
477
-
478
- Registering global converters will make these available for all Flask patterns. It might even override already registered converters. This global state might break unrelated code.
479
-
480
- It is therefore recommended that, if you don't want to pass in the converters option for every pattern, you create your own subclass of `Mustermann::Flask`.
481
-
482
- ``` ruby
483
- require 'mustermann/flask'
484
-
485
- MyFlask = Class.new(Mustermann::Flask)
486
-
487
- MyFlask.register_converter(:id) do |converter|
488
- converter.constraint = /\d/
489
- converter.convert = -> s { s.to_i }
490
- end
491
-
492
- MyFlask.register_converter(:num) do |converter, base: 10|
493
- converter.constraint = base > 10 ? /[\da-#{(@base-1).to_s(base)}]/ : /[0-#{base-1}]/
494
- converter.convert = -> s { s.to_i(base) }
495
- end
141
+ The output value may be a String, a `Mustermann::Pattern`, an `Array` of either (tried in order until one expands successfully), or a `Mustermann::Expander` directly.
496
142
 
497
- pattern = MyFlask.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>')
498
- pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
499
- ```
500
-
501
- You can even register this type for usage with `Mustermann.new`:
143
+ <a name="-mustermann-flask"></a>
144
+ # Flask Syntax for Mustermann
502
145
 
503
- ``` ruby
504
- Mustermann.register(:my_flask, MyFlask)
505
- pattern = Mustermann.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>', type: :my_flask)
506
- pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
507
- ```
146
+ See [docs/patterns/flask.md](../docs/patterns/flask.md).
508
147
 
509
148
  <a name="-mustermann-pyramid"></a>
510
149
  # Pyramid Syntax for Mustermann
511
150
 
512
- This gem implements the `pyramid` pattern type for Mustermann. It is compatible with [Pyramid](http://www.pylonsproject.org/projects/pyramid/about) and [Pylons](http://www.pylonsproject.org/projects/pylons-framework/about).
513
-
514
- ## Overview
515
-
516
- **Supported options:**
517
- `capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode` and `ignore_unknown_options`
518
-
519
- **External Documentation:** [Pylons Framework: URL Configuration](http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/configuration.html#url-config), [Pylons Book: Routes in Detail](http://pylonsbook.com/en/1.0/urls-routing-and-dispatch.html#routes-in-detail), [Pyramid: Route Pattern Syntax](http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/urldispatch.html#route-pattern-syntax)
520
-
521
- ``` ruby
522
- require 'mustermann/pyramid'
523
-
524
- Mustermann.new('/{prefix}/*suffix', type: :pyramid).params('/a/b/c') # => { prefix: 'a', suffix: ['b', 'c'] }
525
-
526
- pattern = Mustermann.new('/{name}', type: :pyramid)
527
-
528
- pattern.respond_to? :expand # => true
529
- pattern.expand(name: 'foo') # => '/foo'
530
-
531
- pattern.respond_to? :to_templates # => true
532
- pattern.to_templates # => ['/{name}']
533
- ```
534
-
535
- ## Syntax
536
-
537
- <table>
538
- <thead>
539
- <tr>
540
- <th>Syntax Element</th>
541
- <th>Description</th>
542
- </tr>
543
- </thead>
544
- <tbody>
545
- <tr>
546
- <td><b>&#123;</b><i>name</i><b>&#125;</b></td>
547
- <td>
548
- Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
549
- Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
550
- </td>
551
- </tr>
552
- <tr>
553
- <td><b>&#123;</b><i>name</i><b>:</b><i>regexp</i><b>&#125;</b></td>
554
- <td>
555
- Captures anything matching the <i>regexp</i> regular expression. Capture is named <i>name</i>.
556
- Capture behavior can be modified with <tt>capture</tt>.
557
- </td>
558
- </tr>
559
- <tr>
560
- <td><b>*</b><i>name</i></td>
561
- <td>
562
- Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
563
- </td>
564
- </tr>
565
- <tr>
566
- <td><b>/</b></td>
567
- <td>
568
- Matches forward slash. Does not match URI encoded version of forward slash.
569
- </td>
570
- </tr>
571
- <tr>
572
- <td><i>any other character</i></td>
573
- <td>Matches exactly that character or a URI encoded version of it.</td>
574
- </tr>
575
- </tbody>
576
- </table>
151
+ See [docs/patterns/pyramid.md](../docs/patterns/pyramid.md).
577
152
 
578
153
  <a name="-mustermann-shell"></a>
579
154
  # Shell Syntax for Mustermann
580
155
 
581
- This gem implements the `shell` pattern type for Mustermann. It is compatible with common Unix shells (like bash or zsh).
582
-
583
- ## Overview
584
-
585
- **Supported options:** `uri_decode` and `ignore_unknown_options`.
586
-
587
- **External documentation:** [Ruby's fnmatch](http://www.ruby-doc.org/core-2.1.4/File.html#method-c-fnmatch), [Wikipedia: Glob (programming)](http://en.wikipedia.org/wiki/Glob_(programming))
588
-
589
- ``` ruby
590
- require 'mustermann'
591
-
592
- pattern = Mustermann.new('/*', type: :shell)
593
- pattern === "/foo.bar" # => true
594
- pattern === "/foo/bar" # => false
595
-
596
- pattern = Mustermann.new('/**/*', type: :shell)
597
- pattern === "/foo.bar" # => true
598
- pattern === "/foo/bar" # => true
599
-
600
- pattern = Mustermann.new('/{foo,bar}', type: :shell)
601
- pattern === "/foo" # => true
602
- pattern === "/bar" # => true
603
- pattern === "/baz" # => false
604
- ```
605
-
606
- ## Syntax
607
-
608
- <table>
609
- <thead>
610
- <tr>
611
- <th>Syntax Element</th>
612
- <th>Description</th>
613
- </tr>
614
- </thead>
615
- <tbody>
616
- <tr>
617
- <td><b>*</b></td>
618
- <td>Matches anything but a slash.</td>
619
- </tr>
620
- <tr>
621
- <td><b>**</b></td>
622
- <td>Matches anything.</td>
623
- </tr>
624
- <tr>
625
- <td><b>[</b><i>set</i><b>]</b></td>
626
- <td>Matches one character in <i>set</i>.</td>
627
- </tr>
628
- <tr>
629
- <td><b>&#123;</b><i>a</i>,<i>b</i><b>&#125;</b></td>
630
- <td>Matches <i>a</i> or <i>b</i>.</td>
631
- </tr>
632
- <tr>
633
- <td><b>\</b><i>x</i></td>
634
- <td>Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.</td>
635
- </tr>
636
- <tr>
637
- <td><i>any other character</i></td>
638
- <td>Matches exactly that character or a URI encoded version of it.</td>
639
- </tr>
640
- </tbody>
641
- </table>
156
+ See [docs/patterns/shell.md](../docs/patterns/shell.md).
642
157
 
643
158
 
644
159
  <a name="-mustermann-simple"></a>
645
160
  # Simple Syntax for Mustermann
646
161
 
647
- This gem implements the `simple` pattern type for Mustermann. It is compatible with [Sinatra](http://www.sinatrarb.com/) (1.x), [Scalatra](http://scalatra.org/) and [Dancer](http://perldancer.org/).
648
-
649
- ## Overview
650
-
651
- **Supported options:**
652
- `greedy`, `space_matches_plus`, `uri_decode` and `ignore_unknown_options`.
653
-
654
- This is useful for porting an application that relies on this behavior to a later Sinatra version and to make sure Sinatra 2.0 patterns do not decrease performance. Simple patterns internally use the same code older Sinatra versions used for compiling the pattern. Error messages for broken patterns will therefore not be as informative as for other pattern implementations.
655
-
656
- ``` ruby
657
- require 'mustermann'
658
-
659
- pattern = Mustermann.new('/:example', type: :simple)
660
- pattern === "/foo.bar" # => true
661
- pattern === "/foo/bar" # => false
662
- pattern.params("/foo.bar") # => { "example" => "foo.bar" }
663
- pattern.params("/foo/bar") # => nil
664
-
665
- pattern = Mustermann.new('/:example/?:optional?', type: :simple)
666
- pattern === "/foo.bar" # => true
667
- pattern === "/foo/bar" # => true
668
- pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
669
- pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
670
-
671
- pattern = Mustermann.new('/*', type: :simple)
672
- pattern === "/foo.bar" # => true
673
- pattern === "/foo/bar" # => true
674
- pattern.params("/foo.bar") # => { "splat" => ["foo.bar"] }
675
- pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
676
- ```
677
-
678
- ## Syntax
679
-
680
- <table>
681
- <thead>
682
- <tr>
683
- <th>Syntax Element</th>
684
- <th>Description</th>
685
- </tr>
686
- </thead>
687
- <tbody>
688
- <tr>
689
- <td><b>:</b><i>name</i></td>
690
- <td>
691
- Captures anything but a forward slash in a greedy fashion. Capture is named <i>name</i>.
692
- </td>
693
- </tr>
694
- <tr>
695
- <td><b>*</b></td>
696
- <td>
697
- Captures anything in a non-greedy fashion. Capture is named splat.
698
- It is always an array of captures, as you can use <tt>*</tt> more than once in a pattern.
699
- </td>
700
- </tr>
701
- <tr>
702
- <td><i>x</i><b>?</b></td>
703
- <td>Makes <i>x</i> optional. For instance <tt>foo?</tt> matches <tt>foo</tt> or <tt>fo</tt>.</td>
704
- </tr>
705
- <tr>
706
- <td><b>/</b></td>
707
- <td>
708
- Matches forward slash. Does not match URI encoded version of forward slash.
709
- </td>
710
- </tr>
711
- <tr>
712
- <td><i>any special character</i></td>
713
- <td>Matches exactly that character or a URI encoded version of it.</td>
714
- </tr>
715
- <tr>
716
- <td><i>any other character</i></td>
717
- <td>Matches exactly that character.</td>
718
- </tr>
719
- </tbody>
720
- </table>
162
+ See [docs/patterns/simple.md](../docs/patterns/simple.md).
721
163
 
722
164
  <a name="-mustermann-strscan"></a>
723
165
  # String Scanner for Mustermann
@@ -759,91 +201,47 @@ You can also pass in default options for ad hoc patterns when creating the scann
759
201
  scanner = Mustermann::StringScanner.new(input, type: :shell)
760
202
  ```
761
203
 
762
- <a name="-mustermann-uri-template"></a>
763
- # URI Template Syntax for Mustermann
764
-
765
- This gem implements the `uri-template` (or `template`) pattern type for Mustermann. It is compatible with [RFC 6570](https://tools.ietf.org/html/rfc6570) (level 4), [JSON API](http://jsonapi.org/), [JSON Home Documents](http://tools.ietf.org/html/draft-nottingham-json-home-02) and [many more](https://code.google.com/p/uri-templates/wiki/Implementations)
204
+ <a name="-mustermann-to-pattern"></a>
205
+ # `to_pattern` for Mustermann
766
206
 
767
207
  ## Overview
768
208
 
769
- **Supported options:**
770
- `capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
771
-
772
- Please keep the following in mind:
773
-
774
- > "Some URI Templates can be used in reverse for the purpose of variable matching: comparing the template to a fully formed URI in order to extract the variable parts from that URI and assign them to the named variables. Variable matching only works well if the template expressions are delimited by the beginning or end of the URI or by characters that cannot be part of the expansion, such as reserved characters surrounding a simple string expression. In general, regular expression languages are better suited for variable matching."
775
- > &mdash; *RFC 6570, Sec 1.5: "Limitations"*
209
+ `mustermann/to_pattern` adds a `to_pattern` method to `String`, `Symbol`, `Regexp`, `Array`, and `Mustermann::Pattern`, and provides the `Mustermann::ToPattern` mixin so you can add the same method to your own classes.
776
210
 
777
211
  ``` ruby
778
- require 'mustermann'
779
-
780
- pattern = Mustermann.new('/{example}', type: :template)
781
- pattern === "/foo.bar" # => true
782
- pattern === "/foo/bar" # => false
783
- pattern.params("/foo.bar") # => { "example" => "foo.bar" }
784
- pattern.params("/foo/bar") # => nil
212
+ require 'mustermann/to_pattern'
785
213
 
786
- pattern = Mustermann.new("{/segments*}/{page}{.ext,cmpr:2}", type: :template)
787
- pattern.params("/a/b/c.tar.gz") # => {"segments"=>["a","b"], "page"=>"c", "ext"=>"tar", "cmpr"=>"gz"}
214
+ "/foo".to_pattern # => #<Mustermann::Sinatra:"/foo">
215
+ "/foo".to_pattern(type: :rails) # => #<Mustermann::Rails:"/foo">
216
+ %r{/foo}.to_pattern # => #<Mustermann::Regular:"\\/foo">
217
+ "/foo".to_pattern.to_pattern # => #<Mustermann::Sinatra:"/foo">
788
218
  ```
789
219
 
790
- ## Generating URI Templates
220
+ ## `Mustermann::ToPattern` mixin
791
221
 
792
- You do not need to use URI templates (and this gem) if all you want is reusing them for hypermedia links. Most other pattern types support generating these (via `#to_pattern`):
222
+ Include `Mustermann::ToPattern` in any class to get a `to_pattern` method driven by its `to_s` output:
793
223
 
794
224
  ``` ruby
795
- require 'mustermann'
225
+ require 'mustermann/to_pattern'
796
226
 
797
- Mustermann.new('/:name').to_templates # => ['/{name}']
798
- ```
799
-
800
- Moreover, Mustermann's default pattern type implements a subset of URI templates (`{capture}` and `{+capture}`) and can therefore also be used for simple templates/
227
+ class MyRoute
228
+ include Mustermann::ToPattern
801
229
 
802
- ``` ruby
803
- require 'mustermann'
230
+ def to_s
231
+ "/users/:id"
232
+ end
233
+ end
804
234
 
805
- Mustermann.new('/{name}').expand(name: "example") # => "/example"
235
+ MyRoute.new.to_pattern # => #<Mustermann::Sinatra:"/users/:id">
236
+ MyRoute.new.to_pattern(type: :rails) # => #<Mustermann::Rails:"/users/:id">
806
237
  ```
807
238
 
808
- ## Syntax
809
-
810
- <table>
811
- <thead>
812
- <tr>
813
- <th>Syntax Element</th>
814
- <th>Description</th>
815
- </tr>
816
- </thead>
817
- <tbody>
818
- <tr>
819
- <td><b>&#123;</b><i>o</i> <i>var</i> <i>m</i><b>,</b> <i>var</i> <i>m</i><b>,</b> ...<b>&#125;</b></td>
820
- <td>
821
- Captures expansion.
822
- Operator <i>o</i>: <code>+ # . / ; ? &amp;</tt> or none.
823
- Modifier <i>m</i>: <code>:num *</tt> or none.
824
- </td>
825
- </tr>
826
- <tr>
827
- <td><b>/</b></td>
828
- <td>
829
- Matches forward slash. Does not match URI encoded version of forward slash.
830
- </td>
831
- </tr>
832
- <tr>
833
- <td><i>any other character</i></td>
834
- <td>Matches exactly that character or a URI encoded version of it.</td>
835
- </tr>
836
- </tbody>
837
- </table>
838
-
839
- The operators `+` and `#` will always match non-greedy, whereas all other operators match semi-greedy by default.
840
- All modifiers and operators are supported. However, it does not parse lists as single values without the *explode* modifier (aka *star*).
841
- Parametric operators (`;`, `?` and `&`) currently only match parameters in given order.
842
-
843
- Note that it differs from URI templates in that it takes the unescaped version of special character instead of the escaped version.
844
-
845
- If you reuse the exact same templates and expose them via an external API meant for expansion,
846
- you should set `uri_decode` to `false` in order to conform with the specification.
239
+ If your class wraps another object (via `__getobj__`, as in `Delegator` subclasses), `to_pattern` will unwrap it before converting.
240
+
241
+ <a name="-mustermann-uri-template"></a>
242
+ # URI Template Syntax for Mustermann
243
+
244
+ See [docs/patterns/template.md](../docs/patterns/template.md).
847
245
 
848
246
 
849
247
  <a name="-mustermann-visualizer"></a>
@@ -106,7 +106,7 @@ module Mustermann
106
106
 
107
107
  # Allows you to register your own converters.
108
108
  #
109
- # It is reommended to use this on a subclass, so to not influence other subsystems
109
+ # It is recommended to use this on a subclass, so to not influence other subsystems
110
110
  # using flask templates.
111
111
  #
112
112
  # The object passed in as converter can implement #convert and/or #constraint.
@@ -135,7 +135,7 @@ module Mustermann
135
135
  # MyPattern.new("/<up:name>").params('/foo') # => { "name" => "FOO" }
136
136
  #
137
137
  # @example with converter class
138
- # require 'mustermann/flasl'
138
+ # require 'mustermann/flask'
139
139
  #
140
140
  # class MyPattern < Mustermann::Flask
141
141
  # class Converter
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ require 'mustermann'
3
+ require 'mustermann/expander'
4
+ require 'mustermann/set'
5
+
6
+ module Mustermann
7
+ # A mapper allows mapping one string to another based on pattern parsing and expanding.
8
+ #
9
+ # @example
10
+ # require 'mustermann/mapper'
11
+ # mapper = Mustermann::Mapper.new("/:foo" => "/:foo.html")
12
+ # mapper['/example'] # => "/example.html"
13
+ class Mapper
14
+ # Creates a new mapper.
15
+ #
16
+ # @overload initialize(**options)
17
+ # @param options [Hash] options The options hash
18
+ # @yield block for generating mappings as a hash
19
+ # @yieldreturn [Hash] see {#update}
20
+ #
21
+ # @example
22
+ # require 'mustermann/mapper'
23
+ # Mustermann::Mapper.new(type: :rails) {{
24
+ # "/:foo" => ["/:foo.html", "/:foo.:format"]
25
+ # }}
26
+ #
27
+ # @overload initialize(**options)
28
+ # @param options [Hash] options The options hash
29
+ # @yield block for generating mappings as a hash
30
+ # @yieldparam mapper [Mustermann::Mapper] the mapper instance
31
+ #
32
+ # @example
33
+ # require 'mustermann/mapper'
34
+ # Mustermann::Mapper.new(type: :rails) do |mapper|
35
+ # mapper["/:foo"] = ["/:foo.html", "/:foo.:format"]
36
+ # end
37
+ #
38
+ # @overload initialize(map = {}, **options)
39
+ # @param map [Hash] see {#update}
40
+ # @param [Hash] options The options hash
41
+ #
42
+ # @example map before options
43
+ # require 'mustermann/mapper'
44
+ # Mustermann::Mapper.new({"/:foo" => "/:foo.html"}, type: :rails)
45
+ def initialize(map = {}, additional_values: :ignore, **options, &block)
46
+ @options = options
47
+ @additional_values = additional_values
48
+ @set = Set.new(use_trie: false, use_cache: false, **options)
49
+ block.arity == 0 ? update(yield) : yield(self) if block
50
+ update(map) if map
51
+ end
52
+
53
+ # Add multiple mappings.
54
+ #
55
+ # @param map [Hash{String, Pattern: String, Pattern, Arry<String, Pattern>, Expander}] the mapping
56
+ def update(map)
57
+ map.to_h.each_pair do |input, output|
58
+ output = Expander.new(*output, additional_values: @additional_values, **@options) unless output.is_a? Expander
59
+ @set.add(input, output)
60
+ end
61
+ end
62
+
63
+ # @return [Hash{Patttern: Expander}] Hash version of the mapper.
64
+ def to_h = @set.patterns.to_h { [_1, @set[_1]] }
65
+
66
+ # Convert a string according to mappings. You can pass in additional params.
67
+ #
68
+ # @example mapping with and without additional parameters
69
+ # mapper = Mustermann::Mapper.new("/:example" => "(/:prefix)?/:example.html")
70
+ #
71
+ def convert(input, values = {})
72
+ @set.match_all(input).inject(input) do |current, m|
73
+ params = Hash[values.merge(m.params).map { |k, v| [k.to_s, v] }]
74
+ m.value.expandable?(params) ? m.value.expand(params) : current
75
+ end
76
+ end
77
+
78
+ # Add a single mapping.
79
+ #
80
+ # @param key [String, Pattern] format of the input string
81
+ # @param value [String, Pattern, Arry<String, Pattern>, Expander] format of the output string
82
+ def []=(key, value)
83
+ update key => value
84
+ end
85
+
86
+ alias_method :[], :convert
87
+ end
88
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+ require 'thread'
4
+ require 'mustermann'
5
+
6
+ module Mustermann
7
+ # A simple, persistent cache for creating repositories.
8
+ #
9
+ # @example
10
+ # require 'mustermann/pattern_cache'
11
+ # cache = Mustermann::PatternCache.new
12
+ #
13
+ # # use this instead of Mustermann.new
14
+ # pattern = cache.create_pattern("/:name", type: :rails)
15
+ #
16
+ # @note
17
+ # {Mustermann::Pattern.new} (which is used by {Mustermann.new}) will reuse instances that have
18
+ # not yet been garbage collected. You only need an extra cache if you do not keep a reference to
19
+ # the patterns around.
20
+ #
21
+ # @api private
22
+ class PatternCache
23
+ # @param [Hash] pattern_options default options used for {#create_pattern}
24
+ def initialize(**pattern_options)
25
+ @cached = ::Set.new
26
+ @mutex = Mutex.new
27
+ @pattern_options = pattern_options
28
+ end
29
+
30
+ # @param (see Mustermann.new)
31
+ # @return (see Mustermann.new)
32
+ # @raise (see Mustermann.new)
33
+ # @see Mustermann.new
34
+ def create_pattern(string, **pattern_options)
35
+ pattern = Mustermann.new(string, **pattern_options, **@pattern_options)
36
+ @mutex.synchronize { @cached.add(pattern) } unless @cached.include? pattern
37
+ pattern
38
+ end
39
+
40
+ # Removes all pattern instances from the cache.
41
+ def clear
42
+ @mutex.synchronize { @cached.clear }
43
+ end
44
+
45
+ # @return [Integer] number of currently cached patterns
46
+ def size
47
+ @mutex.synchronize { @cached.size }
48
+ end
49
+ end
50
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mustermann'
3
3
  require 'mustermann/pattern'
4
- require 'mustermann/simple_match'
5
4
 
6
5
  module Mustermann
7
6
  # Matches strings that are identical to the pattern.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ require 'mustermann'
3
+
4
+ module Mustermann
5
+ # Mixin for adding {#to_pattern} ducktyping to objects.
6
+ #
7
+ # @example
8
+ # require 'mustermann/to_pattern'
9
+ #
10
+ # class Foo
11
+ # include Mustermann::ToPattern
12
+ #
13
+ # def to_s
14
+ # ":foo/:bar"
15
+ # end
16
+ # end
17
+ #
18
+ # Foo.new.to_pattern # => #<Mustermann::Sinatra:":foo/:bar">
19
+ #
20
+ # By default included into String, Symbol, Regexp, Array and {Mustermann::Pattern}.
21
+ module ToPattern
22
+ PRIMITIVES = [String, Symbol, Array, Regexp, Mustermann::Pattern]
23
+ private_constant :PRIMITIVES
24
+
25
+ # Converts the object into a {Mustermann::Pattern}.
26
+ #
27
+ # @example converting a string
28
+ # ":name.png".to_pattern # => #<Mustermann::Sinatra:":name.png">
29
+ #
30
+ # @example converting a string with options
31
+ # "/*path".to_pattern(type: :rails) # => #<Mustermann::Rails:"/*path">
32
+ #
33
+ # @example converting a regexp
34
+ # /.*/.to_pattern # => #<Mustermann::Regular:".*">
35
+ #
36
+ # @example converting a pattern
37
+ # Mustermann.new("foo").to_pattern # => #<Mustermann::Sinatra:"foo">
38
+ #
39
+ # @param [Hash] options The options hash.
40
+ # @return [Mustermann::Pattern] pattern corresponding to object.
41
+ def to_pattern(**options)
42
+ input = self if PRIMITIVES.any? { |p| self.is_a? p }
43
+ input ||= __getobj__ if respond_to?(:__getobj__)
44
+ Mustermann.new(input || to_s, **options)
45
+ end
46
+
47
+ PRIMITIVES.each do |klass|
48
+ append_features(klass)
49
+ end
50
+ end
51
+ end
metadata CHANGED
@@ -1,10 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mustermann-contrib
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 4.0.0.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
8
+ - Kunpei Sakai
9
+ - Patrik Ragnarsson
10
+ - Jordan Owens
8
11
  - Zachary Scott
9
12
  bindir: bin
10
13
  cert_chain: []
@@ -16,14 +19,14 @@ dependencies:
16
19
  requirements:
17
20
  - - '='
18
21
  - !ruby/object:Gem::Version
19
- version: 3.1.1
22
+ version: 4.0.0.alpha
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - '='
25
28
  - !ruby/object:Gem::Version
26
- version: 3.1.1
29
+ version: 4.0.0.alpha
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: hansi
29
32
  requirement: !ruby/object:Gem::Requirement
@@ -52,12 +55,15 @@ files:
52
55
  - lib/mustermann/file_utils/glob_pattern.rb
53
56
  - lib/mustermann/fileutils.rb
54
57
  - lib/mustermann/flask.rb
58
+ - lib/mustermann/mapper.rb
59
+ - lib/mustermann/pattern_cache.rb
55
60
  - lib/mustermann/pyramid.rb
56
61
  - lib/mustermann/shell.rb
57
62
  - lib/mustermann/simple.rb
58
63
  - lib/mustermann/string_scanner.rb
59
64
  - lib/mustermann/strscan.rb
60
65
  - lib/mustermann/template.rb
66
+ - lib/mustermann/to_pattern.rb
61
67
  - lib/mustermann/uri_template.rb
62
68
  - lib/mustermann/visualizer.rb
63
69
  - lib/mustermann/visualizer/highlight.rb
@@ -78,7 +84,11 @@ files:
78
84
  homepage: https://github.com/sinatra/mustermann
79
85
  licenses:
80
86
  - MIT
81
- metadata: {}
87
+ metadata:
88
+ bug_tracker_uri: https://github.com/sinatra/mustermann/issues
89
+ changelog_uri: https://github.com/sinatra/mustermann/blob/main/CHANGELOG.md
90
+ documentation_uri: https://github.com/sinatra/mustermann/tree/main/mustermann-contrib#readme
91
+ source_code_uri: https://github.com/sinatra/mustermann/tree/main/mustermann-contrib
82
92
  rdoc_options: []
83
93
  require_paths:
84
94
  - lib
@@ -86,7 +96,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
96
  requirements:
87
97
  - - ">="
88
98
  - !ruby/object:Gem::Version
89
- version: 2.7.0
99
+ version: 3.3.0
90
100
  required_rubygems_version: !ruby/object:Gem::Requirement
91
101
  requirements:
92
102
  - - ">="