ocean-rails 3.6.1 → 3.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b1ebefcaefe0c03253d9b06bda0853e2e989aee
4
- data.tar.gz: a1403dc8927f967419e22cf7007893a0fbeff4be
3
+ metadata.gz: 77809e9f1822672d4c90aeb4a2409d237acab4c0
4
+ data.tar.gz: 83438c0ffeb2603067f0f05d8ffb0a29e8926d63
5
5
  SHA512:
6
- metadata.gz: 4eca93e9da87b0baad368541f08d7c9673fd9bc59ada298be8c3e0a0b8a83322b0b11df9f5f3d0d09041541abf380d93efae65772f64b3d84346a6b1cfb0c8fe
7
- data.tar.gz: 5b44ae5a565e4aa376c2f07b01500479ad7f01f43d57a3840efae99f38678416a287e102754dbc121b859b26f8b0304a610759ff28a1bfa9412042f97976d7c4
6
+ metadata.gz: f053a42f438109874ac1997c974b9528cada2c4e2bdb1bfe0bfe87ab9279f60eedfe046840704b27724836bf0df9fc559d5a0536f8c068cc490c7b98b29dc773
7
+ data.tar.gz: 4494b55bfe4aed431f141b5789eb68debb938b97c1269263096e4f377805529f791c76cabe05983c7b1bc36e24511f25359bfd232e4c9c36393c9410a2649787
@@ -48,19 +48,41 @@ class Api
48
48
  # Api::RemoteResource.get!("foo") returns a new RemoteResource or raises an error
49
49
  # Api::RemoteResource.new("foo").get! returns the RemoteResource or raises an error
50
50
  #
51
- # #post, #post!, #put, #put!, #delete, and #delete! are also available.
52
- #
53
- # If get or get! retrieve an Ocean collection, they will return an array of RemoteResources.
54
- #
55
51
  # +thing.get!+ returns the RemoteResource if successful. If not, raises an exception.
56
52
  # +thing.get+ does the same, but always returns the RemoteResource. The remote resource
57
53
  # can be examined to see its status.
58
54
  #
55
+ # Hyperlinks can be specified to avoid hardcoding API URIs. This is essential. Simply specify
56
+ # the hyperlink as the first parameter to the instance versions of the HTTP accessors:
57
+ #
58
+ # x = Api::RemoteResource.new("http://foo.com/x")
59
+ # x.get(:creator)
60
+ # x.get!(:creator)['username']
61
+ # x.put(:confirm, body: {x:2, y:3})
62
+ # x.post(:bars, body: {username: "Bl0feld", password: "SPECTRE"})
63
+ # x.delete(:expired)
64
+ #
65
+ # Note the difference between:
66
+ #
67
+ # x.put(body: {x:2, y:3})
68
+ # x.put(:self, body: {x:2, y:3})
69
+ # x.put(:confirm, body: {x:2, y:3})
70
+ #
71
+ # The first two are logically equivalent: they both make a PUT request to x itself,
72
+ # though in the second case, the self hyperlink is explicit (the first arg defaults to
73
+ # :self). The third case sends a PUT request to x's hyperlink :confirm. From this follows
74
+ # that the following also are equivalent:
75
+ #
76
+ # x.get
77
+ # x.get(:self)
78
+ #
79
+ # #post, #post!, #put, #put!, #delete, and #delete! are also available.
80
+ #
59
81
  # Exceptions:
60
82
  #
61
- # GetFailed raised when the GET to obtain the remote resource has failed.
62
- # UnparseableJson raised when the response body doesn't parse as JSON.
63
- # JsonIsNoResource raised when the structure of the parsed JSON body is not a resource.
83
+ # GetFailed raised when the GET to obtain the remote resource has failed.
84
+ # UnparseableJson raised when the response body doesn't parse as JSON.
85
+ # JsonIsNoResource raised when the structure of the parsed JSON body is not a resource.
64
86
  #
65
87
  # To parse as a resource, the JSON must be a wrapped Ocean resource of this format:
66
88
  #
@@ -79,8 +101,12 @@ class Api
79
101
  #
80
102
  # To refresh a resource using a conditional GET:
81
103
  #
82
- # +thing.refresh+
83
- # +thing.refresh!+
104
+ # thing.refresh
105
+ # thing.refresh!
106
+ #
107
+ # #refresh returns the resource itself, so you can do
108
+ #
109
+ # thing.refresh['updated_at']
84
110
  #
85
111
  # NB: this class can also be used to fetch any JSON data. E.g.:
86
112
  #
@@ -91,6 +117,9 @@ class Api
91
117
  # +nil+, you can always chack +#status+, +#status_message+, and/or +#headers+ to determine what
92
118
  # went wrong. After fetching non-resource data, +#present?+ will always be false.
93
119
  #
120
+ # TODO: 1. If .get or .get! retrieve an Ocean collection, let them return an array of RemoteResources.
121
+ # 2. Implement refresh.
122
+ #
94
123
  class RemoteResource
95
124
 
96
125
  attr_reader :uri, :content_type, :retries, :backoff_time, :backoff_rate, :backoff_max
@@ -123,26 +152,6 @@ class Api
123
152
  end
124
153
 
125
154
 
126
- def self.get!(*args)
127
- _retrieve new(*args)
128
- end
129
-
130
- def get!
131
- RemoteResource._retrieve self unless present?
132
- self
133
- end
134
-
135
- def self.get(*args)
136
- rr = new(*args)
137
- rr.get! rescue nil
138
- rr
139
- end
140
-
141
- def get
142
- get! rescue nil
143
- self
144
- end
145
-
146
155
  def [](key)
147
156
  get!
148
157
  resource[key]
@@ -165,18 +174,108 @@ class Api
165
174
  end
166
175
 
167
176
 
177
+ class PostFailed < StandardError; end
168
178
  class GetFailed < StandardError; end
179
+ class PutFailed < StandardError; end
180
+ class DeleteFailed < StandardError; end
169
181
  class UnparseableJson < StandardError; end
170
182
  class JsonIsNoResource < StandardError; end
171
183
 
172
184
 
185
+ def self.get!(*args)
186
+ _retrieve new(*args)
187
+ end
188
+
189
+ def self.get(*args)
190
+ rr = new(*args)
191
+ rr.get! rescue nil
192
+ rr
193
+ end
194
+
195
+
196
+ def get!(hlink=nil)
197
+ RemoteResource._retrieve self unless present?
198
+ hlink = hlink.to_s if hlink
199
+ if !hlink || hlink == 'self'
200
+ self
201
+ else
202
+ RemoteResource.get!(hyperlink[hlink]['href'])
203
+ end
204
+ end
205
+
206
+ def get(hlink=nil)
207
+ get! rescue nil
208
+ hlink = hlink.to_s if hlink
209
+ if !hlink || hlink == 'self'
210
+ self
211
+ else
212
+ RemoteResource.get(hyperlink[hlink]['href'])
213
+ end
214
+ end
215
+
216
+
217
+ def put!(hlink=nil, body: {})
218
+ get!
219
+ hlink = hlink.to_s if hlink
220
+ if !hlink || hlink == 'self'
221
+ _modify(body)
222
+ self
223
+ else
224
+ hl_res = RemoteResource.new(self.hyperlink[hlink]['href'])
225
+ hl_res.send :_modify, body
226
+ hl_res
227
+ end
228
+ end
229
+
230
+ def put(hlink=nil, body: {})
231
+ get
232
+ hlink = hlink.to_s if hlink
233
+ if !hlink || hlink == 'self'
234
+ _modify(body) rescue nil
235
+ self
236
+ else
237
+ hl_res = RemoteResource.new(self.hyperlink[hlink]['href'])
238
+ hl_res.send :_modify, body
239
+ hl_res
240
+ end
241
+ end
242
+
243
+
244
+ def post!(hlink=nil, body: {})
245
+ get!
246
+ hlink = (hlink || 'self').to_s
247
+ _create(hyperlink[hlink]['href'], body)
248
+ end
249
+
250
+ def post(hlink=nil, body: {})
251
+ get
252
+ hlink = (hlink || 'self').to_s
253
+ _create(hyperlink[hlink]['href'], body) rescue nil
254
+ end
255
+
256
+
257
+ def delete!(hlink=nil)
258
+ get!
259
+ hlink = (hlink || 'self').to_s
260
+ _destroy(hyperlink[hlink]['href'])
261
+ self
262
+ end
263
+
264
+ def delete(hlink=nil)
265
+ get
266
+ hlink = (hlink || 'self').to_s
267
+ _destroy(hyperlink[hlink]['href']) rescue nil
268
+ self
269
+ end
270
+
271
+
173
272
  private
174
273
 
175
274
  attr_accessor :present
176
275
  attr_writer :raw, :resource, :resource_type, :status, :headers, :status_message
177
276
 
178
277
 
179
- def self._retrieve(rr)
278
+ def self._credentials(rr)
180
279
  if rr.credentials.present? && rr.credentials != Api.credentials(API_USER, API_PASSWORD)
181
280
  credentials = rr.credentials
182
281
  token = rr.x_api_token
@@ -184,6 +283,12 @@ class Api
184
283
  credentials = nil
185
284
  token = rr.x_api_token || Api.service_token
186
285
  end
286
+ [credentials, token]
287
+ end
288
+
289
+
290
+ def self._retrieve(rr)
291
+ credentials, token = _credentials(rr)
187
292
  response = Api.request rr.uri, :get, headers: {}, credentials: credentials, x_api_token: token,
188
293
  retries: rr.retries, backoff_time: rr.backoff_time, backoff_rate: rr.backoff_rate,
189
294
  backoff_max: rr.backoff_max
@@ -203,16 +308,9 @@ class Api
203
308
 
204
309
  def _setup(body, response)
205
310
  self.raw = body
206
- raise JsonIsNoResource unless raw.is_a? Hash
207
- raise JsonIsNoResource unless raw.size == 1
208
- resource_type, value = raw.to_a.first
209
- raise JsonIsNoResource unless value.is_a? Hash
210
- raise JsonIsNoResource unless value['_links'].is_a? Hash
211
- raise JsonIsNoResource unless value['_links']['self'].is_a? Hash
212
- raise JsonIsNoResource unless value['_links']['self']['href'].is_a? String
213
- raise JsonIsNoResource unless value['_links']['self']['type'].is_a? String
214
- self.resource = value
215
- self.resource_type = resource_type
311
+ type, resource = verify_resource body
312
+ self.resource = resource
313
+ self.resource_type = type
216
314
  self.status = response.status
217
315
  self.status_message = response.message
218
316
  self.headers = response.headers
@@ -220,5 +318,72 @@ class Api
220
318
  end
221
319
 
222
320
 
321
+ def verify_resource(json)
322
+ raise JsonIsNoResource unless json.is_a? Hash
323
+ raise JsonIsNoResource unless json.size == 1
324
+ resource_type, attributes = json.to_a.first
325
+ raise JsonIsNoResource unless attributes.is_a? Hash
326
+ raise JsonIsNoResource unless attributes['_links'].is_a? Hash
327
+ raise JsonIsNoResource unless attributes['_links']['self'].is_a? Hash
328
+ raise JsonIsNoResource unless attributes['_links']['self']['href'].is_a? String
329
+ raise JsonIsNoResource unless attributes['_links']['self']['type'].is_a? String
330
+ [resource_type, attributes]
331
+ end
332
+
333
+
334
+ def _modify(body)
335
+ credentials, token = RemoteResource._credentials(self)
336
+ response = Api.request uri, :put, headers: {}, body: body,
337
+ credentials: credentials, x_api_token: token,
338
+ retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
339
+ backoff_max: backoff_max
340
+ raise PutFailed unless response.success?
341
+ begin
342
+ self.raw = response.body
343
+ rescue JSON::ParserError
344
+ raise UnparseableJson
345
+ end
346
+ type, resource = verify_resource(response.body) rescue return
347
+ return if resource_type && type != resource_type
348
+ self.resource = resource
349
+ self.resource_type = type
350
+ self.status = response.status
351
+ self.status_message = response.message
352
+ self.headers = response.headers
353
+ self.present = true
354
+ end
355
+
356
+
357
+ def _create(post_uri, body)
358
+ credentials, token = RemoteResource._credentials(self)
359
+ response = Api.request post_uri, :post, headers: {}, body: body,
360
+ credentials: credentials, x_api_token: token,
361
+ retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
362
+ backoff_max: backoff_max
363
+ raise PostFailed unless response.success?
364
+ begin
365
+ raw = response.body
366
+ rescue JSON::ParserError
367
+ raise UnparseableJson
368
+ end
369
+ type, resource = verify_resource(response.body)
370
+ created = RemoteResource.new(post_uri)
371
+ created.send :_setup, raw, response
372
+ created
373
+ end
374
+
375
+
376
+ def _destroy(delete_uri)
377
+ credentials, token = RemoteResource._credentials(self)
378
+ response = Api.request delete_uri, :delete, headers: {},
379
+ credentials: credentials, x_api_token: token,
380
+ retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
381
+ backoff_max: backoff_max
382
+ self.status = response.status
383
+ self.status_message = response.message
384
+ self.headers = response.headers
385
+ raise DeleteFailed unless response.success?
386
+ end
387
+
223
388
  end
224
389
  end
@@ -83,25 +83,6 @@ module OceanApplicationController
83
83
  end
84
84
 
85
85
 
86
- # #
87
- # # Requires the request to be conditional: it must have an +If-None-Match+ and/or an
88
- # # +If-Modified-Since+ HTTP header. If the request isn't conditional, a 428 error is
89
- # # returned. The body will be a standard API error message, with two strings:
90
- # # <tt>"Precondition Required"</tt> and <tt>"If-None-Match and/or If-Modified-Since missing"</tt>.
91
- # #
92
- # def require_conditional
93
- # if request.headers['If-None-Match'].blank? && request.headers['If-Modified-Since'].blank?
94
- # render_api_error 428, "Precondition Required",
95
- # "If-None-Match and/or If-Modified-Since missing"
96
- # #expires_in 0, must_revalidate: true
97
- # expires_now
98
- # false
99
- # else
100
- # true
101
- # end
102
- # end
103
-
104
-
105
86
  #
106
87
  # Updates +created_by+ and +updated_by+ to the ApiUser for which the current request
107
88
  # is authorised. The attributes can be declared either String (recommended) or
data/lib/ocean/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ocean
2
- VERSION = "3.6.1"
2
+ VERSION = "3.7.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ocean-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.1
4
+ version: 3.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Bengtson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-02 00:00:00.000000000 Z
11
+ date: 2014-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus