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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/StdUriTemplate.rb +360 -0
  3. 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: []