spyke 1.8.11 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c05f29726a60edf95e40f701f6e1c25b362eb56
4
- data.tar.gz: 373739290398204bd2954fe21b623fd5812af70c
3
+ metadata.gz: debb215522b638dd3b33070246527635aabfc284
4
+ data.tar.gz: 5e02ddc322d6a4811229cf939616eb5227e0d90c
5
5
  SHA512:
6
- metadata.gz: deab62cb350a993a9a563a6a92594238048f1a9e908d6767e4fb4b95e8e15cb9b41a71316c058186a0622de767c99cdb3cb7057c957ecde28da62d6bb3b71750
7
- data.tar.gz: 19f8ec5668c71e2c588a28da72e87fdaa1e64346018998aac3be60217746752813e2dec35c43c03ca9a7dfa1a9c60ed7dacd92996d5648d28d4da49a011c1853
6
+ metadata.gz: 123c24f04e9f34ed7f0f3a3335c94573266f39171d9c2183d1125d7235ed239a44766e6cf9771bcd6ca40e41ac8411756551777b215b27438def9ee1be233d44
7
+ data.tar.gz: ebf9567257ae4c3b2ec20c7a837b667f2107f1877d3c18471ec886779b25916f7de0617ca37919a6a3da7c5384b29ec46ad6808a1b1d1ef8c74ec7d6d4ad2cff
data/README.md CHANGED
@@ -58,12 +58,10 @@ class JSONParser < Faraday::Response::Middleware
58
58
  metadata: json[:extra],
59
59
  errors: json[:errors]
60
60
  }
61
- rescue MultiJson::ParseError => exception
62
- { errors: { base: [ error: exception.message ] } }
63
61
  end
64
62
  end
65
63
 
66
- Spyke::Config.connection = Faraday.new(url: 'http://api.com') do |c|
64
+ Spyke::Base.connection = Faraday.new(url: 'http://api.com') do |c|
67
65
  c.request :json
68
66
  c.use JSONParser
69
67
  c.adapter Faraday.default_adapter
@@ -77,7 +75,6 @@ Adding a class and inheriting from `Spyke::Base` will allow you to interact with
77
75
  ```ruby
78
76
  class User < Spyke::Base
79
77
  has_many :posts
80
-
81
78
  scope :active, -> { where(active: true) }
82
79
  end
83
80
 
@@ -108,7 +105,9 @@ User.create(name: 'Bob')
108
105
 
109
106
  ### Custom URIs
110
107
 
111
- You can specify custom URIs on both the class and association level:
108
+ You can specify custom URIs on both the class and association level.
109
+ Set uri to `nil` for associations you only want to use embedded JSON
110
+ and never call out to the API.
112
111
 
113
112
  ```ruby
114
113
  class User < Spyke::Base
@@ -127,23 +126,37 @@ user.posts # => GET http://api.com/posts/for_user/3
127
126
  Post.find(4) # => GET http://api.com/posts/4
128
127
  ```
129
128
 
130
- ### Log output
129
+ ### Custom requests
131
130
 
132
- When used with Rails, Spyke will automatically output helpful
133
- ActiveRecord-like messages to the main log:
131
+ Custom request methods and the `using` scope methods allow you to
132
+ perform requests for non-REST actions:
134
133
 
135
- ```bash
136
- Started GET "/posts" for 127.0.0.1 at 2014-12-01 14:31:20 +0000
137
- Processing by PostsController#index as HTML
138
- Parameters: {}
139
- Spyke (40.3ms) GET http://api.com/posts [200]
140
- Completed 200 OK in 75ms (Views: 64.6ms | Spyke: 40.3ms | ActiveRecord: 0ms)
134
+ The `.using` scope:
135
+
136
+ ```ruby
137
+ Post.using('posts/recent') # => GET http://api.com/posts/recent
138
+ Post.using(:recent) # => GET http://api.com/posts/recent
139
+ Post.using(:recent).where(status: 'draft') # => GET http://api.com/posts/recent?status=draft
140
+ Post.using(:recent).post # => POST http://api.com/posts/recent
141
+ ```
142
+
143
+ Custom requests from instance:
144
+
145
+ ```ruby
146
+ Post.find(3).put(:publish) # => PUT http://api.com/posts/3/publish
147
+ ```
148
+
149
+ Arbitrary requests (returns plain Result object):
150
+
151
+ ```ruby
152
+ Post.request(:post, 'posts/3/log', time: '12:00')
153
+ # => POST http://api.com/posts/3/log - { time: '12:00' }
141
154
  ```
142
155
 
143
156
  ### API-side validations
144
157
 
145
158
  Spyke expects errors to be formatted in the same way as the
146
- ActiveModel::Errors hash, ie:
159
+ [ActiveModel::Errors details hash](https://cowbell-labs.com/2015-01-22-active-model-errors-details.html), ie:
147
160
 
148
161
  ```ruby
149
162
  { title: [{ error: 'blank'}, { error: 'too_short', count: 10 }]}
@@ -154,7 +167,39 @@ remap it in Faraday to match the above. Doing this will allow you to
154
167
  show errors returned from the server in forms and f.ex using
155
168
  `@post.errors.full_messages` just like ActiveRecord.
156
169
 
170
+ ### Using multiple APIs
171
+
172
+ If you need to use different APIs, instead of configuring `Spyke::Base`
173
+ you can configure each class individually:
174
+
175
+ ```ruby
176
+ class Post < Spyke::Base
177
+ self.connection = Faraday.new(url: 'http://sashimi.com') do |faraday|
178
+ # middleware
179
+ end
180
+ end
181
+ ```
182
+
183
+ ### Log output
184
+
185
+ When used with Rails, Spyke will automatically output helpful
186
+ ActiveRecord-like messages to the main log:
187
+
188
+ ```bash
189
+ Started GET "/posts" for 127.0.0.1 at 2014-12-01 14:31:20 +0000
190
+ Processing by PostsController#index as HTML
191
+ Parameters: {}
192
+ Spyke (40.3ms) GET http://api.com/posts [200]
193
+ Completed 200 OK in 75ms (Views: 64.6ms | Spyke: 40.3ms | ActiveRecord: 0ms)
194
+ ```
195
+
196
+ ### Other examples
197
+
198
+ For more examples of how Spyke can be used, check out [fixtures.rb](https://github.com/balvig/spyke/blob/master/test/support/fixtures.rb) and the
199
+ [test suite](https://github.com/balvig/spyke/tree/master/test).
200
+
201
+
157
202
  ## Contributing
158
203
 
159
204
  If possible please take a look at the [tests marked "wishlisted"](https://github.com/balvig/spyke/search?l=ruby&q=wishlisted&utf8=%E2%9C%93)!
160
- These are features/fixes we want to implement but haven't gotten around to doing yet :)
205
+ These are features/fixes I'd like to implement but haven't gotten around to doing yet :)
data/lib/spyke/config.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  module Spyke
2
2
  class Config
3
- class_attribute :connection
3
+ def self.connection=(faraday)
4
+ warn "[DEPRECATION] `Spyke::Config.connection=` is deprecated. Please use `Spyke::Base.connection=` instead."
5
+ Spyke::Base.connection = faraday
6
+ end
4
7
  end
5
8
  end
data/lib/spyke/http.rb CHANGED
@@ -9,14 +9,29 @@ module Spyke
9
9
  extend ActiveSupport::Concern
10
10
  METHODS = %i{ get post put patch delete }
11
11
 
12
+ included do
13
+ class_attribute :connection, instance_accessor: false
14
+ end
15
+
12
16
  module ClassMethods
13
17
  METHODS.each do |method|
14
- define_method(method) do |path, params = {}|
15
- new_instance_or_collection_from_result send("#{method}_raw", path, params)
18
+ define_method(method) do
19
+ new_instance_or_collection_from_result scoped_request(method)
16
20
  end
21
+ end
17
22
 
18
- define_method("#{method}_raw") do |path, params = {}|
19
- request(method, path, params)
23
+ def request(method, path, params = {})
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
33
+ payload[:url], payload[:status] = response.env.url, response.status
34
+ Result.new_from_response(response)
20
35
  end
21
36
  end
22
37
 
@@ -29,28 +44,15 @@ module Spyke
29
44
  end
30
45
 
31
46
  def uri(uri_template = nil)
32
- @uri ||= uri_template || superclass_uri || default_uri
33
- end
34
-
35
- def connection
36
- Config.connection
47
+ @uri ||= uri_template || default_uri
37
48
  end
38
49
 
39
50
  private
40
51
 
41
- def request(method, path, params = {})
42
- ActiveSupport::Notifications.instrument('request.spyke', method: method) do |payload|
43
- response = connection.send(method) do |request|
44
- if method == :get
45
- request.url path.to_s, params
46
- else
47
- request.url path.to_s
48
- request.body = params
49
- end
50
- end
51
- payload[:url], payload[:status] = response.env.url, response.status
52
- Result.new_from_response(response)
53
- end
52
+ def scoped_request(method)
53
+ uri = new.uri
54
+ params = current_scope.params.except(*uri.variables)
55
+ request(method, uri, params)
54
56
  end
55
57
 
56
58
  def new_instance_or_collection_from_result(result)
@@ -69,10 +71,6 @@ module Spyke
69
71
  end
70
72
  end
71
73
 
72
- def superclass_uri
73
- superclass.uri.dup.freeze if superclass != Base
74
- end
75
-
76
74
  def default_uri
77
75
  "#{model_name.plural}/(:id)"
78
76
  end
@@ -83,7 +81,7 @@ module Spyke
83
81
  params = action if action.is_a?(Hash)
84
82
  path = resolve_path_from_action(action)
85
83
 
86
- result = self.class.send("#{method}_raw", path, params)
84
+ result = self.class.request(method, path, params)
87
85
 
88
86
  add_errors_to_model(result.errors)
89
87
  self.attributes = result.data
data/lib/spyke/orm.rb CHANGED
@@ -8,7 +8,7 @@ module Spyke
8
8
  class_attribute :include_root
9
9
  self.include_root = true
10
10
 
11
- class_attribute :callback_methods
11
+ class_attribute :callback_methods, instance_accessor: false
12
12
  self.callback_methods = { create: :post, update: :put }.freeze
13
13
  end
14
14
 
@@ -29,8 +29,7 @@ module Spyke
29
29
  end
30
30
 
31
31
  def fetch
32
- uri = new.uri
33
- get_raw uri, current_scope.params.except(*uri.variables)
32
+ scoped_request :get
34
33
  end
35
34
 
36
35
  def create(attributes = {})
@@ -18,6 +18,15 @@ module Spyke
18
18
  relation
19
19
  end
20
20
 
21
+ def using(uri)
22
+ if uri.is_a? Symbol
23
+ @options[:uri] = File.join @options[:uri], uri.to_s
24
+ else
25
+ @options[:uri] = uri
26
+ end
27
+ where
28
+ end
29
+
21
30
  # Overrides Enumerable find
22
31
  def find(id)
23
32
  scoping { klass.find(id) }
data/lib/spyke/scoping.rb CHANGED
@@ -7,6 +7,7 @@ module Spyke
7
7
 
8
8
  module ClassMethods
9
9
  delegate :where, :build, :any?, :empty?, to: :all
10
+ delegate :using, to: :all
10
11
 
11
12
  def all
12
13
  current_scope || Relation.new(self, uri: uri)
data/lib/spyke/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spyke
2
- VERSION = '1.8.11'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -2,7 +2,6 @@ require 'test_helper'
2
2
 
3
3
  module Spyke
4
4
  class AttributesTest < MiniTest::Test
5
-
6
5
  def test_block_initialization
7
6
  recipe = Recipe.new do |r|
8
7
  r.title = 'Sushi'
@@ -6,17 +6,18 @@ module Spyke
6
6
  stub_request(:any, /.*/)
7
7
  end
8
8
 
9
- def test_before_save
10
- Recipe.any_instance.expects(:before_save_callback)
9
+ def test_before_create
10
+ Recipe.any_instance.expects(:before_create_callback)
11
11
  Recipe.create
12
12
  end
13
13
 
14
- def test_before_create
15
- Recipe.any_instance.expects(:before_create_callback)
14
+ def test_before_save
15
+ Recipe.any_instance.expects(:before_save_callback)
16
16
  Recipe.create
17
17
  end
18
18
 
19
19
  def test_before_update
20
+ Recipe.any_instance.expects(:before_save_callback)
20
21
  Recipe.any_instance.expects(:before_update_callback)
21
22
  Recipe.new(id: 1).save
22
23
  end
@@ -2,36 +2,40 @@ require 'test_helper'
2
2
 
3
3
  module Spyke
4
4
  class CustomRequestTest < MiniTest::Test
5
- def test_custom_get_request_using_class_method
5
+ def test_custom_get_request_from_class
6
6
  endpoint = stub_request(:get, 'http://sushi.com/recipes/recent').to_return_json(result: [{ id: 1, title: 'Bread' }])
7
- recipes = Recipe.get('/recipes/recent')
7
+ recipes = Recipe.using('/recipes/recent').get
8
8
  assert_equal %w{ Bread }, recipes.map(&:title)
9
9
  assert_requested endpoint
10
10
  end
11
11
 
12
- def test_get_request_with_prepended_scope
13
- skip 'wishlisted'
12
+ def test_custom_put_request_from_class
13
+ endpoint = stub_request(:put, 'http://sushi.com/recipes/publish_all')
14
+ Recipe.using('/recipes/publish_all').put
15
+ assert_requested endpoint
16
+ end
17
+
18
+ def test_custom_request_with_prepended_scope
14
19
  endpoint = stub_request(:get, 'http://sushi.com/recipes/recent?status=published')
15
- Recipe.published.get('/recipes/recent')
20
+ Recipe.published.using('/recipes/recent').to_a
16
21
  assert_requested endpoint
17
22
  end
18
23
 
19
- def test_get_request_with_appended_scope
20
- skip 'wishlisted'
24
+ def test_custom_request_with_appended_scope
21
25
  endpoint = stub_request(:get, 'http://sushi.com/recipes/recent?status=published')
22
- Recipe.get('/recipes/recent').published.to_a
26
+ Recipe.using('/recipes/recent').published.to_a
23
27
  assert_requested endpoint
24
28
  end
25
29
 
26
- def test_custom_get_request_from_class
27
- endpoint = stub_request(:get, 'http://sushi.com/recipes/recent').to_return_json(result: [{ id: 1, title: 'Bread' }])
28
- assert_equal %w{ Bread }, Recipe.get('/recipes/recent').map(&:title)
30
+ def test_custom_request_with_symbol_and_appended_scope
31
+ endpoint = stub_request(:get, 'http://sushi.com/recipes/recent?status=published')
32
+ Recipe.using(:recent).published.to_a
29
33
  assert_requested endpoint
30
34
  end
31
35
 
32
- def test_custom_put_request_from_class
33
- endpoint = stub_request(:put, 'http://sushi.com/recipes/1/publish')
34
- Recipe.put('/recipes/1/publish')
36
+ def test_create_on_custom_request
37
+ endpoint = stub_request(:post, 'http://sushi.com/recipes/recent').with(body: { recipe: { status: 'published' } })
38
+ Recipe.using(:recent).published.create
35
39
  assert_requested endpoint
36
40
  end
37
41
 
@@ -50,5 +54,11 @@ module Spyke
50
54
  recipe.put(:draft)
51
55
  assert_requested endpoint
52
56
  end
57
+
58
+ def test_multiple_apis
59
+ endpoint = stub_request(:get, 'http://sashimi.com/other_recipes')
60
+ OtherRecipe.all.to_a
61
+ assert_requested endpoint
62
+ end
53
63
  end
54
64
  end
data/test/orm_test.rb CHANGED
@@ -113,12 +113,6 @@ module Spyke
113
113
  assert_requested endpoint
114
114
  end
115
115
 
116
- def test_inheritance_passes_on_custom_method_and_uri
117
- endpoint = stub_request(:put, 'http://sushi.com/images')
118
- StepImage.create
119
- assert_requested endpoint
120
- end
121
-
122
116
  def test_inheritance_not_overwriting_custom_uri
123
117
  endpoint = stub_request(:put, 'http://sushi.com/recipes/1/image')
124
118
  RecipeImage.where(recipe_id: 1).create
@@ -158,14 +152,14 @@ module Spyke
158
152
  end
159
153
 
160
154
  def test_relative_uris
161
- previous = Spyke::Config.connection.url_prefix
162
- Spyke::Config.connection.url_prefix = 'http://sushi.com/api/v2/'
155
+ previous = Spyke::Base.connection.url_prefix
156
+ Spyke::Base.connection.url_prefix = 'http://sushi.com/api/v2/'
163
157
 
164
158
  endpoint = stub_request(:get, 'http://sushi.com/api/v2/recipes')
165
159
  Recipe.all.to_a
166
160
  assert_requested endpoint
167
161
 
168
- Spyke::Config.connection.url_prefix = previous
162
+ Spyke::Base.connection.url_prefix = previous
169
163
  end
170
164
  end
171
165
  end
@@ -1,3 +1,24 @@
1
+ # Dummy api
2
+ class JSONParser < Faraday::Response::Middleware
3
+ def parse(body)
4
+ json = MultiJson.load(body, symbolize_keys: true)
5
+ {
6
+ data: json[:result],
7
+ metadata: json[:metadata],
8
+ errors: json[:errors]
9
+ }
10
+ rescue MultiJson::ParseError => exception
11
+ { errors: { base: [ error: exception.message ] } }
12
+ end
13
+ end
14
+
15
+ Spyke::Base.connection = Faraday.new(url: 'http://sushi.com') do |faraday|
16
+ faraday.request :json
17
+ faraday.use JSONParser
18
+ faraday.adapter Faraday.default_adapter
19
+ end
20
+
21
+ # Test classes
1
22
  class Recipe < Spyke::Base
2
23
  has_many :groups
3
24
  has_many :gallery_images, class_name: 'Image'
@@ -83,6 +104,15 @@ class Comment < Spyke::Base
83
104
  scope :approved, -> { where(comment_approved: true) }
84
105
  end
85
106
 
107
+ class OtherApi < Spyke::Base
108
+ self.connection = Faraday.new(url: 'http://sashimi.com') do |faraday|
109
+ faraday.use JSONParser
110
+ faraday.adapter Faraday.default_adapter
111
+ end
112
+ end
113
+
114
+ class OtherRecipe < OtherApi; end
115
+
86
116
  class Search
87
117
  def initialize(query)
88
118
  @query = query
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: 1.8.11
4
+ version: 2.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: 2015-03-02 00:00:00.000000000 Z
11
+ date: 2015-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -296,7 +296,6 @@ files:
296
296
  - test/orm_test.rb
297
297
  - test/path_test.rb
298
298
  - test/relation_test.rb
299
- - test/support/api.rb
300
299
  - test/support/fixtures.rb
301
300
  - test/support/webmock.rb
302
301
  - test/test_helper.rb
@@ -332,7 +331,6 @@ test_files:
332
331
  - test/orm_test.rb
333
332
  - test/path_test.rb
334
333
  - test/relation_test.rb
335
- - test/support/api.rb
336
334
  - test/support/fixtures.rb
337
335
  - test/support/webmock.rb
338
336
  - test/test_helper.rb
data/test/support/api.rb DELETED
@@ -1,19 +0,0 @@
1
- # Set up dummy api
2
- class JSONParser < Faraday::Response::Middleware
3
- def parse(body)
4
- json = MultiJson.load(body, symbolize_keys: true)
5
- {
6
- data: json[:result],
7
- metadata: json[:metadata],
8
- errors: json[:errors]
9
- }
10
- rescue MultiJson::ParseError => exception
11
- { errors: { base: [ error: exception.message ] } }
12
- end
13
- end
14
-
15
- Spyke::Config.connection = Faraday.new(url: 'http://sushi.com') do |faraday|
16
- faraday.request :json
17
- faraday.use JSONParser
18
- faraday.adapter Faraday.default_adapter
19
- end