ocean-rails 3.6.1 → 3.7.0

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