stduritemplate 0.0.1
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/lib/StdUriTemplate.rb +360 -0
- metadata +43 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a6c6c70b322eec463bb4e49abad078594606c9ee8692872c695098c48204748d
|
4
|
+
data.tar.gz: cf32c2a80bb9c0a9bae759da4be2ecbd2c0a6eb0156430998cbb66ec851abf23
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c322b8dee4abfb60c4d6907c7c803a34ad0a178c250c378fa1d748b50be5607c97c9c6320dabae50a3ef615f00e81ab1de5a21519f0274b2cb62fff850141b27
|
7
|
+
data.tar.gz: fde57a96631f3fc6f272df9b7e4f374372690daef304fcb5dfb1e4eff0574aa6382694f8237e3f40de6a84e065e45c9b0a375db8144875a55597bf5270dc3544
|
@@ -0,0 +1,360 @@
|
|
1
|
+
module StdUriTemplate
|
2
|
+
|
3
|
+
# Public API
|
4
|
+
public
|
5
|
+
|
6
|
+
def self.expand(template, substitutions)
|
7
|
+
expand_impl(template, substitutions)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Private implementation
|
11
|
+
private
|
12
|
+
|
13
|
+
module Modifier
|
14
|
+
NO_MOD = :no_mod
|
15
|
+
PLUS = :plus
|
16
|
+
DASH = :dash
|
17
|
+
DOT = :dot
|
18
|
+
SLASH = :slash
|
19
|
+
SEMICOLON = :semicolon
|
20
|
+
QUESTION_MARK = :question_mark
|
21
|
+
AT = :at
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.validate_literal(c, col)
|
25
|
+
case c
|
26
|
+
when '+', '#', '/', ';', '?', '&', ' ', '!', '=', '$', '|', '*', ':', '~', '-'
|
27
|
+
raise ArgumentError, "Illegal character identified in the token at col:#{col}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_max_char(buffer, col)
|
32
|
+
return -1 if buffer.nil?
|
33
|
+
|
34
|
+
value = buffer.to_s
|
35
|
+
|
36
|
+
if value.empty?
|
37
|
+
-1
|
38
|
+
else
|
39
|
+
begin
|
40
|
+
Integer(value)
|
41
|
+
rescue ArgumentError
|
42
|
+
raise ArgumentError, "Cannot parse max chars at col:#{col}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.get_modifier(c, token, col)
|
48
|
+
case c
|
49
|
+
when '+'
|
50
|
+
Modifier::PLUS
|
51
|
+
when '#'
|
52
|
+
Modifier::DASH
|
53
|
+
when '.'
|
54
|
+
Modifier::DOT
|
55
|
+
when '/'
|
56
|
+
Modifier::SLASH
|
57
|
+
when ';'
|
58
|
+
Modifier::SEMICOLON
|
59
|
+
when '?'
|
60
|
+
Modifier::QUESTION_MARK
|
61
|
+
when '&'
|
62
|
+
Modifier::AT
|
63
|
+
else
|
64
|
+
validate_literal(c, col)
|
65
|
+
token << c
|
66
|
+
Modifier::NO_MOD
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.expand_impl(str, substitutions)
|
71
|
+
result = ''
|
72
|
+
token = nil
|
73
|
+
modifier = nil
|
74
|
+
composite = false
|
75
|
+
max_char_buffer = nil
|
76
|
+
first_token = true
|
77
|
+
|
78
|
+
str.chars.each_with_index do |character, i|
|
79
|
+
case character
|
80
|
+
when '{'
|
81
|
+
token = ''
|
82
|
+
first_token = true
|
83
|
+
when '}'
|
84
|
+
if token
|
85
|
+
expanded = expand_token(modifier, token, composite, get_max_char(max_char_buffer, i), first_token, substitutions, result, i)
|
86
|
+
first_token = false if expanded && first_token
|
87
|
+
token = nil
|
88
|
+
modifier = nil
|
89
|
+
composite = false
|
90
|
+
max_char_buffer = nil
|
91
|
+
else
|
92
|
+
raise ArgumentError, "Failed to expand token, invalid at col:#{i}"
|
93
|
+
end
|
94
|
+
when ','
|
95
|
+
if token
|
96
|
+
expanded = expand_token(modifier, token, composite, get_max_char(max_char_buffer, i), first_token, substitutions, result, i)
|
97
|
+
first_token = false if expanded && first_token
|
98
|
+
token = ''
|
99
|
+
composite = false
|
100
|
+
max_char_buffer = nil
|
101
|
+
end
|
102
|
+
else
|
103
|
+
if token
|
104
|
+
if modifier.nil?
|
105
|
+
modifier = get_modifier(character, token, i)
|
106
|
+
elsif max_char_buffer
|
107
|
+
if character =~ /\d/
|
108
|
+
max_char_buffer << character
|
109
|
+
else
|
110
|
+
raise ArgumentError, "Illegal character identified in the token at col:#{i}"
|
111
|
+
end
|
112
|
+
else
|
113
|
+
if character == ':'
|
114
|
+
max_char_buffer = ''
|
115
|
+
elsif character == '*'
|
116
|
+
composite = true
|
117
|
+
else
|
118
|
+
validate_literal(character, i)
|
119
|
+
token << character
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
result << character
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
if token.nil?
|
129
|
+
result
|
130
|
+
else
|
131
|
+
raise ArgumentError, 'Unterminated token'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.add_prefix(mod, result)
|
136
|
+
case mod
|
137
|
+
when Modifier::DASH
|
138
|
+
result << '#'
|
139
|
+
when Modifier::DOT
|
140
|
+
result << '.'
|
141
|
+
when Modifier::SLASH
|
142
|
+
result << '/'
|
143
|
+
when Modifier::SEMICOLON
|
144
|
+
result << ';'
|
145
|
+
when Modifier::QUESTION_MARK
|
146
|
+
result << '?'
|
147
|
+
when Modifier::AT
|
148
|
+
result << '&'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.add_separator(mod, result)
|
153
|
+
case mod
|
154
|
+
when Modifier::DOT
|
155
|
+
result << '.'
|
156
|
+
when Modifier::SLASH
|
157
|
+
result << '/'
|
158
|
+
when Modifier::SEMICOLON
|
159
|
+
result << ';'
|
160
|
+
when Modifier::QUESTION_MARK, Modifier::AT
|
161
|
+
result << '&'
|
162
|
+
else
|
163
|
+
result << ','
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.add_value(mod, token, value, result, max_char)
|
168
|
+
case mod
|
169
|
+
when Modifier::PLUS, Modifier::DASH
|
170
|
+
add_expanded_value(value, result, max_char, false)
|
171
|
+
when Modifier::QUESTION_MARK, Modifier::AT
|
172
|
+
result << token + '='
|
173
|
+
add_expanded_value(value, result, max_char, true)
|
174
|
+
when Modifier::SEMICOLON
|
175
|
+
result << token
|
176
|
+
result << '=' unless value.empty?
|
177
|
+
add_expanded_value(value, result, max_char, true)
|
178
|
+
when Modifier::DOT, Modifier::SLASH, Modifier::NO_MOD
|
179
|
+
add_expanded_value(value, result, max_char, true)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.add_value_element(mod, token, value, result, max_char)
|
184
|
+
case mod
|
185
|
+
when Modifier::PLUS, Modifier::DASH
|
186
|
+
add_expanded_value(value, result, max_char, false)
|
187
|
+
when Modifier::QUESTION_MARK, Modifier::AT, Modifier::SEMICOLON, Modifier::DOT, Modifier::SLASH, Modifier::NO_MOD
|
188
|
+
add_expanded_value(value, result, max_char, true)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.add_expanded_value(value, result, max_char, replace_reserved)
|
193
|
+
max = (max_char != -1) ? [max_char, value.length].min : value.length
|
194
|
+
reserved_buffer = nil
|
195
|
+
|
196
|
+
max.times do |i|
|
197
|
+
character = value[i]
|
198
|
+
|
199
|
+
if character == '%' && !replace_reserved
|
200
|
+
reserved_buffer = ''
|
201
|
+
end
|
202
|
+
|
203
|
+
if reserved_buffer
|
204
|
+
reserved_buffer << character
|
205
|
+
|
206
|
+
if reserved_buffer.length == 3
|
207
|
+
is_encoded = false
|
208
|
+
begin
|
209
|
+
encoded = URI.decode_www_form_component(reserved_buffer)
|
210
|
+
is_encoded = !(encoded == reserved_buffer)
|
211
|
+
rescue StandardError
|
212
|
+
end
|
213
|
+
|
214
|
+
if is_encoded
|
215
|
+
result << reserved_buffer
|
216
|
+
else
|
217
|
+
result << "%25"
|
218
|
+
result << reserved_buffer[1..-1] unless replace_reserved
|
219
|
+
end
|
220
|
+
|
221
|
+
reserved_buffer = nil
|
222
|
+
end
|
223
|
+
else
|
224
|
+
if character == ' '
|
225
|
+
result << "%20"
|
226
|
+
elsif character == '%'
|
227
|
+
result << "%25"
|
228
|
+
else
|
229
|
+
if replace_reserved
|
230
|
+
result << URI.encode_www_form_component(character)
|
231
|
+
else
|
232
|
+
result << character
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
if reserved_buffer
|
239
|
+
result << "%25"
|
240
|
+
if replace_reserved
|
241
|
+
result << URI.encode_www_form_component(reserved_buffer[1..-1])
|
242
|
+
else
|
243
|
+
result << reserved_buffer[1..-1]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.list?(value)
|
249
|
+
value.kind_of?(Array) || value.kind_of?(Enumerable)
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.map?(value)
|
253
|
+
value.kind_of?(Hash)
|
254
|
+
end
|
255
|
+
|
256
|
+
module SubstitutionType
|
257
|
+
STRING = :string
|
258
|
+
LIST = :list
|
259
|
+
MAP = :map
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.get_substitution_type(value, col)
|
263
|
+
if (value.nil? || value.kind_of?(String))
|
264
|
+
SubstitutionType::STRING
|
265
|
+
elsif map?(value)
|
266
|
+
SubstitutionType::MAP
|
267
|
+
elsif list?(value)
|
268
|
+
SubstitutionType::LIST
|
269
|
+
else
|
270
|
+
raise ArgumentError, "Illegal class passed as substitution, found #{value.class} at col:#{col}"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.empty?(subst_type, value)
|
275
|
+
case subst_type
|
276
|
+
when SubstitutionType::STRING
|
277
|
+
value.nil?
|
278
|
+
when SubstitutionType::LIST
|
279
|
+
value.respond_to?(:empty?) ? value.empty? : true
|
280
|
+
when SubstitutionType::MAP
|
281
|
+
value.respond_to?(:empty?) ? value.empty? : true
|
282
|
+
else
|
283
|
+
true
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def self.expand_token(modifier, token, composite, max_char, first_token, substitutions, result, col)
|
288
|
+
raise ArgumentError, "Found an empty token at col:#{col}" if token.empty?
|
289
|
+
|
290
|
+
value = substitutions[token]
|
291
|
+
value = value.to_s if [Integer, Float].any? { |type| value.is_a?(type) }
|
292
|
+
|
293
|
+
subst_type = get_substitution_type(value, col)
|
294
|
+
return false if empty?(subst_type, value)
|
295
|
+
|
296
|
+
if first_token
|
297
|
+
add_prefix(modifier, result)
|
298
|
+
else
|
299
|
+
add_separator(modifier, result)
|
300
|
+
end
|
301
|
+
|
302
|
+
case subst_type
|
303
|
+
when SubstitutionType::STRING
|
304
|
+
add_string_value(modifier, token, value.to_s, result, max_char)
|
305
|
+
when SubstitutionType::LIST
|
306
|
+
add_list_value(modifier, token, value, result, max_char, composite)
|
307
|
+
when SubstitutionType::MAP
|
308
|
+
add_map_value(modifier, token, value, result, max_char, composite)
|
309
|
+
end
|
310
|
+
|
311
|
+
true
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.add_string_value(modifier, token, value, result, max_char)
|
315
|
+
add_value(modifier, token, value, result, max_char)
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.add_list_value(modifier, token, value, result, max_char, composite)
|
319
|
+
first = true
|
320
|
+
value.each do |v|
|
321
|
+
if first
|
322
|
+
add_value(modifier, token, v.to_s, result, max_char)
|
323
|
+
first = false
|
324
|
+
else
|
325
|
+
if composite
|
326
|
+
add_separator(modifier, result)
|
327
|
+
add_value(modifier, token, v.to_s, result, max_char)
|
328
|
+
else
|
329
|
+
result << ','
|
330
|
+
add_value_element(modifier, token, v.to_s, result, max_char)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
!first
|
335
|
+
end
|
336
|
+
|
337
|
+
def self.add_map_value(modifier, token, value, result, max_char, composite)
|
338
|
+
first = true
|
339
|
+
raise ArgumentError, 'Value trimming is not allowed on Maps' if max_char != -1
|
340
|
+
|
341
|
+
value.each do |key, val|
|
342
|
+
if composite
|
343
|
+
add_separator(modifier, result) unless first
|
344
|
+
add_value_element(modifier, token, key.to_s, result, max_char)
|
345
|
+
result << '='
|
346
|
+
else
|
347
|
+
if first
|
348
|
+
add_value(modifier, token, key.to_s, result, max_char)
|
349
|
+
else
|
350
|
+
result << ','
|
351
|
+
add_value_element(modifier, token, key.to_s, result, max_char)
|
352
|
+
end
|
353
|
+
result << ','
|
354
|
+
end
|
355
|
+
add_value_element(modifier, token, val.to_s, result, max_char)
|
356
|
+
first = false
|
357
|
+
end
|
358
|
+
!first
|
359
|
+
end
|
360
|
+
end
|
metadata
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stduritemplate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrea Peruffo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-08-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: std-uritemplate implementation for Ruby
|
14
|
+
email: andrea.peruffo1982@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/StdUriTemplate.rb
|
20
|
+
homepage: https://rubygems.org/gems/std-uritemplate
|
21
|
+
licenses:
|
22
|
+
- APACHE-2
|
23
|
+
metadata: {}
|
24
|
+
post_install_message:
|
25
|
+
rdoc_options: []
|
26
|
+
require_paths:
|
27
|
+
- lib
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
requirements: []
|
39
|
+
rubygems_version: 3.4.10
|
40
|
+
signing_key:
|
41
|
+
specification_version: 4
|
42
|
+
summary: stduritemplate
|
43
|
+
test_files: []
|