flapjack-diner 2.0.0.pre.alpha.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -10
  3. data/README.md +165 -272
  4. data/flapjack-diner.gemspec +1 -1
  5. data/lib/flapjack-diner.rb +54 -25
  6. data/lib/flapjack-diner/argument_validator.rb +0 -17
  7. data/lib/flapjack-diner/configuration.rb +417 -0
  8. data/lib/flapjack-diner/log_formatter.rb +22 -0
  9. data/lib/flapjack-diner/query.rb +114 -0
  10. data/lib/flapjack-diner/relationships.rb +180 -0
  11. data/lib/flapjack-diner/request.rb +280 -0
  12. data/lib/flapjack-diner/resources.rb +64 -0
  13. data/lib/flapjack-diner/response.rb +91 -0
  14. data/lib/flapjack-diner/tools.rb +46 -456
  15. data/lib/flapjack-diner/utility.rb +16 -0
  16. data/lib/flapjack-diner/version.rb +1 -1
  17. data/spec/flapjack-diner_spec.rb +9 -18
  18. data/spec/{resources/relationships_spec.rb → relationships_spec.rb} +75 -29
  19. data/spec/resources/checks_spec.rb +7 -7
  20. data/spec/resources/contacts_spec.rb +21 -19
  21. data/spec/resources/events_spec.rb +13 -13
  22. data/spec/resources/maintenance_periods_spec.rb +3 -3
  23. data/spec/resources/media_spec.rb +3 -3
  24. data/spec/resources/metrics_spec.rb +1 -1
  25. data/spec/resources/rules_spec.rb +278 -0
  26. data/spec/resources/states_spec.rb +1 -1
  27. data/spec/resources/statistics_spec.rb +1 -1
  28. data/spec/resources/tags_spec.rb +75 -19
  29. data/spec/support/fixture_data.rb +57 -98
  30. metadata +21 -29
  31. data/.rubocop.yml +0 -21
  32. data/.rubocop_todo.yml +0 -135
  33. data/lib/flapjack-diner/resources/acceptors.rb +0 -77
  34. data/lib/flapjack-diner/resources/checks.rb +0 -52
  35. data/lib/flapjack-diner/resources/contacts.rb +0 -54
  36. data/lib/flapjack-diner/resources/events.rb +0 -54
  37. data/lib/flapjack-diner/resources/maintenance_periods.rb +0 -76
  38. data/lib/flapjack-diner/resources/media.rb +0 -75
  39. data/lib/flapjack-diner/resources/metrics.rb +0 -23
  40. data/lib/flapjack-diner/resources/rejectors.rb +0 -77
  41. data/lib/flapjack-diner/resources/relationships.rb +0 -314
  42. data/lib/flapjack-diner/resources/states.rb +0 -24
  43. data/lib/flapjack-diner/resources/statistics.rb +0 -24
  44. data/lib/flapjack-diner/resources/tags.rb +0 -47
  45. data/spec/resources/acceptors_spec.rb +0 -278
  46. data/spec/resources/rejectors_spec.rb +0 -278
@@ -0,0 +1,64 @@
1
+ require 'flapjack-diner/configuration'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ module Resources
6
+ def self.included(base)
7
+ # base.extend ClassMethods
8
+ base.class_eval do
9
+ Flapjack::Diner::Configuration::RESOURCES.each_pair do |name, config|
10
+ requests = config[:requests]
11
+ next if requests.nil?
12
+
13
+ if requests.key?(:post)
14
+ define_singleton_method("create_#{name}".to_sym) do |*data|
15
+ resp = Flapjack::Diner::Request.new(
16
+ name, "/#{name}", :data => data,
17
+ :validations => requests[:post]
18
+ ).post
19
+ @response = Flapjack::Diner::Response.new(resp)
20
+ @response.process
21
+ @response.output
22
+ end
23
+ end
24
+
25
+ if requests.key?(:get)
26
+ define_singleton_method(name) do |*data|
27
+ resp = Flapjack::Diner::Request.new(
28
+ name, "/#{name}", :data => data,
29
+ :validations => requests[:get]
30
+ ).get
31
+ @response = Flapjack::Diner::Response.new(resp)
32
+ @response.process
33
+ @response.output
34
+ end
35
+ end
36
+
37
+ if requests.key?(:patch)
38
+ define_singleton_method("update_#{name}".to_sym) do |*data|
39
+ resp = Flapjack::Diner::Request.new(
40
+ name, "/#{name}", :data => data,
41
+ :validations => requests[:patch]
42
+ ).patch
43
+ @response = Flapjack::Diner::Response.new(resp)
44
+ @response.process
45
+ @response.output
46
+ end
47
+ end
48
+
49
+ next unless requests.key?(:delete)
50
+ define_singleton_method("delete_#{name}".to_sym) do |*uuids|
51
+ raise "'#{method_name}' requires at least one #{resource} UUID " \
52
+ 'parameter' if uuids.nil? || uuids.empty?
53
+ resp = Flapjack::Diner::Request.new(name, "/#{name}",
54
+ :ids => uuids).delete
55
+ @response = Flapjack::Diner::Response.new(resp)
56
+ @response.process
57
+ @response.output
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,91 @@
1
+ require 'flapjack-diner/utility'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ class Response
6
+ SUCCESS_STATUS_CODES = [200, 201, 204]
7
+
8
+ attr_reader :output, :context, :error
9
+
10
+ def initialize(resp, opts = {})
11
+ @response = resp
12
+ @output = nil
13
+ @context = nil
14
+ @error = nil
15
+ @return_keys_as_strings = Flapjack::Diner.return_keys_as_strings
16
+ end
17
+
18
+ def process
19
+ if 204.eql?(@response.code)
20
+ @output = true
21
+ @context = nil
22
+ @error = nil
23
+ return
24
+ end
25
+ if @response.respond_to?(:parsed_response)
26
+ parsed = @response.parsed_response
27
+ end
28
+ strify = @return_keys_as_strings.is_a?(TrueClass)
29
+ if [200, 201].include?(@response.code)
30
+ @output = handle_data(parsed, strify)
31
+ return
32
+ end
33
+ @error = handle_errors(parsed, strify)
34
+ end
35
+
36
+ private
37
+
38
+ def flatten_jsonapi_data(data)
39
+ ret = nil
40
+ case data
41
+ when Array
42
+ ret = data.inject([]) do |memo, d|
43
+ attrs = d['attributes'] || {}
44
+ d.each_pair do |k, v|
45
+ next if 'attributes'.eql?(k)
46
+ attrs.update(k => v)
47
+ end
48
+ memo += [attrs]
49
+ memo
50
+ end
51
+ when Hash
52
+ ret = data['attributes'] || {}
53
+ data.each_pair do |k, v|
54
+ next if 'attributes'.eql?(k)
55
+ ret.update(k => v)
56
+ end
57
+ else
58
+ ret = data
59
+ end
60
+ ret
61
+ end
62
+
63
+ def handle_data(parsed, strify)
64
+ return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
65
+ !parsed.key?('data')
66
+ @context = {}
67
+ c_incl_key = strify ? 'included' : :included
68
+ if parsed.key?('included')
69
+ incl = flatten_jsonapi_data(parsed['included'])
70
+ @context[c_incl_key] = incl.each_with_object({}) do |i, memo|
71
+ memo[i['type']] ||= {}
72
+ memo[i['type']][i['id']] = strify ? i : Flapjack::Diner::Utility.symbolize(i)
73
+ end
74
+ end
75
+ (%w(relationships meta) & parsed.keys).each do |k|
76
+ c = parsed[k]
77
+ @context[strify ? k : k.to_sym] = (strify ? c : Flapjack::Diner::Utility.symbolize(c))
78
+ end
79
+ ret = flatten_jsonapi_data(parsed['data'])
80
+ strify ? ret : Flapjack::Diner::Utility.symbolize(ret)
81
+ end
82
+
83
+ def handle_errors(parsed, strify)
84
+ return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
85
+ !parsed.key?('errors')
86
+ errs = parsed['errors']
87
+ strify ? errs : Flapjack::Diner::Utility.symbolize(errs)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,482 +1,72 @@
1
- require 'uri'
2
-
3
1
  module Flapjack
4
2
  module Diner
5
3
  module Tools
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}"
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
24
-
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
35
-
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
52
-
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
74
- end
75
- end
76
-
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
81
-
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)
4
+ module ClassMethods
5
+ def included_data
6
+ return if context.nil?
7
+ context[return_keys_as_strings ? 'included' : :included]
156
8
  end
157
- req_uri = build_uri(path)
158
- log_request('POST', req_uri, :data => req_data)
159
9
 
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
10
+ def related(record, rel, incl = included_data)
11
+ return if incl.nil?
165
12
 
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
13
+ type = record[return_keys_as_strings ? 'type' : :type]
14
+ return if type.nil?
176
15
 
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)
16
+ res = Flapjack::Diner::Configuration::RESOURCES.values.detect do |r|
17
+ type.eql?(r[:resource])
198
18
  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"
202
- end
19
+ return if res.nil? || res[:relationships].nil?
203
20
 
204
- log_request('PATCH', req_uri, :data => req_data)
205
-
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
214
-
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}}
223
- end
224
-
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
21
+ rel_cfg = res[:relationships][rel.to_sym]
22
+ return if rel_cfg.nil?
232
23
 
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
249
-
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}"
266
- end
267
- logger.info response_message
268
- return if response.body.nil?
269
- logger.info " Response Body: #{response.body[0..300]}"
270
- end
271
-
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)
283
- end
284
- @last_error = handle_response_errors(parsed, strify)
285
- nil
286
- end
287
-
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)
24
+ rel_type = rel_cfg[:resource]
25
+ has_data = incl.key?(rel_type)
26
+ case rel_cfg[:number]
27
+ when :singular
28
+ has_data ? singularly_related(record, rel, rel_type, incl) : nil
29
+ else
30
+ has_data ? multiply_related(record, rel, rel_type, incl) : []
306
31
  end
307
- else
308
- ret = data
309
32
  end
310
- ret
311
- end
312
33
 
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
34
+ private
319
35
 
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
36
+ def singularly_related(record, rel, type, incl)
37
+ relat, data, id_a, rel = related_accessors(rel)
38
+ return if record[relat].nil? ||
39
+ record[relat][rel].nil? ||
40
+ record[relat][rel][data].nil?
348
41
 
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)
42
+ id = record[relat][rel][data][id_a]
43
+ return if id.nil? || !incl.key?(type)
44
+ incl[type][id]
358
45
  end
359
- end
360
46
 
361
- def build_array_query(value, prefix)
362
- value.map {|v| build_nested_query(v, "#{prefix}[]") }.join('&')
363
- end
47
+ def multiply_related(record, rel, type, incl)
48
+ relat, data, id_a, rel = related_accessors(rel)
49
+ return [] if record[relat].nil? ||
50
+ record[relat][rel].nil? ||
51
+ record[relat][rel][data].nil? ||
52
+ record[relat][rel][data].empty?
364
53
 
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
54
+ ids = record[relat][rel][data].map {|m| m[id_a] }
55
+ return [] if ids.empty? || !incl.key?(type)
56
+ incl[type].values_at(*ids)
381
57
  end
382
- end
383
58
 
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)
59
+ def related_accessors(*args)
60
+ acc = [:relationships, :data, :id]
61
+ return (acc + args).map(&:to_s) if return_keys_as_strings
62
+ (acc + args.map(&:to_sym))
416
63
  end
417
64
  end
418
65
 
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
66
+ def self.included(base)
67
+ base.extend ClassMethods
68
+ # base.class_eval do
69
+ # end
480
70
  end
481
71
  end
482
72
  end