stduritemplate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []