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

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 (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