json_api_client 1.0.1 → 1.0.2
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27bde5e4eec74b2589b7ec2c121e799d40defbc9
|
4
|
+
data.tar.gz: 8a25b071e84fd39f17e0044eda38eff1107d75bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86f7e70883d0521db847055f539cc8adff437f375f76a113b6d7abe5f960f6b50b82edee08a789886e0f34aa7dc9a1d9a8f5657d1a342d97bb35427f2403196e
|
7
|
+
data.tar.gz: 8f7d8fc7e34e175bb7c3c5cf230d0bf00995bfbc07cb09d29890262f738b3b205cc45359ba80dcc8f04620b7f430c7c1d72beadf9dd4241833bf02850b340e89
|
data/README.md
CHANGED
@@ -8,9 +8,9 @@ This gem is meant to help you build an API client for interacting with REST APIs
|
|
8
8
|
|
9
9
|
You will want to create your own resource classes that inherit from `JsonApiClient::Resource` similar to how you would create an `ActiveRecord` class. You may also want to create your own abstract base class to share common behavior. Additionally, you will probably want to namespace your models. Namespacing your model will not affect the url routing to that resource.
|
10
10
|
|
11
|
-
```
|
11
|
+
```ruby
|
12
12
|
module MyApi
|
13
|
-
# this is an "abstract" base class that
|
13
|
+
# this is an "abstract" base class that
|
14
14
|
class Base < JsonApiClient::Resource
|
15
15
|
# set the api base url in an abstract base class
|
16
16
|
self.site = "http://example.com/"
|
@@ -31,7 +31,7 @@ By convention, we figure guess the resource route from the class name. In the ab
|
|
31
31
|
|
32
32
|
Some basic example usage:
|
33
33
|
|
34
|
-
```
|
34
|
+
```ruby
|
35
35
|
MyApi::Article.all
|
36
36
|
MyApi::Article.where(author_id: 1).find(2)
|
37
37
|
MyApi::Article.where(author_id: 1).all
|
@@ -62,33 +62,35 @@ All class level finders/creators should return a `JsonApiClient::ResultSet` whic
|
|
62
62
|
|
63
63
|
Out of the box, `json_api_client` handles server side validation only.
|
64
64
|
|
65
|
-
```
|
65
|
+
```ruby
|
66
66
|
User.create(name: "Bob", email_address: "invalid email")
|
67
|
-
=> false
|
67
|
+
# => false
|
68
68
|
|
69
69
|
user = User.new(name: "Bob", email_address: "invalid email")
|
70
70
|
user.save
|
71
|
-
=> false
|
71
|
+
# => false
|
72
72
|
|
73
73
|
# returns an error collector which is array-like
|
74
74
|
user.errors
|
75
|
-
=> ["Email address is invalid"]
|
75
|
+
# => ["Email address is invalid"]
|
76
76
|
|
77
77
|
# get all error titles
|
78
78
|
user.errors.full_messages
|
79
|
-
=> ["Email address is invalid"]
|
79
|
+
# => ["Email address is invalid"]
|
80
80
|
|
81
81
|
# get errors for a specific parameter
|
82
82
|
user.errors[:email_address]
|
83
|
-
=> ["Email address is invalid"]
|
83
|
+
# => ["Email address is invalid"]
|
84
84
|
|
85
85
|
user = User.find(1)
|
86
86
|
user.update_attributes(email_address: "invalid email")
|
87
|
-
=> false
|
87
|
+
# => false
|
88
|
+
|
88
89
|
user.errors
|
89
|
-
=> ["Email address is invalid"]
|
90
|
+
# => ["Email address is invalid"]
|
91
|
+
|
90
92
|
user.email_address
|
91
|
-
=> "invalid email"
|
93
|
+
# => "invalid email"
|
92
94
|
```
|
93
95
|
|
94
96
|
For now we are assuming that error sources are all parameters.
|
@@ -101,7 +103,7 @@ If you want to add client side validation, I suggest creating a form model class
|
|
101
103
|
|
102
104
|
If the response has a top level meta data section, we can access it via the `meta` accessor on `ResultSet`.
|
103
105
|
|
104
|
-
```
|
106
|
+
```ruby
|
105
107
|
# Example response:
|
106
108
|
{
|
107
109
|
"meta": {
|
@@ -119,9 +121,10 @@ If the response has a top level meta data section, we can access it via the `met
|
|
119
121
|
articles = Articles.all
|
120
122
|
|
121
123
|
articles.meta.copyright
|
122
|
-
=> "Copyright 2015 Example Corp."
|
124
|
+
# => "Copyright 2015 Example Corp."
|
125
|
+
|
123
126
|
articles.meta.authors
|
124
|
-
=> ["Yehuda Katz", "Steve Klabnik", "Dan Gebhardt"]
|
127
|
+
# => ["Yehuda Katz", "Steve Klabnik", "Dan Gebhardt"]
|
125
128
|
```
|
126
129
|
|
127
130
|
## Top-level Links
|
@@ -130,7 +133,7 @@ articles.meta.authors
|
|
130
133
|
|
131
134
|
If the resource returns top level links, we can access them via the `links` accessor on `ResultSet`.
|
132
135
|
|
133
|
-
```
|
136
|
+
```ruby
|
134
137
|
articles = Articles.find(1)
|
135
138
|
articles.links.related
|
136
139
|
```
|
@@ -141,41 +144,40 @@ You can force nested resource paths for your models by using a `belongs_to` asso
|
|
141
144
|
|
142
145
|
**Note: Using belongs_to is only necessary for setting a nested path.**
|
143
146
|
|
144
|
-
```
|
147
|
+
```ruby
|
145
148
|
module MyApi
|
146
149
|
class Account < JsonApiClient::Resource
|
147
|
-
|
150
|
+
belongs_to :user
|
148
151
|
end
|
149
152
|
end
|
150
153
|
|
151
154
|
# try to find without the nested parameter
|
152
155
|
MyApi::Account.find(1)
|
153
|
-
=> raises ArgumentError
|
156
|
+
# => raises ArgumentError
|
154
157
|
|
155
158
|
# makes request to /users/2/accounts/1
|
156
159
|
MyApi::Account.where(user_id: 2).find(1)
|
157
|
-
=> returns ResultSet
|
160
|
+
# => returns ResultSet
|
158
161
|
```
|
159
162
|
|
160
163
|
## Custom Methods
|
161
164
|
|
162
165
|
You can create custom methods on both collections (class method) and members (instance methods).
|
163
166
|
|
164
|
-
```
|
167
|
+
```ruby
|
165
168
|
module MyApi
|
166
169
|
class User < JsonApiClient::Resource
|
170
|
+
# GET /users/search
|
171
|
+
custom_endpoint :search, on: :collection, request_method: :get
|
167
172
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
# PUT /users/:id/verify
|
172
|
-
custom_endpoint :verify, on: :member, request_method: :put
|
173
|
+
# PUT /users/:id/verify
|
174
|
+
custom_endpoint :verify, on: :member, request_method: :put
|
173
175
|
end
|
174
176
|
end
|
175
177
|
|
176
178
|
# makes GET request to /users/search?name=Jeff
|
177
179
|
MyApi::User.search(name: 'Jeff')
|
178
|
-
=> <ResultSet of MyApi::User instances>
|
180
|
+
# => <ResultSet of MyApi::User instances>
|
179
181
|
|
180
182
|
user = MyApi::User.find(1)
|
181
183
|
# makes PUT request to /users/1/verify?foo=bar
|
@@ -188,7 +190,7 @@ user.verify(foo: 'bar')
|
|
188
190
|
|
189
191
|
If the response returns a [compound document](http://jsonapi.org/format/#document-structure-compound-documents), then we should be able to get the related resources.
|
190
192
|
|
191
|
-
```
|
193
|
+
```ruby
|
192
194
|
# makes request to /articles/1?include=author,comments.author
|
193
195
|
results = Article.includes(:author, :comments => :author).find(1)
|
194
196
|
|
@@ -200,24 +202,24 @@ authors = results.map(&:author)
|
|
200
202
|
|
201
203
|
[See specification](http://jsonapi.org/format/#fetching-sparse-fieldsets)
|
202
204
|
|
203
|
-
```
|
205
|
+
```ruby
|
204
206
|
# makes request to /articles?fields[articles]=title,body
|
205
207
|
article = Article.select("title,body").first
|
206
208
|
|
207
209
|
# should have fetched the requested fields
|
208
210
|
article.title
|
209
|
-
=> "Rails is Omakase"
|
211
|
+
# => "Rails is Omakase"
|
210
212
|
|
211
213
|
# should not have returned the created_at
|
212
214
|
article.created_at
|
213
|
-
=> raise NoMethodError
|
215
|
+
# => raise NoMethodError
|
214
216
|
```
|
215
217
|
|
216
218
|
## Sorting
|
217
219
|
|
218
220
|
[See specification](http://jsonapi.org/format/#fetching-sorting)
|
219
221
|
|
220
|
-
```
|
222
|
+
```ruby
|
221
223
|
# makes request to /people?sort=age
|
222
224
|
youngest = Person.sort(:age).all
|
223
225
|
|
@@ -234,7 +236,7 @@ oldest = Person.sort(age: :desc).all
|
|
234
236
|
|
235
237
|
### Requesting
|
236
238
|
|
237
|
-
```
|
239
|
+
```ruby
|
238
240
|
# makes request to /articles?page=2&per_page=30
|
239
241
|
articles = Article.page(2).per(30).to_a
|
240
242
|
|
@@ -248,7 +250,7 @@ articles = Article.paginate(page: 2, per_page: 30).to_a
|
|
248
250
|
|
249
251
|
If the response contains additional pagination links, you can also get at those:
|
250
252
|
|
251
|
-
```
|
253
|
+
```ruby
|
252
254
|
articles = Article.paginate(page: 2, per_page: 30).to_a
|
253
255
|
articles.pages.next
|
254
256
|
articles.pages.last
|
@@ -262,7 +264,7 @@ A `JsonApiClient::ResultSet` object should be paginatable with both `kaminari` a
|
|
262
264
|
|
263
265
|
[See specifiation](http://jsonapi.org/format/#fetching-filtering)
|
264
266
|
|
265
|
-
```
|
267
|
+
```ruby
|
266
268
|
# makes request to /people?filter[name]=Jeff
|
267
269
|
Person.where(name: 'Jeff').all
|
268
270
|
```
|
@@ -277,7 +279,7 @@ The added benefit of declaring your schema is that you can access fields before
|
|
277
279
|
|
278
280
|
### Example
|
279
281
|
|
280
|
-
```
|
282
|
+
```ruby
|
281
283
|
class User < JsonApiClient::Resource
|
282
284
|
property :name, type: :string
|
283
285
|
property :is_admin, type: :boolean, default: false
|
@@ -287,18 +289,20 @@ end
|
|
287
289
|
|
288
290
|
# default values
|
289
291
|
u = User.new
|
292
|
+
|
290
293
|
u.name
|
291
|
-
=> nil
|
294
|
+
# => nil
|
295
|
+
|
292
296
|
u.is_admin
|
293
|
-
=> false
|
297
|
+
# => false
|
298
|
+
|
294
299
|
u.points_accrued
|
295
|
-
=> 0
|
300
|
+
# => 0
|
296
301
|
|
297
302
|
# casting
|
298
303
|
u.average_points_per_day = "0.3"
|
299
304
|
u.average_points_per_day
|
300
|
-
=> 0.3
|
301
|
-
|
305
|
+
# => 0.3
|
302
306
|
```
|
303
307
|
|
304
308
|
### Types
|
@@ -321,7 +325,7 @@ Note : Do not map the primary key as int.
|
|
321
325
|
|
322
326
|
You can customize this path by changing your resource's `table_name`:
|
323
327
|
|
324
|
-
```
|
328
|
+
```ruby
|
325
329
|
module MyApi
|
326
330
|
class SomeResource < Base
|
327
331
|
def self.table_name
|
@@ -338,7 +342,7 @@ MyApi::SomeResource.all
|
|
338
342
|
|
339
343
|
You can configure your API client to use a custom connection that implementes the `run` instance method. It should return data that your parser can handle. The default connection class wraps Faraday and lets you add middleware.
|
340
344
|
|
341
|
-
```
|
345
|
+
```ruby
|
342
346
|
class NullConnection
|
343
347
|
def initialize(*args)
|
344
348
|
end
|
@@ -352,7 +356,6 @@ end
|
|
352
356
|
class CustomConnectionResource < TestResource
|
353
357
|
self.connection_class = NullConnection
|
354
358
|
end
|
355
|
-
|
356
359
|
```
|
357
360
|
|
358
361
|
#### Connection Options
|
@@ -360,10 +363,10 @@ end
|
|
360
363
|
You can configure your connection using Faraday middleware. In general, you'll want
|
361
364
|
to do this in a base model that all your resources inherit from:
|
362
365
|
|
363
|
-
```
|
366
|
+
```ruby
|
364
367
|
MyApi::Base.connection do |connection|
|
365
368
|
# set OAuth2 headers
|
366
|
-
connection.use
|
369
|
+
connection.use FaradayMiddleware::OAuth2, 'MYTOKEN'
|
367
370
|
|
368
371
|
# log responses
|
369
372
|
connection.use Faraday::Response::Logger
|
@@ -382,10 +385,10 @@ end
|
|
382
385
|
|
383
386
|
You can configure your API client to use a custom parser that implements the `parse` class method. It should return a `JsonApiClient::ResultSet` instance. You can use it by setting the parser attribute on your model:
|
384
387
|
|
385
|
-
```
|
388
|
+
```ruby
|
386
389
|
class MyCustomParser
|
387
390
|
def self.parse(klass, response)
|
388
|
-
…
|
391
|
+
# …
|
389
392
|
# returns some ResultSet object
|
390
393
|
end
|
391
394
|
end
|
@@ -399,14 +402,14 @@ end
|
|
399
402
|
|
400
403
|
You can customize how the scope builder methods map to request parameters.
|
401
404
|
|
402
|
-
```
|
405
|
+
```ruby
|
403
406
|
class MyQueryBuilder
|
404
407
|
def def initialize(klass); end
|
405
408
|
|
406
409
|
def where(conditions = {})
|
407
410
|
end
|
408
411
|
|
409
|
-
… add order, includes, paginate, page, first, build
|
412
|
+
# … add order, includes, paginate, page, first, build
|
410
413
|
end
|
411
414
|
|
412
415
|
class MyApi::Base < JsonApiClient::Resource
|
@@ -418,7 +421,7 @@ end
|
|
418
421
|
|
419
422
|
You can customize how your resources find pagination information from the response.
|
420
423
|
|
421
|
-
```
|
424
|
+
```ruby
|
422
425
|
class MyPaginator
|
423
426
|
def initialize(result_set, data); end
|
424
427
|
# implement current_page, total_entries, etc
|
@@ -78,7 +78,7 @@ module JsonApiClient
|
|
78
78
|
#
|
79
79
|
# @return [Connection] The connection to the json api server
|
80
80
|
def connection(rebuild = false, &block)
|
81
|
-
_build_connection(&block)
|
81
|
+
_build_connection(rebuild, &block)
|
82
82
|
connection_object
|
83
83
|
end
|
84
84
|
|
@@ -334,6 +334,11 @@ module JsonApiClient
|
|
334
334
|
relationships.set_all_attributes_dirty if relationships
|
335
335
|
end
|
336
336
|
|
337
|
+
def valid?(context = nil)
|
338
|
+
context ||= (new_record? ? :create : :update)
|
339
|
+
super(context)
|
340
|
+
end
|
341
|
+
|
337
342
|
# Commit the current changes to the resource to the remote server.
|
338
343
|
# If the resource was previously loaded from the server, we will
|
339
344
|
# try to update the record. Otherwise if it's a new record, then
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_api_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Ching
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -175,8 +175,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
175
|
version: '0'
|
176
176
|
requirements: []
|
177
177
|
rubyforge_project:
|
178
|
-
rubygems_version: 2.4.
|
178
|
+
rubygems_version: 2.4.8
|
179
179
|
signing_key:
|
180
180
|
specification_version: 4
|
181
181
|
summary: Build client libraries compliant with specification defined by jsonapi.org
|
182
182
|
test_files: []
|
183
|
+
has_rdoc:
|