uri_template 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +53 -0
- data/README.md +56 -0
- data/lib/uri_template/colon.rb +88 -29
- data/lib/uri_template/expression.rb +33 -0
- data/lib/uri_template/literal.rb +53 -0
- data/lib/uri_template/rfc6570/expression/named.rb +72 -0
- data/lib/uri_template/rfc6570/expression/unnamed.rb +76 -0
- data/lib/uri_template/rfc6570/expression.rb +374 -0
- data/lib/uri_template/rfc6570/regex_builder.rb +117 -0
- data/lib/uri_template/rfc6570.rb +18 -536
- data/lib/uri_template/token.rb +64 -0
- data/lib/uri_template/utils.rb +47 -10
- data/lib/uri_template.rb +189 -88
- data/uri_template.gemspec +4 -4
- metadata +14 -10
- data/CHANGELOG +0 -46
- data/README +0 -67
- data/lib/uri_template/draft7.rb +0 -266
data/CHANGELOG.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# 0.5.0 - 23.09.2012
|
2
|
+
- - Removed draft7
|
3
|
+
- - splitted absoulte? method into host? and scheme?
|
4
|
+
- + the URITemplate interface is now much stronger
|
5
|
+
- * code quality _significantly_ improved
|
6
|
+
- + concat method
|
7
|
+
|
8
|
+
# 0.4.0 - 06.07.2012
|
9
|
+
- + expand now accepts symbols as keys ( thanks to @peterhellber )
|
10
|
+
- + expand now accepts arrays of pairs ( thanks to @peterhellber )
|
11
|
+
- * fixed some testing bugs
|
12
|
+
|
13
|
+
# 0.3.0 - 24.05.2012
|
14
|
+
- + Implemented the final version. Default implementation is now RFC 6570
|
15
|
+
- * BUGFIX: variables with terminal dots were allowed
|
16
|
+
- * BUGFIX: lists of commas were parsed incorrectly
|
17
|
+
|
18
|
+
# 0.2.1 - 30.12.2011
|
19
|
+
- * Compatibility: Works now with MRI 1.8.7 and REE
|
20
|
+
|
21
|
+
# 0.2.0 - 03.12.2011
|
22
|
+
- * Reworked the escaping mechanism
|
23
|
+
- + escape_utils can now be used to boost escape/unescape performance
|
24
|
+
|
25
|
+
# 0.1.4 - 19.11.2011
|
26
|
+
- * Compatiblity: Works now with MRI 1.9.3, Rubinius and JRuby
|
27
|
+
- * Various (significant!) performance improvements
|
28
|
+
|
29
|
+
# 0.1.3 - 15.11.2011
|
30
|
+
- * BUGFIX: Draft7./ now concatenates literals correctly
|
31
|
+
- * BUGFIX: Draft7.tokens is now public
|
32
|
+
|
33
|
+
# 0.1.2 - 10.11.2011
|
34
|
+
- + added a new template-type: Colon
|
35
|
+
this should allow (some day) to rails-like routing tables
|
36
|
+
- + made the tokens-method mandatory and added two interfaces for tokens.
|
37
|
+
this allows cross-type features like variable anaylisis
|
38
|
+
|
39
|
+
# 0.1.1 - 4.11.2011
|
40
|
+
- + added a bunch of useful helper methods
|
41
|
+
|
42
|
+
# 0.1.0 - 2.11.2011
|
43
|
+
- - Removed Sections. They made too many headaches.
|
44
|
+
- + Made draft7 template concatenateable. This should replace sections.
|
45
|
+
- * BUGFIX: multiline uris were matched
|
46
|
+
- * BUGFIX: variablenames were decoded when this was not appreciated
|
47
|
+
|
48
|
+
# 0.0.2 - 1.11.2011
|
49
|
+
- * BUGFIX: Concatenating empty sections no more leads to catch-all templates, when an emtpy template was appreciated.
|
50
|
+
- + The extracted variables now contains the keys :suffix and :prefix if the match didn't consume the whole uri.
|
51
|
+
|
52
|
+
# 0.0.1 - 30.10.2011
|
53
|
+
- Initial version
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
URITemplate - a uri template library
|
2
|
+
========================
|
3
|
+
|
4
|
+
[![Build Status](https://secure.travis-ci.org/hannesg/uri_template.png)](http://travis-ci.org/hannesg/uri_template)
|
5
|
+
[![Dependency Status](https://gemnasium.com/hannesg/uri_template.png)](https://gemnasium.com/hannesg/uri_template)
|
6
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/hannesg/uri_template)
|
7
|
+
|
8
|
+
With URITemplate you can generate URIs based on simple templates and extract variables from URIs using the same templates. There are currently two syntaxes defined. Namely the one defined in [RFC 6570]( http://tools.ietf.org/html/rfc6570 ) and a colon based syntax, similiar to the one used by sinatra.
|
9
|
+
|
10
|
+
From version 0.2.0, it will use escape_utils if available. This will significantly boost uri-escape/unescape performance if more characters need to be escaped ( may be slightly slower in trivial cases. working on that ... ), but does not run everywhere. To enable this, do the following:
|
11
|
+
|
12
|
+
# escape_utils has to be loaded when uri_templates is loaded
|
13
|
+
gem 'escape_utils'
|
14
|
+
require 'escape_utils'
|
15
|
+
|
16
|
+
gem 'uri_template'
|
17
|
+
require 'uri_template'
|
18
|
+
|
19
|
+
UriTemplate::Utils.using_escape_utils? #=> true
|
20
|
+
|
21
|
+
|
22
|
+
Examples
|
23
|
+
-------------------
|
24
|
+
|
25
|
+
require 'uri_template'
|
26
|
+
|
27
|
+
tpl = URITemplate.new('http://{host}{/segments*}/{file}{.extensions*}')
|
28
|
+
|
29
|
+
# This will give: http://www.host.com/path/to/a/file.x.y
|
30
|
+
tpl.expand('host'=>'www.host.com','segments'=>['path','to','a'],'file'=>'file','extensions'=>['x','y'])
|
31
|
+
|
32
|
+
# This will give: { 'host'=>'www.host.com','segments'=>['path','to','a'],'file'=>'file','extensions'=>['x','y']}
|
33
|
+
tpl.extract('http://www.host.com/path/to/a/file.x.y')
|
34
|
+
|
35
|
+
# If you like colon templates more:
|
36
|
+
tpl2 = URITemplate.new(:colon, '/:x/y')
|
37
|
+
|
38
|
+
# This will give: {'x' => 'z'}
|
39
|
+
tpl2.extract('/z/y')
|
40
|
+
|
41
|
+
|
42
|
+
RFC 6570 Syntax
|
43
|
+
--------------------
|
44
|
+
|
45
|
+
The syntax defined by [RFC 6570]( http://tools.ietf.org/html/rfc6570 ) is pretty straight forward. Basically anything surrounded by curly brackets is interpreted as variable.
|
46
|
+
|
47
|
+
URITemplate.new('{variable}').expand('variable' => 'value') #=> "value"
|
48
|
+
|
49
|
+
The way variables are inserted can be modified using operators. The operator is the first character between the curly brackets. There are seven operators defined `#`, `+`, `;`, `?`, `&`, `/` and `.`. So if you want to create a form-style query do this:
|
50
|
+
|
51
|
+
URITemplate.new('{?variable}').expand('variable' => 'value') #=> "?variable=value"
|
52
|
+
|
53
|
+
Benchmarks
|
54
|
+
-----------------------
|
55
|
+
|
56
|
+
I have assembled one benchmark based on the uritemplate-test examples. You can find them in the "benchmarks" folder. The short result: uri_template is 2-10x faster than addressable on ruby 1.9.3.
|
data/lib/uri_template/colon.rb
CHANGED
@@ -20,15 +20,45 @@ require 'forwardable'
|
|
20
20
|
require 'uri_template'
|
21
21
|
require 'uri_template/utils'
|
22
22
|
|
23
|
-
# A colon based template denotes variables with a colon.
|
24
|
-
# This template type is realy basic but having just on template type was a bit weird.
|
25
23
|
module URITemplate
|
26
24
|
|
25
|
+
# A colon based template denotes variables with a colon.
|
26
|
+
#
|
27
|
+
# This template type is somewhat compatible with sinatra.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# tpl = URITemplate::Colon.new('/foo/:bar')
|
31
|
+
# tpl.extract('/foo/baz') #=> {'bar'=>'baz'}
|
32
|
+
# tpl.expand('bar'=>'boom') #=> '/foo/boom'
|
33
|
+
#
|
27
34
|
class Colon
|
28
35
|
|
29
36
|
include URITemplate
|
30
37
|
|
31
|
-
VAR = /(?:\{:([a-z]+)\}|:([a-z]+)(?![a-z]))/u
|
38
|
+
VAR = /(?:\{:([a-z]+)\}|:([a-z]+)(?![a-z])|\*)/u
|
39
|
+
|
40
|
+
class InvalidValue < StandardError
|
41
|
+
|
42
|
+
include URITemplate::InvalidValue
|
43
|
+
|
44
|
+
attr_reader :variable, :value
|
45
|
+
|
46
|
+
def initialize(variable, value)
|
47
|
+
@variable = variable
|
48
|
+
@value = value
|
49
|
+
super(generate_message())
|
50
|
+
end
|
51
|
+
|
52
|
+
class SplatIsNotAnArray < self
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def generate_message()
|
58
|
+
return "The template variable " + variable.inspect + " cannot expand the given value "+ value.inspect
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
32
62
|
|
33
63
|
class Token
|
34
64
|
|
@@ -44,11 +74,41 @@ class Colon
|
|
44
74
|
end
|
45
75
|
|
46
76
|
def expand(vars)
|
47
|
-
return Utils.escape_url(Utils.object_to_param(vars[
|
77
|
+
return Utils.escape_url(Utils.object_to_param(vars[name]))
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_r
|
81
|
+
return '([^/]*?)'
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
return ":#{name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class Splat < Variable
|
91
|
+
|
92
|
+
SPLAT = 'splat'.freeze
|
93
|
+
|
94
|
+
attr_reader :index
|
95
|
+
|
96
|
+
def initialize(index)
|
97
|
+
@index = index
|
98
|
+
super(SPLAT)
|
99
|
+
end
|
100
|
+
|
101
|
+
def expand(vars)
|
102
|
+
var = vars[name]
|
103
|
+
if Array === var
|
104
|
+
return Utils.escape_uri(Utils.object_to_param(var[index]))
|
105
|
+
else
|
106
|
+
raise InvalidValue::SplatIsNotAnArray.new(name,var)
|
107
|
+
end
|
48
108
|
end
|
49
109
|
|
50
110
|
def to_r
|
51
|
-
return
|
111
|
+
return '(.+?)'
|
52
112
|
end
|
53
113
|
|
54
114
|
end
|
@@ -78,13 +138,13 @@ class Colon
|
|
78
138
|
# Tries to convert the value into a colon-template.
|
79
139
|
# @example
|
80
140
|
# URITemplate::Colon.try_convert('/foo/:bar/').pattern #=> '/foo/:bar/'
|
81
|
-
# URITemplate::Colon.try_convert(URITemplate
|
141
|
+
# URITemplate::Colon.try_convert(URITemplate.new(:rfc6570, '/foo/{bar}/')).pattern #=> '/foo/{:bar}/'
|
82
142
|
def self.try_convert(x)
|
83
143
|
if x.kind_of? String
|
84
144
|
return new(x)
|
85
145
|
elsif x.kind_of? self
|
86
146
|
return x
|
87
|
-
elsif x.kind_of? URITemplate::
|
147
|
+
elsif x.kind_of? URITemplate::RFC6570 and x.level == 1
|
88
148
|
return new( x.pattern.gsub(/\{(.*?)\}/u){ "{:#{$1}}" } )
|
89
149
|
else
|
90
150
|
return nil
|
@@ -92,6 +152,7 @@ class Colon
|
|
92
152
|
end
|
93
153
|
|
94
154
|
def initialize(pattern)
|
155
|
+
raise ArgumentError,"Expected a String but got #{pattern.inspect}" unless pattern.kind_of? String
|
95
156
|
@pattern = pattern
|
96
157
|
end
|
97
158
|
|
@@ -102,9 +163,20 @@ class Colon
|
|
102
163
|
def extract(uri)
|
103
164
|
md = self.to_r.match(uri)
|
104
165
|
return nil unless md
|
105
|
-
|
106
|
-
|
107
|
-
}.
|
166
|
+
result = {}
|
167
|
+
splat = []
|
168
|
+
self.tokens.select{|tk| tk.kind_of? URITemplate::Expression }.each_with_index do |tk,i|
|
169
|
+
if tk.kind_of? Token::Splat
|
170
|
+
splat << md[i+1]
|
171
|
+
result['splat'] = splat unless result.key? 'splat'
|
172
|
+
else
|
173
|
+
result[tk.name] = Utils.unescape_url( md[i+1] )
|
174
|
+
end
|
175
|
+
end
|
176
|
+
if block_given?
|
177
|
+
return yield(result)
|
178
|
+
end
|
179
|
+
return result
|
108
180
|
end
|
109
181
|
|
110
182
|
def type
|
@@ -119,30 +191,17 @@ class Colon
|
|
119
191
|
@tokens ||= tokenize!
|
120
192
|
end
|
121
193
|
|
122
|
-
# Tries to concatenate two templates, as if they were path segments.
|
123
|
-
# Removes double slashes or inserts one if they are missing.
|
124
|
-
#
|
125
|
-
# @example
|
126
|
-
# tpl = URITemplate::Colon.new('/xy/')
|
127
|
-
# (tpl / '/z/' ).pattern #=> '/xy/z/'
|
128
|
-
# (tpl / 'z/' ).pattern #=> '/xy/z/'
|
129
|
-
# (tpl / ':z' ).pattern #=> '/xy/:z'
|
130
|
-
# (tpl / ':a' / 'b' ).pattern #=> '/xy/:a/b'
|
131
|
-
#
|
132
|
-
def /(o)
|
133
|
-
this, other, this_converted, other_converted = URITemplate.coerce( self, o )
|
134
|
-
if this_converted
|
135
|
-
return this / other
|
136
|
-
end
|
137
|
-
return self.class.new( File.join( this.pattern, other.pattern ) )
|
138
|
-
end
|
139
|
-
|
140
194
|
protected
|
141
195
|
|
142
196
|
def tokenize!
|
197
|
+
number_of_splats = 0
|
143
198
|
RegexpEnumerator.new(VAR).each(@pattern).map{|x|
|
144
199
|
if x.kind_of? String
|
145
|
-
Token::Static.new(x)
|
200
|
+
Token::Static.new(Utils.escape_uri(x))
|
201
|
+
elsif x[0] == '*'
|
202
|
+
n = number_of_splats
|
203
|
+
number_of_splats = number_of_splats + 1
|
204
|
+
Token::Splat.new(n)
|
146
205
|
else
|
147
206
|
# TODO: when rubinius supports ambigious names this could be replaced with x['name'] *sigh*
|
148
207
|
Token::Variable.new(x[1] || x[2])
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# This program is free software: you can redistribute it and/or modify
|
3
|
+
# it under the terms of the Affero GNU General Public License as published by
|
4
|
+
# the Free Software Foundation, either version 3 of the License, or
|
5
|
+
# (at your option) any later version.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
14
|
+
#
|
15
|
+
# (c) 2011 - 2012 by Hannes Georg
|
16
|
+
#
|
17
|
+
|
18
|
+
# A module which all non-literal tokens should include.
|
19
|
+
module URITemplate::Expression
|
20
|
+
|
21
|
+
include URITemplate::Token
|
22
|
+
|
23
|
+
attr_reader :variables
|
24
|
+
|
25
|
+
def literal?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def expression?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# This program is free software: you can redistribute it and/or modify
|
3
|
+
# it under the terms of the Affero GNU General Public License as published by
|
4
|
+
# the Free Software Foundation, either version 3 of the License, or
|
5
|
+
# (at your option) any later version.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
14
|
+
#
|
15
|
+
# (c) 2011 - 2012 by Hannes Georg
|
16
|
+
#
|
17
|
+
|
18
|
+
# A module which all literal tokens should include.
|
19
|
+
module URITemplate::Literal
|
20
|
+
|
21
|
+
include URITemplate::Token
|
22
|
+
|
23
|
+
SLASH = ?/
|
24
|
+
|
25
|
+
attr_reader :string
|
26
|
+
|
27
|
+
def literal?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def expression?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
0
|
37
|
+
end
|
38
|
+
|
39
|
+
def expand(_)
|
40
|
+
return string
|
41
|
+
end
|
42
|
+
|
43
|
+
def starts_with_slash?
|
44
|
+
string[0] == SLASH
|
45
|
+
end
|
46
|
+
|
47
|
+
def ends_with_slash?
|
48
|
+
string[-1] == SLASH
|
49
|
+
end
|
50
|
+
|
51
|
+
alias to_s string
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# This program is free software: you can redistribute it and/or modify
|
3
|
+
# it under the terms of the Affero GNU General Public License as published by
|
4
|
+
# the Free Software Foundation, either version 3 of the License, or
|
5
|
+
# (at your option) any later version.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
14
|
+
#
|
15
|
+
# (c) 2011 - 2012 by Hannes Georg
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'uri_template/rfc6570'
|
19
|
+
|
20
|
+
class URITemplate::RFC6570::Expression::Named < URITemplate::RFC6570::Expression
|
21
|
+
|
22
|
+
alias self_pair pair
|
23
|
+
|
24
|
+
def to_r_source
|
25
|
+
source = regex_builder
|
26
|
+
source.group do
|
27
|
+
source.escaped_prefix
|
28
|
+
first = true
|
29
|
+
@variable_specs.each do | var, expand , max_length |
|
30
|
+
if expand
|
31
|
+
source.capture do
|
32
|
+
source.separated_list(first) do
|
33
|
+
source.character_class('+')\
|
34
|
+
.escaped_pair_connector\
|
35
|
+
.character_class_with_comma(max_length)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
source.group do
|
40
|
+
source.escaped_separator unless first
|
41
|
+
source << Regexp.escape(var)
|
42
|
+
source.group do
|
43
|
+
source.escaped_pair_connector
|
44
|
+
source.capture do
|
45
|
+
source.character_class_with_comma(max_length)
|
46
|
+
end
|
47
|
+
source << '|' unless self.class::PAIR_IF_EMPTY
|
48
|
+
end
|
49
|
+
end.length('?')
|
50
|
+
end
|
51
|
+
first = false
|
52
|
+
end
|
53
|
+
end.length('?')
|
54
|
+
return source.join
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def extracted_nil
|
60
|
+
self.class::PAIR_IF_EMPTY ? nil : ""
|
61
|
+
end
|
62
|
+
|
63
|
+
def after_expand(name, splitted)
|
64
|
+
result = URITemplate::Utils.pair_array_to_hash2( splitted )
|
65
|
+
if result.size == 1 && result[0][0] == name
|
66
|
+
return result
|
67
|
+
else
|
68
|
+
return [ [ name , result ] ]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# This program is free software: you can redistribute it and/or modify
|
3
|
+
# it under the terms of the Affero GNU General Public License as published by
|
4
|
+
# the Free Software Foundation, either version 3 of the License, or
|
5
|
+
# (at your option) any later version.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
14
|
+
#
|
15
|
+
# (c) 2011 - 2012 by Hannes Georg
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'uri_template/rfc6570'
|
19
|
+
|
20
|
+
class URITemplate::RFC6570::Expression::Unnamed < URITemplate::RFC6570::Expression
|
21
|
+
|
22
|
+
def self_pair(_, value, max_length = 0,&block)
|
23
|
+
if block
|
24
|
+
ev = value.map(&block).join(self.class::LIST_CONNECTOR)
|
25
|
+
else
|
26
|
+
ev = escape(value)
|
27
|
+
end
|
28
|
+
cut( ev, max_length ,&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_r_source
|
32
|
+
vs = @variable_specs.size - 1
|
33
|
+
i = 0
|
34
|
+
source = regex_builder
|
35
|
+
source.group do
|
36
|
+
source.escaped_prefix
|
37
|
+
@variable_specs.each do | var, expand , max_length |
|
38
|
+
last = (vs == i)
|
39
|
+
first = (i == 0)
|
40
|
+
if expand
|
41
|
+
source.group(true) do
|
42
|
+
source.separated_list(first) do
|
43
|
+
source.group do
|
44
|
+
source.character_class('+').reluctant
|
45
|
+
source.escaped_pair_connector
|
46
|
+
end.length('?')
|
47
|
+
source.character_class(max_length)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
source.escaped_separator unless first
|
52
|
+
source.group(true) do
|
53
|
+
if last
|
54
|
+
source.character_class_with_comma(max_length)
|
55
|
+
else
|
56
|
+
source.character_class(max_length)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
i = i+1
|
61
|
+
end
|
62
|
+
end.length('?')
|
63
|
+
return source.join
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def after_expand(name, splitted)
|
69
|
+
if splitted.none?{|_,b| b }
|
70
|
+
return [ [ name, splitted.map{|a,_| a } ] ]
|
71
|
+
else
|
72
|
+
return [ [ name, splitted ] ]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|