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 +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
|