spyke 4.1.1 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 25dd79a32574d3585c1f3823b1306458bc1b389b
4
- data.tar.gz: a644d3fba35342f673d257bea9e1ce2f1f324ca0
3
+ metadata.gz: 2a430604d1097764cd8add4c5b0f44161e5939d4
4
+ data.tar.gz: af8c8efb25d69ee8555510408edc393b5f8d1e5d
5
5
  SHA512:
6
- metadata.gz: a796e6db458d43dc92511cbc877493fa43a7280ced8d843f4150bf3fabb3f598fd433f949fc310c34489e709d02f94564e8fe52d34f6f7e07912ab77595c4a4f
7
- data.tar.gz: a6fe1f3e863c4aa305cff5b4cde884e9666c0ea0bbf90c3a4f103d220a9f42114af1061bae3fcf20ba956a2f301a648a06d04af6a3d3894c8c3cd5312046abd3
6
+ metadata.gz: 805690aac305cad08aeb0d8f34a63ca17aab8296080d42c51cb9f23117679592c3e8e1addf867595e2d1f82e19ad7607a06b1e2b6c8adf99b7dd4c2a3625d551
7
+ data.tar.gz: 8337033aea0abef30be2ce84d83e80c947ee643eb3a0214fb6637f8a1404b36c5f1f67c7d02e06b0429aa027010d737aa8a705f9bf92c6cc87d6a199b886892b
data/README.md CHANGED
@@ -167,6 +167,24 @@ remap it in Faraday to match the above. Doing this will allow you to
167
167
  show errors returned from the server in forms and f.ex using
168
168
  `@post.errors.full_messages` just like ActiveRecord.
169
169
 
170
+ ### Error handling and fallbacks
171
+
172
+ Should the API fail to connect or time out, a `Spyke::ConnectionError` will be raised.
173
+ If you need to recover gracefully from connection problems, you can
174
+ either rescue that exception or use the `with_fallback` feature:
175
+
176
+ ```ruby
177
+ # API is down
178
+ Article.all # => Spyke::ConnectionError
179
+ Article.with_fallback.all # => []
180
+
181
+ Article.find(1) # => Spyke::ConnectionError
182
+ Article.with_fallback.find(1) # => nil
183
+
184
+ article = Article.with_fallback(Article.new(title: "Dummy")).find(1)
185
+ article.title # => "Dummy"
186
+ ```
187
+
170
188
  ### Attributes-wrapping
171
189
 
172
190
  Spyke, like Rails, by default wraps sent attributes in a root element,
data/circle.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  machine:
2
2
  ruby:
3
- version: 2.1.5
3
+ version: 2.2.2
@@ -1,3 +1,4 @@
1
1
  module Spyke
2
2
  class ResourceNotFound < StandardError; end
3
+ class ConnectionError < StandardError; end
3
4
  end
data/lib/spyke/http.rb CHANGED
@@ -22,14 +22,7 @@ module Spyke
22
22
 
23
23
  def request(method, path, params = {})
24
24
  ActiveSupport::Notifications.instrument('request.spyke', method: method) do |payload|
25
- response = connection.send(method) do |request|
26
- if method == :get
27
- request.url path.to_s, params
28
- else
29
- request.url path.to_s
30
- request.body = params
31
- end
32
- end
25
+ response = send_request(method, path, params)
33
26
  payload[:url], payload[:status] = response.env.url, response.status
34
27
  Result.new_from_response(response)
35
28
  end
@@ -49,6 +42,19 @@ module Spyke
49
42
 
50
43
  private
51
44
 
45
+ def send_request(method, path, params)
46
+ connection.send(method) do |request|
47
+ if method == :get
48
+ request.url path.to_s, params
49
+ else
50
+ request.url path.to_s
51
+ request.body = params
52
+ end
53
+ end
54
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError
55
+ raise ConnectionError
56
+ end
57
+
52
58
  def scoped_request(method)
53
59
  uri = new.uri
54
60
  params = current_scope.params.except(*uri.variables)
@@ -9,7 +9,10 @@ module Spyke
9
9
  delegate :to_ary, :[], :any?, :empty?, :last, :size, :metadata, to: :find_some
10
10
 
11
11
  def initialize(klass, options = {})
12
- @klass, @options, @params = klass, options, {}
12
+ @klass = klass
13
+ @options = options
14
+ @params = {}
15
+ @should_fallback = false
13
16
  end
14
17
 
15
18
  def where(conditions = {})
@@ -27,6 +30,12 @@ module Spyke
27
30
  where
28
31
  end
29
32
 
33
+ def with_fallback(fallback = nil)
34
+ @should_fallback = true
35
+ @fallback = fallback
36
+ where
37
+ end
38
+
30
39
  # Overrides Enumerable find
31
40
  def find(id)
32
41
  scoping { klass.find(id) }
@@ -34,10 +43,14 @@ module Spyke
34
43
 
35
44
  def find_one
36
45
  @find_one ||= klass.new_instance_from_result(fetch)
46
+ rescue ConnectionError => error
47
+ fallback_or_reraise(error, default: nil)
37
48
  end
38
49
 
39
50
  def find_some
40
51
  @find_some ||= klass.new_collection_from_result(fetch)
52
+ rescue ConnectionError => error
53
+ fallback_or_reraise(error, default: [])
41
54
  end
42
55
 
43
56
  def each(&block)
@@ -65,5 +78,17 @@ module Spyke
65
78
  ensure
66
79
  klass.current_scope = previous
67
80
  end
81
+
82
+ def fallback_or_reraise(error, default:)
83
+ if should_fallback?
84
+ @fallback || default
85
+ else
86
+ raise error
87
+ end
88
+ end
89
+
90
+ def should_fallback?
91
+ @should_fallback
92
+ end
68
93
  end
69
94
  end
data/lib/spyke/scoping.rb CHANGED
@@ -7,7 +7,7 @@ module Spyke
7
7
 
8
8
  module ClassMethods
9
9
  delegate :where, :build, :any?, :empty?, to: :all
10
- delegate :with, to: :all
10
+ delegate :with, :with_fallback, to: :all
11
11
 
12
12
  def all
13
13
  current_scope || Relation.new(self, uri: uri)
data/lib/spyke/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spyke
2
- VERSION = '4.1.1'
2
+ VERSION = '5.0.0'
3
3
  end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ module Spyke
4
+ class FallbacksTest < MiniTest::Test
5
+ def setup
6
+ stub_request(:get, "http://sushi.com/recipes/1").to_timeout
7
+ stub_request(:get, "http://sushi.com/recipes?published=true").to_timeout
8
+ end
9
+
10
+ def test_find_with_default_fallback
11
+ assert_raises ResourceNotFound do
12
+ Recipe.with_fallback.find(1)
13
+ end
14
+ end
15
+
16
+ def test_find_with_custom_fallback
17
+ dummy_recipe = Recipe.new(title: "Dummy Recipe")
18
+
19
+ result = Recipe.with_fallback(dummy_recipe).find(1)
20
+
21
+ assert_equal "Dummy Recipe", result.title
22
+ end
23
+
24
+ def test_find_one_with_default_fallback
25
+ recipe = Recipe.with_fallback.where(id: 1).find_one
26
+
27
+ assert_equal nil, recipe
28
+ end
29
+
30
+ def test_find_some_with_default_fallback
31
+ assert_equal [], Recipe.where(published: true).with_fallback.all.to_a
32
+ end
33
+
34
+ def test_find_some_with_custom_fallback
35
+ dummy_result = [Recipe.new(title: "Dummy Recipe")]
36
+
37
+ result = Recipe.where(published: true).with_fallback(dummy_result).all
38
+
39
+ assert_equal "Dummy Recipe", result.first.title
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spyke
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.1
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Balvig
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-12 00:00:00.000000000 Z
11
+ date: 2017-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -293,6 +293,7 @@ files:
293
293
  - test/attributes_test.rb
294
294
  - test/callbacks_test.rb
295
295
  - test/custom_request_test.rb
296
+ - test/fallbacks_test.rb
296
297
  - test/orm_test.rb
297
298
  - test/path_test.rb
298
299
  - test/scopes_test.rb
@@ -319,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
320
  version: '0'
320
321
  requirements: []
321
322
  rubyforge_project:
322
- rubygems_version: 2.5.1
323
+ rubygems_version: 2.5.2
323
324
  signing_key:
324
325
  specification_version: 4
325
326
  summary: Interact with REST services in an ActiveRecord-like manner
@@ -328,6 +329,7 @@ test_files:
328
329
  - test/attributes_test.rb
329
330
  - test/callbacks_test.rb
330
331
  - test/custom_request_test.rb
332
+ - test/fallbacks_test.rb
331
333
  - test/orm_test.rb
332
334
  - test/path_test.rb
333
335
  - test/scopes_test.rb