mustermann 4.0.0.alpha3 → 4.0.0.alpha4
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 +4 -4
- data/README.md +21 -21
- data/lib/mustermann/ast/fast_pattern.rb +1 -1
- data/lib/mustermann/concat.rb +10 -5
- data/lib/mustermann/match.rb +107 -13
- data/lib/mustermann/pattern.rb +2 -2
- data/lib/mustermann/regexp_based.rb +5 -5
- data/lib/mustermann/set/linear.rb +2 -2
- data/lib/mustermann/set/match.rb +10 -10
- data/lib/mustermann/set/trie.rb +5 -4
- data/lib/mustermann/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c45d32932b0745c20363ab0fb0084eb861754482e3c47ed68684cf245f10100f
|
|
4
|
+
data.tar.gz: edb3fbe54217319a9a059b2a57c74b4e8f7d84cc4721fdb9a78bbf5ed6afa113
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 39c217af2266a0dc8f647dafd1c5772cf10e6f921a9d8ccabdcd29357a1a3dc9991065221f12eacc473af1ca183ac8bbd14dc8eae2d83834b21c81ac55e8ffc4
|
|
7
|
+
data.tar.gz: f9a2c84244115819b62737f222b3c181ca5c5192e83243387473b3bf07666be8e019a78e591861e55a93579c28ab9e0f63725feddc0b6a61ff01f6f736c29735
|
data/README.md
CHANGED
|
@@ -455,27 +455,6 @@ set.expand(id: '5') # => '/users/5' (first applicable pattern)
|
|
|
455
455
|
set.expand(:posts, id: '5') # => '/posts/5' (patterns for a specific value)
|
|
456
456
|
```
|
|
457
457
|
|
|
458
|
-
<a name="-duck-typing"></a>
|
|
459
|
-
## Duck Typing
|
|
460
|
-
|
|
461
|
-
<a name="-duck-typing-to-pattern"></a>
|
|
462
|
-
### `to_pattern`
|
|
463
|
-
|
|
464
|
-
All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
|
|
465
|
-
|
|
466
|
-
``` ruby
|
|
467
|
-
require 'mustermann'
|
|
468
|
-
|
|
469
|
-
class MyObject
|
|
470
|
-
def to_pattern(**options)
|
|
471
|
-
Mustermann.new("/foo", **options)
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
object = MyObject.new
|
|
476
|
-
Mustermann.new(object, type: :rails) # => #<Mustermann::Rails:"/foo">
|
|
477
|
-
```
|
|
478
|
-
|
|
479
458
|
### Match order
|
|
480
459
|
|
|
481
460
|
A set can match patterns and values in loose or strict insertion order.
|
|
@@ -536,6 +515,27 @@ set.match("/static").value # => :first
|
|
|
536
515
|
set.match_all("/static").map(&:value) # => [:first, :second, :third]
|
|
537
516
|
```
|
|
538
517
|
|
|
518
|
+
<a name="-duck-typing"></a>
|
|
519
|
+
## Duck Typing
|
|
520
|
+
|
|
521
|
+
<a name="-duck-typing-to-pattern"></a>
|
|
522
|
+
### `to_pattern`
|
|
523
|
+
|
|
524
|
+
All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
|
|
525
|
+
|
|
526
|
+
``` ruby
|
|
527
|
+
require 'mustermann'
|
|
528
|
+
|
|
529
|
+
class MyObject
|
|
530
|
+
def to_pattern(**options)
|
|
531
|
+
Mustermann.new("/foo", **options)
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
object = MyObject.new
|
|
536
|
+
Mustermann.new(object, type: :rails) # => #<Mustermann::Rails:"/foo">
|
|
537
|
+
```
|
|
538
|
+
|
|
539
539
|
<a name="-duck-typing-respond-to"></a>
|
|
540
540
|
### `respond_to?`
|
|
541
541
|
|
|
@@ -34,7 +34,7 @@ module Mustermann
|
|
|
34
34
|
return unless match = @regexp.match(string)
|
|
35
35
|
params = match.named_captures
|
|
36
36
|
params.transform_values! { |v| unescape(v) } if string.include?('%')
|
|
37
|
-
Match.new(self,
|
|
37
|
+
Match.new(self, match, params:)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Public override: fast path for simple patterns, falls through to super otherwise.
|
data/lib/mustermann/concat.rb
CHANGED
|
@@ -75,16 +75,21 @@ module Mustermann
|
|
|
75
75
|
|
|
76
76
|
# @see Mustermann::Pattern#peek_match
|
|
77
77
|
def peek_match(string)
|
|
78
|
-
|
|
79
|
-
params
|
|
78
|
+
post_match = string
|
|
79
|
+
params = {}
|
|
80
|
+
captures = []
|
|
81
|
+
named_captures = {}
|
|
80
82
|
|
|
81
83
|
patterns.each do |pattern|
|
|
82
|
-
return unless part = pattern.peek_match(
|
|
84
|
+
return unless part = pattern.peek_match(post_match)
|
|
83
85
|
params.merge!(part.params)
|
|
84
|
-
|
|
86
|
+
named_captures.merge!(part.named_captures)
|
|
87
|
+
captures.concat(part.captures)
|
|
88
|
+
post_match = post_match[part.to_s.size..-1]
|
|
85
89
|
end
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
matched = string[0, string.size - post_match.size]
|
|
92
|
+
Match.new(self, string, matched:, params:, captures:, named_captures:, post_match:)
|
|
88
93
|
end
|
|
89
94
|
|
|
90
95
|
# @see Mustermann::Pattern#peek_params
|
data/lib/mustermann/match.rb
CHANGED
|
@@ -1,39 +1,133 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Mustermann
|
|
4
|
+
# The return value of {Mustermann::Pattern#match}, {Mustermann::Pattern#peek_match}, {Mustermann::Set#match}, and similar methods.
|
|
5
|
+
# Mimics large parts of the MatchData API, but also provides access to the pattern and params hash.
|
|
4
6
|
class Match
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
# @return [Mustermann::Pattern] the pattern that produced the match
|
|
8
|
+
attr_reader :pattern
|
|
9
|
+
|
|
10
|
+
# @return [String] the string that was matched
|
|
11
|
+
attr_reader :string
|
|
12
|
+
|
|
13
|
+
# @return [Hash] the params hash
|
|
14
|
+
attr_reader :params
|
|
15
|
+
|
|
16
|
+
# @return [Array] the captures array
|
|
17
|
+
attr_reader :captures
|
|
18
|
+
|
|
19
|
+
# @return [Hash] the named captures hash, usually identical to {#params}
|
|
20
|
+
attr_reader :named_captures
|
|
21
|
+
|
|
22
|
+
# @return [String] the post match string
|
|
23
|
+
attr_reader :post_match
|
|
24
|
+
|
|
25
|
+
# @return [String] the pre match string
|
|
26
|
+
attr_reader :pre_match
|
|
27
|
+
|
|
28
|
+
# @return [Regexp, nil] the regular expression that produced the match, if available
|
|
29
|
+
attr_reader :regexp
|
|
30
|
+
|
|
31
|
+
# @overload initialize(pattern, string, **options)
|
|
32
|
+
# @param pattern [Mustermann::Pattern] the pattern that produced the match
|
|
33
|
+
# @param string [String] the string that was matched
|
|
34
|
+
#
|
|
35
|
+
# @overload initialize(match, **options)
|
|
36
|
+
# @param match [Mustermann::Match] the match to copy pattern and string from
|
|
37
|
+
#
|
|
38
|
+
# @overload initialize(pattern, match, **options)
|
|
39
|
+
# @param match [Mustermann::Match, MatchData] the match to copy string from
|
|
40
|
+
#
|
|
41
|
+
# @option options [Array] :captures the captures array
|
|
42
|
+
# @option options [Hash] :named_captures the named captures hash
|
|
43
|
+
# @option options [String] :matched the matched substring (defaults to string for full matches)
|
|
44
|
+
# @option options [Hash] :params the params hash
|
|
45
|
+
# @option options [Regexp] :regexp the regular expression that produced the match
|
|
46
|
+
# @option options [String] :post_match the post match string
|
|
47
|
+
# @option options [String] :pre_match the pre match string
|
|
48
|
+
def initialize(pattern_or_match, string_or_match = nil, matched: nil, params: nil, post_match: nil, pre_match: nil, captures: nil, named_captures: nil, regexp: nil)
|
|
49
|
+
case pattern_or_match
|
|
50
|
+
when Mustermann::Match, MatchData then match = pattern_or_match
|
|
51
|
+
when Mustermann::Pattern then pattern = pattern_or_match
|
|
52
|
+
else raise ArgumentError, "first argument must be a Mustermann::Pattern or a MatchData, not #{pattern_or_match.class}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
case string_or_match
|
|
56
|
+
when Mustermann::Match, MatchData then match ||= string_or_match
|
|
57
|
+
when String then string = string_or_match
|
|
58
|
+
when nil # ignore
|
|
59
|
+
else raise ArgumentError, "second argument must be a String or a MatchData, not #{string_or_match.class}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@pattern = pattern || match&.pattern
|
|
63
|
+
@string = string || match&.string || ''
|
|
64
|
+
@params = params || match&.params || {}
|
|
65
|
+
@post_match = post_match || match&.post_match || ''
|
|
66
|
+
@pre_match = pre_match || match&.pre_match || ''
|
|
67
|
+
@captures = captures || match&.captures || @params.values
|
|
68
|
+
@named_captures = named_captures || match&.named_captures || @params
|
|
69
|
+
@matched = matched || match&.to_s || @string
|
|
70
|
+
|
|
71
|
+
unless @regexp = regexp
|
|
72
|
+
@regexp = match.regexp if match.respond_to?(:regexp)
|
|
73
|
+
@regexp ||= pattern.respond_to?(:regexp) ? pattern.regexp : nil
|
|
74
|
+
end
|
|
13
75
|
end
|
|
14
76
|
|
|
15
|
-
|
|
77
|
+
# @overload [](key)
|
|
78
|
+
# Access params by key.
|
|
79
|
+
# @param key [String, Symbol] the key to access
|
|
80
|
+
# @return the value of the param, or nil if not found
|
|
81
|
+
#
|
|
82
|
+
# @overload [](index)
|
|
83
|
+
# Access captures by index.
|
|
84
|
+
# @param index [Integer] the index to access
|
|
85
|
+
# @return the value of the capture, or nil if not found
|
|
86
|
+
#
|
|
87
|
+
# @overload [](start, length)
|
|
88
|
+
# Access multiple captures by index and length.
|
|
89
|
+
# @param start [Integer] the starting index to access
|
|
90
|
+
# @param length [Integer] the number of captures to access
|
|
91
|
+
# @return [Array] the values of the captures
|
|
92
|
+
#
|
|
93
|
+
# @overload [](range)
|
|
94
|
+
# Access multiple captures by range.
|
|
95
|
+
# @param range [Range] the range of indices to access
|
|
96
|
+
# @return [Array] the values of the captures
|
|
97
|
+
def [](key, length = nil)
|
|
16
98
|
case key
|
|
17
99
|
when String then params[key]
|
|
18
100
|
when Symbol then params[key.to_s]
|
|
19
|
-
|
|
101
|
+
when Integer then length ? captures[key, length] : captures[key]
|
|
102
|
+
when Range then captures[key]
|
|
103
|
+
else raise ArgumentError, "key must be a String, Symbol, Integer, or Range, not #{key.class}"
|
|
20
104
|
end
|
|
21
105
|
end
|
|
22
|
-
|
|
106
|
+
|
|
107
|
+
# Deconstructs the match into a hash of the given keys. Useful for pattern matching.
|
|
108
|
+
# @param keys [Array] the keys to deconstruct
|
|
109
|
+
# @return [Hash] a hash of the given keys and their corresponding values
|
|
110
|
+
# @see https://docs.ruby-lang.org/en/4.0/syntax/pattern_matching_rdoc.html
|
|
23
111
|
def deconstruct_keys(keys) = keys.to_h { |key| [key, self[key]] }
|
|
24
112
|
|
|
113
|
+
# @see Object#hash
|
|
25
114
|
def hash = pattern.hash ^ string.hash ^ params.hash
|
|
26
115
|
|
|
116
|
+
# @see Object#eql?
|
|
27
117
|
def eql?(other)
|
|
28
118
|
return false unless other.is_a? self.class
|
|
29
119
|
pattern == other.pattern && string == other.string && params == other.params
|
|
30
120
|
end
|
|
31
121
|
|
|
122
|
+
# Returns the values of the given keys as an array.
|
|
123
|
+
# @params keys [Array<Symbol, String>] the keys to access
|
|
124
|
+
# @return [Array] the values of the given keys
|
|
32
125
|
def values_at(*keys) = keys.map { |key| self[key] }
|
|
33
126
|
|
|
127
|
+
# @return [String] the matched substring (like MatchData#to_s)
|
|
128
|
+
def to_s = @matched
|
|
129
|
+
|
|
34
130
|
alias == eql?
|
|
35
|
-
alias to_s string
|
|
36
131
|
alias to_h params
|
|
37
|
-
|
|
38
132
|
end
|
|
39
133
|
end
|
data/lib/mustermann/pattern.rb
CHANGED
|
@@ -165,7 +165,7 @@ module Mustermann
|
|
|
165
165
|
# @see #peek_params
|
|
166
166
|
def peek_match(string)
|
|
167
167
|
matched = peek(string)
|
|
168
|
-
Match.new(self,
|
|
168
|
+
Match.new(self, string, matched:, params: {}, post_match: string[matched.size..-1]) if matched
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
# Tries to match the pattern against the beginning of the string (as opposed to the full string).
|
|
@@ -182,7 +182,7 @@ module Mustermann
|
|
|
182
182
|
# @return [Array<Hash, Integer>, nil] Array with params hash and length of substing if matched, nil otherwise
|
|
183
183
|
def peek_params(string)
|
|
184
184
|
match = peek_match(string)
|
|
185
|
-
match ? [match.params, match.
|
|
185
|
+
match ? [match.params, match.to_s.size] : nil
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
# @param [String] string the string to match against
|
|
@@ -35,10 +35,10 @@ module Mustermann
|
|
|
35
35
|
# @see (see Mustermann::Pattern#peek_match)
|
|
36
36
|
def peek_match(string) = build_match(@peek_regexp.match(string))
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
# @param (see Mustermann::Pattern#match)
|
|
39
|
+
# @return (see Mustermann::Pattern#match)
|
|
40
|
+
# @see (see Mustermann::Pattern#match)
|
|
41
|
+
def match(string) = build_match(@regexp.match(string))
|
|
42
42
|
|
|
43
43
|
extend Forwardable
|
|
44
44
|
def_delegators :regexp, :===, :=~, :names
|
|
@@ -47,7 +47,7 @@ module Mustermann
|
|
|
47
47
|
|
|
48
48
|
def build_match(match)
|
|
49
49
|
return unless match
|
|
50
|
-
Match.new(self, match
|
|
50
|
+
Match.new(self, match, params: build_params(match))
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def build_params(match)
|
|
@@ -17,9 +17,9 @@ module Mustermann
|
|
|
17
17
|
result = [] if all
|
|
18
18
|
@patterns.each do |pattern|
|
|
19
19
|
next unless match = peek ? pattern.peek_match(string) : pattern.match(string)
|
|
20
|
-
return Match.new(match
|
|
20
|
+
return Match.new(match, value: @set.values_for_pattern(pattern)&.first) unless all
|
|
21
21
|
values = @set.values_for_pattern(pattern) || [nil]
|
|
22
|
-
values.each { |value| result << Match.new(match
|
|
22
|
+
values.each { |value| result << Match.new(match, value:) }
|
|
23
23
|
end
|
|
24
24
|
result
|
|
25
25
|
end
|
data/lib/mustermann/set/match.rb
CHANGED
|
@@ -3,21 +3,21 @@ require 'mustermann/match'
|
|
|
3
3
|
|
|
4
4
|
module Mustermann
|
|
5
5
|
class Set
|
|
6
|
+
|
|
7
|
+
# Subclass of {Mustermann::Match} that also includes the value associated with the pattern that produced the match.
|
|
6
8
|
class Match < Mustermann::Match
|
|
9
|
+
# @return the value associated with the pattern that produced the match, if any
|
|
7
10
|
attr_reader :value
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
# (see Mustermann::Match#initialize)
|
|
13
|
+
# @option options [Object] :value the value associated with the pattern that produced the match, if any
|
|
14
|
+
def initialize(*, value: nil, **)
|
|
10
15
|
@value = value
|
|
11
|
-
|
|
12
|
-
@pattern = match.pattern
|
|
13
|
-
@string = match.string
|
|
14
|
-
@params = match.params
|
|
15
|
-
@post_match = match.post_match
|
|
16
|
-
@pre_match = match.pre_match
|
|
17
|
-
else
|
|
18
|
-
super(pattern, string, params, post_match:, pre_match:)
|
|
19
|
-
end
|
|
16
|
+
super(*, **)
|
|
20
17
|
end
|
|
18
|
+
|
|
19
|
+
# @see Mustermann::Match#eql?
|
|
20
|
+
def eql?(other) = super && value == other.value
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/mustermann/set/trie.rb
CHANGED
|
@@ -148,7 +148,7 @@ module Mustermann
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
if peek
|
|
151
|
-
matches = build_matches(string
|
|
151
|
+
matches = build_matches(string, params, all:, matched_length: position, post_match: string[position..], pre_match: '')
|
|
152
152
|
return matches unless all
|
|
153
153
|
result.concat(matches)
|
|
154
154
|
end
|
|
@@ -158,17 +158,18 @@ module Mustermann
|
|
|
158
158
|
|
|
159
159
|
NIL_VALUES = [nil].freeze
|
|
160
160
|
|
|
161
|
-
def build_matches(string, params, all: false, post_match: '', pre_match: '')
|
|
161
|
+
def build_matches(string, params, all: false, matched_length: string.size, post_match: '', pre_match: '')
|
|
162
162
|
result = [] if all
|
|
163
|
+
matched = string[0, matched_length]
|
|
163
164
|
|
|
164
165
|
@patterns.each do |pattern|
|
|
165
|
-
next if pattern.except_regexp&.match?(
|
|
166
|
+
next if pattern.except_regexp&.match?(matched)
|
|
166
167
|
|
|
167
168
|
pattern_params = build_pattern_params(pattern, params)
|
|
168
169
|
|
|
169
170
|
values = @set.values_for_pattern(pattern) || NIL_VALUES
|
|
170
171
|
values.each do |value|
|
|
171
|
-
match = Set::Match.new(pattern, string, pattern_params, value:, post_match:, pre_match:)
|
|
172
|
+
match = Set::Match.new(pattern, string, matched:, params: pattern_params, value:, post_match:, pre_match:)
|
|
172
173
|
return match unless all
|
|
173
174
|
result << match
|
|
174
175
|
end
|
data/lib/mustermann/version.rb
CHANGED