flapjack-diner 2.0.0b1 → 2.0.0.pre.alpha.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 +4 -4
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +135 -0
- data/.travis.yml +10 -5
- data/README.md +125 -143
- data/flapjack-diner.gemspec +1 -1
- data/lib/flapjack-diner.rb +24 -54
- data/lib/flapjack-diner/argument_validator.rb +17 -0
- data/lib/flapjack-diner/resources/checks.rb +52 -0
- data/lib/flapjack-diner/resources/contacts.rb +54 -0
- data/lib/flapjack-diner/resources/events.rb +54 -0
- data/lib/flapjack-diner/resources/maintenance_periods.rb +76 -0
- data/lib/flapjack-diner/resources/media.rb +75 -0
- data/lib/flapjack-diner/resources/metrics.rb +23 -0
- data/lib/flapjack-diner/resources/relationships.rb +275 -0
- data/lib/flapjack-diner/resources/rules.rb +75 -0
- data/lib/flapjack-diner/resources/states.rb +24 -0
- data/lib/flapjack-diner/resources/statistics.rb +24 -0
- data/lib/flapjack-diner/resources/tags.rb +47 -0
- data/lib/flapjack-diner/tools.rb +456 -46
- data/lib/flapjack-diner/version.rb +1 -1
- data/spec/flapjack-diner_spec.rb +18 -9
- data/spec/resources/checks_spec.rb +7 -7
- data/spec/resources/contacts_spec.rb +12 -14
- data/spec/resources/events_spec.rb +13 -13
- data/spec/resources/maintenance_periods_spec.rb +3 -3
- data/spec/resources/media_spec.rb +3 -3
- data/spec/resources/metrics_spec.rb +1 -1
- data/spec/{relationships_spec.rb → resources/relationships_spec.rb} +25 -71
- data/spec/resources/rules_spec.rb +62 -62
- data/spec/resources/states_spec.rb +1 -1
- data/spec/resources/statistics_spec.rb +1 -1
- data/spec/resources/tags_spec.rb +19 -75
- data/spec/support/fixture_data.rb +43 -80
- metadata +22 -17
- data/lib/flapjack-diner/configuration.rb +0 -417
- data/lib/flapjack-diner/log_formatter.rb +0 -22
- data/lib/flapjack-diner/query.rb +0 -114
- data/lib/flapjack-diner/relationships.rb +0 -180
- data/lib/flapjack-diner/request.rb +0 -280
- data/lib/flapjack-diner/resources.rb +0 -64
- data/lib/flapjack-diner/response.rb +0 -91
- data/lib/flapjack-diner/utility.rb +0 -16
data/lib/flapjack-diner/tools.rb
CHANGED
@@ -1,72 +1,482 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module Flapjack
|
2
4
|
module Diner
|
3
5
|
module Tools
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
SUCCESS_STATUS_CODES = [200, 201, 204]
|
7
|
+
|
8
|
+
attr_accessor :last_error, :context
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def log_request(method_type, req_uri, data = nil)
|
13
|
+
return if logger.nil? || req_uri.nil?
|
14
|
+
log_msg = "#{method_type} #{req_uri}"
|
15
|
+
unless %w(GET DELETE).include?(method_type) || data.nil?
|
16
|
+
log_msg << "\n Body: #{data.inspect}"
|
8
17
|
end
|
18
|
+
logger.info log_msg
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform_get(path, ids = [], data = {}, opts = {})
|
22
|
+
@last_error = nil
|
23
|
+
@context = nil
|
9
24
|
|
10
|
-
|
11
|
-
|
25
|
+
data = data.reduce({}, &:merge) if data.is_a?(Array)
|
26
|
+
if (ids.size > 1)
|
27
|
+
if data[:filter].nil?
|
28
|
+
data[:filter] = {:id => ids}
|
29
|
+
elsif !data[:filter].has_key?(:id)
|
30
|
+
data[:filter][:id] = ids
|
31
|
+
else
|
32
|
+
data[:filter][:id] |= ids
|
33
|
+
end
|
34
|
+
end
|
12
35
|
|
13
|
-
|
14
|
-
|
36
|
+
filt = data.delete(:filter)
|
37
|
+
unless filt.nil?
|
38
|
+
data[:filter] = filt.each_with_object([]) do |(k, v), memo|
|
39
|
+
value = case v
|
40
|
+
when Array, Set
|
41
|
+
v.to_a.join("|")
|
42
|
+
else
|
43
|
+
v.to_s
|
44
|
+
end
|
45
|
+
if opts[:assoc].nil?
|
46
|
+
memo << "#{k}:#{value}"
|
47
|
+
else
|
48
|
+
memo << "#{opts[:assoc]}.#{k}:#{value}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
15
52
|
|
16
|
-
|
17
|
-
|
53
|
+
incl = data[:include]
|
54
|
+
unless incl.nil?
|
55
|
+
case incl
|
56
|
+
when Array
|
57
|
+
raise ArgumentError.new("Include parameters must not contain commas") if incl.any? {|i| i =~ /,/}
|
58
|
+
data[:include] = if opts[:assoc].nil?
|
59
|
+
incl.join(",")
|
60
|
+
else
|
61
|
+
incl.map {|i|
|
62
|
+
if i.eql?(opts[:assoc].to_s) || (i =~ /^#{opts[:assoc]}\./)
|
63
|
+
i
|
64
|
+
else
|
65
|
+
"#{opts[:assoc]}.#{i}"
|
66
|
+
end
|
67
|
+
}.join(",")
|
68
|
+
end
|
69
|
+
when String
|
70
|
+
raise ArgumentError.new("Include parameters must not contain commas") if incl =~ /,/
|
71
|
+
unless opts[:assoc].nil? || (incl.eql?(opts[:assoc].to_s)) || (incl =~ /^#{opts[:assoc]}\./)
|
72
|
+
data[:include] = "#{opts[:assoc]}.#{incl}"
|
73
|
+
end
|
18
74
|
end
|
19
|
-
|
75
|
+
end
|
20
76
|
|
21
|
-
|
22
|
-
|
77
|
+
req_uri = build_uri(path, ids, data)
|
78
|
+
log_request('GET', req_uri, data)
|
79
|
+
handle_response(get(req_uri.request_uri))
|
80
|
+
end
|
23
81
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
82
|
+
def record_data(source, type, method)
|
83
|
+
r_type = Flapjack::Diner::Resources::Relationships::TYPES[type]
|
84
|
+
req_data = {}
|
85
|
+
['id', :id].each do |i|
|
86
|
+
req_data[:id] = source[i] if source.has_key?(i)
|
87
|
+
end
|
88
|
+
req_data[:type] = r_type
|
89
|
+
|
90
|
+
assocs = Flapjack::Diner::Resources::Relationships::ASSOCIATIONS[type] || {}
|
91
|
+
|
92
|
+
rel_singular = assocs.inject([]) do |memo, (assoc_name, assoc)|
|
93
|
+
if assoc[method].is_a?(TrueClass) && :singular.eql?(assoc[:number])
|
94
|
+
memo << assoc_name
|
95
|
+
end
|
96
|
+
memo
|
97
|
+
end
|
98
|
+
|
99
|
+
rel_multiple = assocs.inject([]) do |memo, (assoc_name, assoc)|
|
100
|
+
if assoc[method].is_a?(TrueClass) && :multiple.eql?(assoc[:number])
|
101
|
+
memo << assoc_name
|
102
|
+
end
|
103
|
+
memo
|
104
|
+
end
|
105
|
+
|
106
|
+
excluded = [:id, :type] + rel_singular + rel_multiple
|
107
|
+
attrs = source.reject do |k,v|
|
108
|
+
excluded.include?(k.to_sym)
|
109
|
+
end
|
110
|
+
|
111
|
+
req_data[:attributes] = attrs unless attrs.empty?
|
112
|
+
|
113
|
+
rel_data = {}
|
114
|
+
|
115
|
+
rel_singular.each do |singular_link|
|
116
|
+
converted = false
|
117
|
+
[singular_link, singular_link.to_s].each do |sl|
|
118
|
+
next if converted || !source.has_key?(sl)
|
119
|
+
rel_data[singular_link] = {:data => {:type => singular_link.to_s, :id => source[sl]}}
|
120
|
+
converted = true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
rel_multiple.each do |multiple_link|
|
125
|
+
converted = false
|
126
|
+
ml_type = Flapjack::Diner::Resources::Relationships::TYPES[multiple_link]
|
127
|
+
[multiple_link, multiple_link.to_s].each do |ml|
|
128
|
+
next if converted || !source.has_key?(ml)
|
129
|
+
rel_data[multiple_link] = {
|
130
|
+
:data => source[ml].map {|id|
|
131
|
+
{:type => ml_type, :id => id}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
converted = true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
req_data[:relationships] = rel_data unless rel_data.empty?
|
139
|
+
|
140
|
+
req_data
|
141
|
+
end
|
142
|
+
|
143
|
+
def perform_post(type, path, data = {})
|
144
|
+
@last_error = nil
|
145
|
+
@context = nil
|
146
|
+
|
147
|
+
jsonapi_ext = ""
|
148
|
+
req_data = nil
|
149
|
+
|
150
|
+
case data
|
151
|
+
when Array
|
152
|
+
req_data = data.collect {|d| record_data(d, type, :post) }
|
153
|
+
jsonapi_ext = "; ext=bulk"
|
154
|
+
when Hash
|
155
|
+
req_data = record_data(data, type, :post)
|
156
|
+
end
|
157
|
+
req_uri = build_uri(path)
|
158
|
+
log_request('POST', req_uri, :data => req_data)
|
159
|
+
|
160
|
+
# TODO send current character encoding in content-type ?
|
161
|
+
opts = {:body => prepare_nested_query(:data => req_data).to_json,
|
162
|
+
:headers => {'Content-Type' => "application/vnd.api+json#{jsonapi_ext}"}}
|
163
|
+
handle_response(post(req_uri.request_uri, opts))
|
164
|
+
end
|
165
|
+
|
166
|
+
def perform_post_links(type, path, *ids)
|
167
|
+
@last_error = nil
|
168
|
+
@context = nil
|
169
|
+
data = ids.collect {|id| {:type => type, :id => id}}
|
170
|
+
req_uri = build_uri(path)
|
171
|
+
log_request('POST', req_uri, :data => data)
|
172
|
+
opts = {:body => prepare_nested_query(:data => data).to_json,
|
173
|
+
:headers => {'Content-Type' => 'application/vnd.api+json'}}
|
174
|
+
handle_response(post(req_uri.request_uri, opts))
|
175
|
+
end
|
176
|
+
|
177
|
+
def perform_patch(type, path, data = nil)
|
178
|
+
@last_error = nil
|
179
|
+
@context = nil
|
180
|
+
|
181
|
+
req_uri = nil
|
182
|
+
req_data = nil
|
183
|
+
|
184
|
+
jsonapi_ext = ""
|
185
|
+
case data
|
186
|
+
when Hash
|
187
|
+
raise "Update data does not contain :id" unless data[:id]
|
188
|
+
req_data = record_data(data, type, :patch)
|
189
|
+
ids = [data[:id]]
|
190
|
+
req_uri = build_uri(path, ids)
|
191
|
+
when Array
|
192
|
+
ids = []
|
193
|
+
req_data = []
|
194
|
+
data.each do |d|
|
195
|
+
d_id = d.has_key?(:id) ? d[:id] : nil
|
196
|
+
ids << d_id unless d_id.nil? || d_id.empty?
|
197
|
+
req_data << record_data(d, type, :patch)
|
31
198
|
end
|
199
|
+
raise "Update data must each contain :id" unless ids.size == data.size
|
200
|
+
req_uri = build_uri(path)
|
201
|
+
jsonapi_ext = "; ext=bulk"
|
32
202
|
end
|
33
203
|
|
34
|
-
|
204
|
+
log_request('PATCH', req_uri, :data => req_data)
|
35
205
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
206
|
+
opts = if req_data.nil?
|
207
|
+
{}
|
208
|
+
else
|
209
|
+
{:body => prepare_nested_query(:data => req_data).to_json,
|
210
|
+
:headers => {'Content-Type' => "application/vnd.api+json#{jsonapi_ext}"}}
|
211
|
+
end
|
212
|
+
handle_response(patch(req_uri.request_uri, opts))
|
213
|
+
end
|
41
214
|
|
42
|
-
|
43
|
-
|
44
|
-
|
215
|
+
def perform_patch_links(type, path, single, *ids)
|
216
|
+
@last_error = nil
|
217
|
+
@context = nil
|
218
|
+
data = if single
|
219
|
+
raise "Must provide one ID for a singular link" unless ids.size == 1
|
220
|
+
[nil].eql?(ids) ? nil : {:type => type, :id => ids.first}
|
221
|
+
else
|
222
|
+
[[]].eql?(ids) ? [] : ids.collect {|id| {:type => type, :id => id}}
|
45
223
|
end
|
46
224
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
225
|
+
req_uri = build_uri(path)
|
226
|
+
|
227
|
+
opts = {:body => prepare_nested_query(:data => data).to_json,
|
228
|
+
:headers => {'Content-Type' => 'application/vnd.api+json'}}
|
229
|
+
log_request('PATCH', req_uri, opts)
|
230
|
+
handle_response(patch(req_uri.request_uri, opts))
|
231
|
+
end
|
232
|
+
|
233
|
+
def perform_delete(type, path, *ids)
|
234
|
+
@last_error = nil
|
235
|
+
@context = nil
|
236
|
+
type = Flapjack::Diner::Resources::Relationships::TYPES[type]
|
237
|
+
|
238
|
+
req_uri = build_uri(path, ids)
|
239
|
+
opts = if ids.size == 1
|
240
|
+
{}
|
241
|
+
else
|
242
|
+
data = ids.collect {|id| {:type => type, :id => id} }
|
243
|
+
{:body => prepare_nested_query(:data => data).to_json,
|
244
|
+
:headers => {'Content-Type' => 'application/vnd.api+json; ext=bulk'}}
|
245
|
+
end
|
246
|
+
log_request('DELETE', req_uri, opts)
|
247
|
+
handle_response(delete(req_uri.request_uri, opts))
|
248
|
+
end
|
53
249
|
|
54
|
-
|
55
|
-
|
56
|
-
|
250
|
+
def perform_delete_links(type, path, *ids)
|
251
|
+
@last_error = nil
|
252
|
+
@context = nil
|
253
|
+
req_uri = build_uri(path)
|
254
|
+
data = ids.collect {|id| {:type => type, :id => id}}
|
255
|
+
opts = {:body => prepare_nested_query(:data => data).to_json,
|
256
|
+
:headers => {'Content-Type' => 'application/vnd.api+json'}}
|
257
|
+
log_request('DELETE', req_uri, opts)
|
258
|
+
handle_response(delete(req_uri.request_uri, opts))
|
259
|
+
end
|
260
|
+
|
261
|
+
def log_response(response)
|
262
|
+
return if logger.nil? || !response.respond_to?(:code)
|
263
|
+
response_message = " Response Code: #{response.code}"
|
264
|
+
unless response.message.nil? || (response.message.eql?(''))
|
265
|
+
response_message << " #{response.message}"
|
57
266
|
end
|
267
|
+
logger.info response_message
|
268
|
+
return if response.body.nil?
|
269
|
+
logger.info " Response Body: #{response.body[0..300]}"
|
270
|
+
end
|
58
271
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
272
|
+
def handle_response(response)
|
273
|
+
log_response(response)
|
274
|
+
return true if 204.eql?(response.code)
|
275
|
+
parsed = if response.respond_to?(:parsed_response)
|
276
|
+
response.parsed_response
|
277
|
+
else
|
278
|
+
nil
|
279
|
+
end
|
280
|
+
strify = return_keys_as_strings.is_a?(TrueClass)
|
281
|
+
if [200, 201].include?(response.code)
|
282
|
+
return handle_response_data(parsed, strify)
|
63
283
|
end
|
284
|
+
@last_error = handle_response_errors(parsed, strify)
|
285
|
+
nil
|
64
286
|
end
|
65
287
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
288
|
+
def flatten_jsonapi_data(data, opts = {})
|
289
|
+
ret = nil
|
290
|
+
case data
|
291
|
+
when Array
|
292
|
+
ret = data.inject([]) do |memo, d|
|
293
|
+
attrs = d['attributes'] || {}
|
294
|
+
d.each_pair do |k, v|
|
295
|
+
next if 'attributes'.eql?(k)
|
296
|
+
attrs.update(k => v)
|
297
|
+
end
|
298
|
+
memo += [attrs]
|
299
|
+
memo
|
300
|
+
end
|
301
|
+
when Hash
|
302
|
+
ret = data['attributes'] || {}
|
303
|
+
data.each_pair do |k, v|
|
304
|
+
next if 'attributes'.eql?(k)
|
305
|
+
ret.update(k => v)
|
306
|
+
end
|
307
|
+
else
|
308
|
+
ret = data
|
309
|
+
end
|
310
|
+
ret
|
311
|
+
end
|
312
|
+
|
313
|
+
def handle_response_errors(parsed, strify)
|
314
|
+
return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
|
315
|
+
!parsed.has_key?('errors')
|
316
|
+
errs = parsed['errors']
|
317
|
+
strify ? errs : symbolize(errs)
|
318
|
+
end
|
319
|
+
|
320
|
+
def handle_response_data(parsed, strify)
|
321
|
+
return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
|
322
|
+
!parsed.has_key?('data')
|
323
|
+
@context = {}
|
324
|
+
if parsed.has_key?('included')
|
325
|
+
incl = flatten_jsonapi_data(parsed['included'], :allow_relationships => true)
|
326
|
+
@context[:included] = (strify ? incl : symbolize(incl))
|
327
|
+
end
|
328
|
+
(['relationships', 'meta'] & parsed.keys).each do |k|
|
329
|
+
c = parsed[k]
|
330
|
+
@context[k.to_sym] = (strify ? c : symbolize(c))
|
331
|
+
end
|
332
|
+
ret = flatten_jsonapi_data(parsed['data'], :allow_relationships => false)
|
333
|
+
strify ? ret : symbolize(ret)
|
334
|
+
end
|
335
|
+
|
336
|
+
def validate_params(query = {}, &validation)
|
337
|
+
return unless block_given?
|
338
|
+
query = {} if [].eql?(query)
|
339
|
+
case query
|
340
|
+
when Array
|
341
|
+
query.each do |q|
|
342
|
+
ArgumentValidator.new(q).instance_eval(&validation)
|
343
|
+
end
|
344
|
+
else
|
345
|
+
ArgumentValidator.new(query).instance_eval(&validation)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# copied from Rack::Utils -- builds the query string for GETs
|
350
|
+
def build_nested_query(value, prefix = nil)
|
351
|
+
case value
|
352
|
+
when Array
|
353
|
+
build_array_query(value, prefix)
|
354
|
+
when Hash
|
355
|
+
build_hash_query(value, prefix)
|
356
|
+
else
|
357
|
+
build_data_query(value, prefix)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def build_array_query(value, prefix)
|
362
|
+
value.map {|v| build_nested_query(v, "#{prefix}[]") }.join('&')
|
363
|
+
end
|
364
|
+
|
365
|
+
def build_hash_query(value, prefix)
|
366
|
+
value.map do |k, v|
|
367
|
+
data = prefix ? "#{prefix}[#{k}]" : k
|
368
|
+
build_nested_query(v, data)
|
369
|
+
end.join('&')
|
370
|
+
end
|
371
|
+
|
372
|
+
def build_data_query(value, prefix)
|
373
|
+
if value.respond_to?(:iso8601)
|
374
|
+
raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
|
375
|
+
"#{escape(prefix)}=#{escape(value.iso8601)}"
|
376
|
+
elsif value.is_a?(String) || value.is_a?(Integer)
|
377
|
+
raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
|
378
|
+
"#{escape(prefix)}=#{escape(value.to_s)}"
|
379
|
+
else
|
380
|
+
prefix
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def escape(s)
|
385
|
+
URI.encode_www_form_component(s)
|
386
|
+
end
|
387
|
+
|
388
|
+
def unwrap_ids(*args)
|
389
|
+
args.select {|a| a.is_a?(String) || a.is_a?(Integer) }
|
390
|
+
end
|
391
|
+
|
392
|
+
def unwrap_uuids(*args)
|
393
|
+
ids = args.select {|a| a.is_a?(String) || a.is_a?(Integer) }
|
394
|
+
raise "IDs must be RFC 4122-compliant UUIDs" unless ids.all? {|id|
|
395
|
+
id =~ /^#{Flapjack::Diner::UUID_RE}$/i
|
396
|
+
}
|
397
|
+
ids
|
398
|
+
end
|
399
|
+
|
400
|
+
def unwrap_data(*args)
|
401
|
+
data = args.reject {|a| a.is_a?(String) || a.is_a?(Integer) }
|
402
|
+
raise "Data must be passed as a Hash, or multiple Hashes" unless data.all? {|a| a.is_a?(Hash) }
|
403
|
+
return symbolize(data.first) if data.size == 1
|
404
|
+
data.each_with_object([]) {|d, o| o << symbolize(d) }
|
405
|
+
end
|
406
|
+
|
407
|
+
# used for the JSON data hashes in POST, PUT, DELETE
|
408
|
+
def prepare_nested_query(value)
|
409
|
+
case value
|
410
|
+
when Array
|
411
|
+
prepare_array_query(value)
|
412
|
+
when Hash
|
413
|
+
prepare_hash_query(value)
|
414
|
+
else
|
415
|
+
prepare_data_query(value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def prepare_array_query(value)
|
420
|
+
value.map {|v| prepare_nested_query(v) }
|
421
|
+
end
|
422
|
+
|
423
|
+
def prepare_hash_query(value)
|
424
|
+
value.each_with_object({}) do |(k, v), a|
|
425
|
+
a[k] = prepare_nested_query(v)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def prepare_data_query(value)
|
430
|
+
if value.respond_to?(:iso8601)
|
431
|
+
value.iso8601
|
432
|
+
else
|
433
|
+
case value
|
434
|
+
when Integer, TrueClass, FalseClass, NilClass
|
435
|
+
value
|
436
|
+
else
|
437
|
+
value.to_s
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def normalise_port(port_str, protocol)
|
443
|
+
if port_str.nil? || port_str.to_i < 1 || port_str.to_i > 65_535
|
444
|
+
'https'.eql?(protocol) ? 443 : 80
|
445
|
+
else
|
446
|
+
port_str.to_i
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
def protocol_host_port
|
451
|
+
%r{^(?:(?<protocol>https?)://)
|
452
|
+
(?<host>[a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])
|
453
|
+
(?::(?<port>\d+))?
|
454
|
+
}ix =~ base_uri
|
455
|
+
|
456
|
+
protocol = protocol.nil? ? 'http' : protocol.downcase
|
457
|
+
[protocol, host, normalise_port(port, protocol)]
|
458
|
+
end
|
459
|
+
|
460
|
+
def build_uri(path, ids = [], params = [])
|
461
|
+
pr, ho, po = protocol_host_port
|
462
|
+
if ids.size == 1
|
463
|
+
path += "/#{URI.escape(ids.first.to_s)}"
|
464
|
+
end
|
465
|
+
params = params.reduce({}, &:merge) if params.is_a?(Array)
|
466
|
+
query = params.empty? ? nil : build_nested_query(params)
|
467
|
+
URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
|
468
|
+
:path => path, :query => query)
|
469
|
+
end
|
470
|
+
|
471
|
+
def symbolize(obj)
|
472
|
+
case obj
|
473
|
+
when Hash
|
474
|
+
obj.each_with_object({}) {|(k, v), a| a[k.to_sym] = symbolize(v) }
|
475
|
+
when Array
|
476
|
+
obj.each_with_object([]) {|e, a| a << symbolize(e) }
|
477
|
+
else
|
478
|
+
obj
|
479
|
+
end
|
70
480
|
end
|
71
481
|
end
|
72
482
|
end
|