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 +4 -4
- data/README.md +61 -16
- data/lib/spyke/config.rb +4 -1
- data/lib/spyke/http.rb +25 -27
- data/lib/spyke/orm.rb +2 -3
- data/lib/spyke/relation.rb +9 -0
- data/lib/spyke/scoping.rb +1 -0
- data/lib/spyke/version.rb +1 -1
- data/test/attributes_test.rb +0 -1
- data/test/callbacks_test.rb +5 -4
- data/test/custom_request_test.rb +24 -14
- data/test/orm_test.rb +3 -9
- data/test/support/fixtures.rb +30 -0
- metadata +2 -4
- data/test/support/api.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: debb215522b638dd3b33070246527635aabfc284
|
4
|
+
data.tar.gz: 5e02ddc322d6a4811229cf939616eb5227e0d90c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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::
|
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
|
-
###
|
129
|
+
### Custom requests
|
131
130
|
|
132
|
-
|
133
|
-
|
131
|
+
Custom request methods and the `using` scope methods allow you to
|
132
|
+
perform requests for non-REST actions:
|
134
133
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
205
|
+
These are features/fixes I'd like to implement but haven't gotten around to doing yet :)
|
data/lib/spyke/config.rb
CHANGED
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
|
15
|
-
new_instance_or_collection_from_result
|
18
|
+
define_method(method) do
|
19
|
+
new_instance_or_collection_from_result scoped_request(method)
|
16
20
|
end
|
21
|
+
end
|
17
22
|
|
18
|
-
|
19
|
-
|
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 ||
|
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
|
42
|
-
|
43
|
-
|
44
|
-
|
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.
|
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
|
-
|
33
|
-
get_raw uri, current_scope.params.except(*uri.variables)
|
32
|
+
scoped_request :get
|
34
33
|
end
|
35
34
|
|
36
35
|
def create(attributes = {})
|
data/lib/spyke/relation.rb
CHANGED
@@ -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
data/lib/spyke/version.rb
CHANGED
data/test/attributes_test.rb
CHANGED
data/test/callbacks_test.rb
CHANGED
@@ -6,17 +6,18 @@ module Spyke
|
|
6
6
|
stub_request(:any, /.*/)
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
Recipe.any_instance.expects(:
|
9
|
+
def test_before_create
|
10
|
+
Recipe.any_instance.expects(:before_create_callback)
|
11
11
|
Recipe.create
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
Recipe.any_instance.expects(:
|
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
|
data/test/custom_request_test.rb
CHANGED
@@ -2,36 +2,40 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
module Spyke
|
4
4
|
class CustomRequestTest < MiniTest::Test
|
5
|
-
def
|
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.
|
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
|
13
|
-
|
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.
|
20
|
+
Recipe.published.using('/recipes/recent').to_a
|
16
21
|
assert_requested endpoint
|
17
22
|
end
|
18
23
|
|
19
|
-
def
|
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.
|
26
|
+
Recipe.using('/recipes/recent').published.to_a
|
23
27
|
assert_requested endpoint
|
24
28
|
end
|
25
29
|
|
26
|
-
def
|
27
|
-
endpoint = stub_request(:get, 'http://sushi.com/recipes/recent')
|
28
|
-
|
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
|
33
|
-
endpoint = stub_request(:
|
34
|
-
Recipe.
|
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::
|
162
|
-
Spyke::
|
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::
|
162
|
+
Spyke::Base.connection.url_prefix = previous
|
169
163
|
end
|
170
164
|
end
|
171
165
|
end
|
data/test/support/fixtures.rb
CHANGED
@@ -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:
|
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-
|
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
|