spyke 1.8.11 → 2.0.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: 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