jsonapi-utils 0.2.0 → 0.2.1

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: fbc9435ea77e7a938e5c3cd34b701e48509294aa
4
- data.tar.gz: af2c51ee9c5bb6f5baffa1aa8bec5706befd546f
3
+ metadata.gz: 7dd1257dc06616941f1a935884e1b3e18ced4898
4
+ data.tar.gz: e464f7c33b4c462e6a9ddc94d669f0b1558a6b6e
5
5
  SHA512:
6
- metadata.gz: f60cd55bdc532063292bdddb6fca175f25d9b82d8fc3d1359481f053499b13eae60174884fd77847131ec3996f5f7ec4e9ca50f5f20c04d51cd0dc036de9aaed
7
- data.tar.gz: c37add6ab91aa7a08c2c49131eea9ce478f403250a43cafaed81d3d39273de1db5bf330aadafe5028a502c07e78eeef84cf417583bc7fb7d83b6db2d050b99b3
6
+ metadata.gz: 231eff83798cb798e4c3e3e28821acea750b82b9b0c51a479678bc9523d7ff815bb8e1151272ace7a7da9abb445751e49450fc0b7f0386efa00f97f98090b56c
7
+ data.tar.gz: 7a5a0d73d18304b431e61788769cf603411c01da961de76865d1499b1f6e4974934ba5eb7fe0e0e397b2dc17e0175b4f55cb72a3cefe31f36f13a0e1b621a021
data/README.md CHANGED
@@ -1,36 +1,576 @@
1
1
  # JSONAPI::Utils
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/jsonapi-utils`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- Required doc:
6
-
7
- * Describe something about the awesome `jsonapi-resources` gem
8
- * Describe how it's easy to serialize JSON API-based responses using the `jsonapi-utils` gem
9
- * JSONAPI::Utils#jsonapi_render (show options like `json`, `resource`, `model`, `scope` and `count`)
10
- * JSONAPI::Utils#jsonapi_serialize
11
- * Example of a model
12
- * Example of a resource
13
- * Example of a base controller and another whatever controller
3
+ JSON::Utils is a simple way to get a full-featured [JSON API](jsonapi.org) serialization in your
4
+ controller's responses. This gem works on top of the awesome gem [jsonapi-resources](https://github.com/cerebris/jsonapi-resources),
5
+ bringing to controllers a Rails-native way to render data.
14
6
 
15
7
  ## Installation
16
8
 
17
- Add this line to your application's Gemfile:
9
+ Add these lines to your application's Gemfile:
18
10
 
19
11
  ```ruby
12
+ gem 'jsonapi-resources', '~> 0.5.7'
20
13
  gem 'jsonapi-utils'
21
14
  ```
22
15
 
23
16
  And then execute:
24
17
 
25
- $ bundle
18
+ ```shell
19
+ $ bundle
20
+ ```
21
+
22
+ ## Macros
23
+
24
+ * `jsonapi_render`: it works like ActionController's `render` method, receiving model objects and
25
+ rendering them into JSON API's data format.
26
+
27
+ * `jsonapi_serialize`: in the backstage, it's the method that actually parsers model objects or hashes and builds JSON data.
28
+ It can be called anywhere in controllers, concerns etc.
29
+
30
+ ## Options
31
+
32
+ Those macros accept the following options:
33
+
34
+ * `resource`: explicitly points the resource to be used in the serialization. By default, JSONAPI::Utils will
35
+ select resources by inferencing from controller's name.
26
36
 
27
- Or install it yourself as:
37
+ ```ruby
38
+ # If in UsersController for some reason it needs to render a different resource:
39
+ jsonapi_render json: Post.all, options: { resource: PostResource }
40
+ ```
41
+
42
+ * `count`: explicitly points the total count of records for the request, in order to build a proper pagination. By default, JSONAPI::Utils will
43
+ count the total number of records for a given resource.
28
44
 
29
- $ gem install jsonapi-utils
45
+ ```ruby
46
+ users = User.all
47
+ jsonapi_render json: users, options: { count: users.size }
48
+ ```
30
49
 
31
50
  ## Usage
32
51
 
33
- TODO: Write usage instructions here
52
+ Let's say we have a Rails app for a super simple blog.
53
+
54
+ ### Models
55
+
56
+ ```ruby
57
+ # app/models/user.rb
58
+ class User < ActiveRecord::Base
59
+ has_many :posts
60
+ validates :first_name, :last_name, presence: true
61
+ end
62
+
63
+ # app/models/user.rb
64
+ class Post < ActiveRecord::Base
65
+ belongs_to :author, class_name: 'User', foreign_key: 'user_id'
66
+ validates :title, :body, presence: true
67
+ end
68
+ ```
69
+
70
+ ### Resources
71
+
72
+ Here is where we define how the serialization will behave:
73
+
74
+ ```ruby
75
+ # app/resources/user_resource.rb
76
+ class UserResource < JSONAPI::Resource
77
+ attributes :first_name, :last_name, :full_name, :birthday
78
+ attribute :full_name
79
+
80
+ has_many :posts
81
+
82
+ def full_name
83
+ "#{@model.first_name} #{@model.last_name}"
84
+ end
85
+ end
86
+
87
+ # app/resources/post_resource.rb
88
+ class PostResource < JSONAPI::Resource
89
+ attributes :title, :body
90
+ has_one :author
91
+ end
92
+ ```
93
+
94
+ ### Routes & Controllers
95
+
96
+ Let's define our routes using the `jsonapi_resources` and `jsonapi_links` macros provied by the `jsonapi-resources` gem:
97
+
98
+ ```ruby
99
+ Rails.application.routes.draw do
100
+ jsonapi_resources :users do
101
+ jsonapi_resources :posts
102
+ jsonapi_links :posts
103
+ end
104
+ end
105
+ ```
106
+
107
+ And a base controller to include the features from `jsonapi-resources` and `jsonapi-utils`:
108
+
109
+ ```ruby
110
+ # app/controllers/base_controller.rb
111
+ class BaseController < JSONAPI::ResourceController
112
+ include JSONAPI::Utils
113
+ protect_from_forgery with: :null_session
114
+ rescue_from ActiveRecord::RecordNotFound, with: :jsonapi_render_not_found
115
+ end
116
+ ```
117
+
118
+ For this example, let's get focused only on read actions. After including `JSONAPI::Utils` we can use the `jsonapi_render` method
119
+ in order to generate responses which follow the JSON API's standards.
120
+
121
+ ```ruby
122
+ # app/controllers/users_controller.rb
123
+ class UsersController < BaseController
124
+ before_action :load_user, only: [:show]
125
+
126
+ # GET /users
127
+ def index
128
+ users = User.all
129
+ jsonapi_render json: users, options: { count: users.size }
130
+ end
131
+
132
+ # GET /users/:id
133
+ def show
134
+ jsonapi_render json: @user
135
+ end
136
+
137
+ private
138
+
139
+ def load_user
140
+ @user = User.find(params[:id])
141
+ end
142
+ end
143
+ ```
144
+
145
+ And:
146
+
147
+ ```ruby
148
+ # app/controllers/posts_controller.rb
149
+ class PostsController < BaseController
150
+ before_action :load_user, only: [:index, :show]
151
+ before_action :load_post, only: [:show]
152
+
153
+ # GET /users/:user_id/posts
154
+ def index
155
+ jsonapi_render json: @user.posts, options: { count: @user.posts.size }
156
+ end
157
+
158
+ # GET /users/:user_id/posts/:id
159
+ def show
160
+ jsonapi_render json: @post
161
+ end
162
+
163
+ private
164
+
165
+ def load_user
166
+ @user = User.find(params[:user_id])
167
+ end
168
+
169
+ def load_post
170
+ @post = @user.posts.find(params[:id])
171
+ end
172
+ end
173
+ ```
174
+
175
+ ### Erros
176
+
177
+ #### Not found
178
+
179
+ As you might have seen in BaseController this line will handle all errors related to not found resources:
180
+
181
+ ```ruby
182
+ rescue_from ActiveRecord::RecordNotFound, with: :jsonapi_render_not_found
183
+ ```
184
+
185
+ The `jsonapi_render_not_found` method will produce the following error payload:
186
+
187
+ ```json
188
+ HTTP/1.1 404 Not found
189
+ Content-Type: application/vnd.api+json
190
+
191
+ {
192
+ "errors": [
193
+ {
194
+ "title": "Record not found",
195
+ "detail": "The record identified by 3 could not be found.",
196
+ "id": null,
197
+ "href": null,
198
+ "code": 404,
199
+ "source": null,
200
+ "links": null,
201
+ "status": "not_found"
202
+ }
203
+ ]
204
+ }
205
+ ```
206
+
207
+ In case you prefer rendering not found resources with null data and `200 OK` status code, you can use `jsonapi_render_not_found_with_null` to produce:
208
+
209
+ ```json
210
+ HTTP/1.1 200 OK
211
+ Content-Type: application/vnd.api+json
212
+
213
+ {
214
+ "data": null
215
+ }
216
+ ```
217
+
218
+ If you need to create custom error message, check [this](https://github.com/cerebris/jsonapi-resources#error-codes).
219
+
220
+ ### Initializer
221
+
222
+ In order to enable a proper pagination, record count etc, let's define an initializer such as:
223
+
224
+ ```ruby
225
+ # config/initializers/jsonapi_resources.rb
226
+ JSONAPI.configure do |config|
227
+ config.json_key_format = :underscored_key
228
+ config.route_format = :dasherized_route
229
+
230
+ config.operations_processor = :active_record
231
+
232
+ config.allow_include = true
233
+ config.allow_sort = true
234
+ config.allow_filter = true
235
+
236
+ config.raise_if_parameters_not_allowed = true
237
+
238
+ config.default_paginator = :paged
239
+
240
+ config.top_level_links_include_pagination = true
241
+
242
+ config.default_page_size = 10
243
+ config.maximum_page_size = 20
244
+
245
+ config.top_level_meta_include_record_count = true
246
+ config.top_level_meta_record_count_key = :record_count
247
+
248
+ config.use_text_errors = false
249
+
250
+ config.exception_class_whitelist = []
251
+
252
+ config.always_include_to_one_linkage_data = false
253
+ end
254
+ ```
255
+
256
+ You may want a different configuration for your API. For more information check [this](https://github.com/cerebris/jsonapi-resources/#configuration).
257
+
258
+ ### Requests & Responses
259
+
260
+ Here's some examples of requests – based on those sample [controllers](#routes--controllers) – and their respective JSON responses.
261
+
262
+ * [Collection](#collection)
263
+ * [Collection (options)](#collection-options)
264
+ * [Single record](#single-record)
265
+ * [Record (options)](#single-record-options)
266
+ * [Relationships (identifier objects)](#relationships-identifier-objects)
267
+ * [Nested resources](#nested-resources)
268
+
269
+ #### Collection
270
+
271
+ Request:
272
+
273
+ ```
274
+ GET /users HTTP/1.1
275
+ Accept: application/vnd.api+json
276
+ ```
277
+
278
+ Response:
279
+
280
+ ```json
281
+ HTTP/1.1 200 OK
282
+ Content-Type: application/vnd.api+json
283
+
284
+ {
285
+ "data": [
286
+ {
287
+ "id": "1",
288
+ "type": "users",
289
+ "links": {
290
+ "self": "http://api.myblog.com/users/1"
291
+ },
292
+ "attributes": {
293
+ "first_name": "Tiago",
294
+ "last_name": "Guedes",
295
+ "full_name": "Tiago Guedes",
296
+ "birthday": null
297
+ },
298
+ "relationships": {
299
+ "posts": {
300
+ "links": {
301
+ "self": "http://api.myblog.com/users/1/relationships/posts",
302
+ "related": "http://api.myblog.com/users/1/posts"
303
+ }
304
+ }
305
+ }
306
+ },
307
+ {
308
+ "id": "2",
309
+ "type": "users",
310
+ "links": {
311
+ "self": "http://api.myblog.com/users/2"
312
+ },
313
+ "attributes": {
314
+ "first_name": "Douglas",
315
+ "last_name": "André",
316
+ "full_name": "Douglas André",
317
+ "birthday": null
318
+ },
319
+ "relationships": {
320
+ "posts": {
321
+ "links": {
322
+ "self": "http://api.myblog.com/users/2/relationships/posts",
323
+ "related": "http://api.myblog.com/users/2/posts"
324
+ }
325
+ }
326
+ }
327
+ }
328
+ ],
329
+ "meta": {
330
+ "record_count": 2
331
+ },
332
+ "links": {
333
+ "first": "http://api.myblog.com/users?page%5Bnumber%5D=1&page%5Bsize%5D=10",
334
+ "last": "http://api.myblog.com/users?page%5Bnumber%5D=1&page%5Bsize%5D=10"
335
+ }
336
+ }
337
+ ```
338
+
339
+ #### Collection (options)
340
+
341
+ Request:
342
+
343
+ ```
344
+ GET /users?include=posts&fields[users]=full_name,posts&fields[posts]=title&page[number]=1&page[size]=1 HTTP/1.1
345
+ Accept: application/vnd.api+json
346
+ ```
347
+
348
+ Response:
349
+
350
+ ```json
351
+ HTTP/1.1 200 OK
352
+ Content-Type: application/vnd.api+json
353
+
354
+ {
355
+ "data": [
356
+ {
357
+ "id": "1",
358
+ "type": "users",
359
+ "links": {
360
+ "self": "http://api.myblog.com/users/1"
361
+ },
362
+ "attributes": {
363
+ "full_name": "Tiago Guedes"
364
+ },
365
+ "relationships": {
366
+ "posts": {
367
+ "links": {
368
+ "self": "http://api.myblog.com/users/1/relationships/posts",
369
+ "related": "http://api.myblog.com/users/1/posts"
370
+ },
371
+ "data": [
372
+ {
373
+ "type": "posts",
374
+ "id": "1"
375
+ }
376
+ ]
377
+ }
378
+ }
379
+ }
380
+ ],
381
+ "included": [
382
+ {
383
+ "id": "1",
384
+ "type": "posts",
385
+ "links": {
386
+ "self": "http://api.myblog.com/posts/1"
387
+ },
388
+ "attributes": {
389
+ "title": "An awesome post"
390
+ }
391
+ }
392
+ ],
393
+ "meta": {
394
+ "record_count": 2
395
+ },
396
+ "links": {
397
+ "first": "http://api.myblog.com/users?fields%5Bposts%5D=title&fields%5Busers%5D=full_name%2Cposts&include=posts&page%5Bnumber%5D=1&page%5Bsize%5D=1",
398
+ "next": "http://api.myblog.com/users?fields%5Bposts%5D=title&fields%5Busers%5D=full_name%2Cposts&include=posts&page%5Bnumber%5D=2&page%5Bsize%5D=1",
399
+ "last": "http://api.myblog.com/users?fields%5Bposts%5D=title&fields%5Busers%5D=full_name%2Cposts&include=posts&page%5Bnumber%5D=2&page%5Bsize%5D=1"
400
+ }
401
+ }
402
+ ```
403
+
404
+ #### Single record
405
+
406
+ Request:
407
+
408
+ ```
409
+ GET /users/1 HTTP/1.1
410
+ Accept: application/vnd.api+json
411
+ ```
412
+
413
+ Response:
414
+
415
+ ```json
416
+ HTTP/1.1 200 OK
417
+ Content-Type: application/vnd.api+json
418
+
419
+ {
420
+ "data": {
421
+ "id": "1",
422
+ "type": "users",
423
+ "links": {
424
+ "self": "http://api.myblog.com/users/1"
425
+ },
426
+ "attributes": {
427
+ "first_name": "Tiago",
428
+ "last_name": "Guedes",
429
+ "full_name": "Tiago Guedes",
430
+ "birthday": null
431
+ },
432
+ "relationships": {
433
+ "posts": {
434
+ "links": {
435
+ "self": "http://api.myblog.com/users/1/relationships/posts",
436
+ "related": "http://api.myblog.com/users/1/posts"
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ ```
443
+
444
+ #### Single record (options)
445
+
446
+ Request:
447
+
448
+ ```
449
+ GET /users/1?include=posts&fields[users]=full_name,posts&fields[posts]=title HTTP/1.1
450
+ Accept: application/vnd.api+json
451
+ ```
452
+
453
+ Response:
454
+
455
+ ```json
456
+ HTTP/1.1 200 OK
457
+ Content-Type: application/vnd.api+json
458
+
459
+ {
460
+ "data": {
461
+ "id": "1",
462
+ "type": "users",
463
+ "links": {
464
+ "self": "http://api.myblog.com/users/1"
465
+ },
466
+ "attributes": {
467
+ "full_name": "Tiago Guedes"
468
+ },
469
+ "relationships": {
470
+ "posts": {
471
+ "links": {
472
+ "self": "http://api.myblog.com/users/1/relationships/posts",
473
+ "related": "http://api.myblog.com/users/1/posts"
474
+ },
475
+ "data": [
476
+ {
477
+ "type": "posts",
478
+ "id": "1"
479
+ }
480
+ ]
481
+ }
482
+ }
483
+ },
484
+ "included": [
485
+ {
486
+ "id": "1",
487
+ "type": "posts",
488
+ "links": {
489
+ "self": "http://api.myblog.com/posts/1"
490
+ },
491
+ "attributes": {
492
+ "title": "An awesome post"
493
+ }
494
+ }
495
+ ]
496
+ }
497
+ ```
498
+
499
+ #### Relationships (identifier objects)
500
+
501
+ Request:
502
+
503
+ ```
504
+ GET /users/1/relationships/posts HTTP/1.1
505
+ Accept: application/vnd.api+json
506
+ ```
507
+
508
+ Response:
509
+
510
+ ```json
511
+ HTTP/1.1 200 OK
512
+ Content-Type: application/vnd.api+json
513
+
514
+ {
515
+ "links": {
516
+ "self": "http://api.myblog.com/users/1/relationships/posts",
517
+ "related": "http://api.myblog.com/users/1/posts"
518
+ },
519
+ "data": [
520
+ {
521
+ "type": "posts",
522
+ "id": "1"
523
+ }
524
+ ]
525
+ }
526
+ ```
527
+
528
+ #### Nested resources
529
+
530
+ Request:
531
+
532
+ ```
533
+ GET /users/1/posts HTTP/1.1
534
+ Accept: application/vnd.api+json
535
+ ```
536
+
537
+ Response:
538
+
539
+ ```json
540
+ HTTP/1.1 200 OK
541
+ Content-Type: application/vnd.api+json
542
+
543
+ {
544
+ "data": [
545
+ {
546
+ "id": "1",
547
+ "type": "posts",
548
+ "links": {
549
+ "self": "http://api.myblog.com/posts/1"
550
+ },
551
+ "attributes": {
552
+ "title": "An awesome post",
553
+ "body": "Lorem ipsum dolot sit amet"
554
+ },
555
+ "relationships": {
556
+ "author": {
557
+ "links": {
558
+ "self": "http://api.myblog.com/posts/1/relationships/author",
559
+ "related": "http://api.myblog.com/posts/1/author"
560
+ }
561
+ }
562
+ }
563
+ }
564
+ ],
565
+ "meta": {
566
+ "record_count": 1
567
+ },
568
+ "links": {
569
+ "first": "http://api.myblog.com/posts?page%5Bnumber%5D=1&page%5Bsize%5D=10",
570
+ "last": "http://api.myblog.com/posts?page%5Bnumber%5D=1&page%5Bsize%5D=10"
571
+ }
572
+ }
573
+ ```
34
574
 
35
575
  ## Development
36
576
 
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Tiago Guedes", "Douglas André"]
10
10
  spec.email = ["tiagopog@gmail.com", "douglas@beautydate.com.br"]
11
11
 
12
- spec.summary = %q{Full-featured JSON API serialization in a simple way}
13
- spec.description = %q{A Rails-way to get your API's data serialized following the JSON API's specs (http://jsosapi.org)}
12
+ spec.summary = %q{JSON::Utils is a simple way to get a full-featured JSON API serialization for your controller's responses.}
13
+ spec.description = %q{A Rails way to get your API's data following the JSON API's specs (http://jsosapi.org)}
14
14
  spec.homepage = "https://github.com/b2beauty/jsonapi-utils"
15
15
  spec.license = "MIT"
16
16
 
data/lib/jsonapi/utils.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "jsonapi/utils/version"
1
+ require 'jsonapi/utils/version'
2
2
 
3
3
  module JSONAPI
4
4
  module Utils
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Utils
3
- VERSION = "0.2.0"
3
+ VERSION = '0.2.1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Guedes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2015-09-14 00:00:00.000000000 Z
12
+ date: 2015-09-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,8 +53,7 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
- description: A Rails-way to get your API's data serialized following the JSON API's
57
- specs (http://jsosapi.org)
56
+ description: A Rails way to get your API's data following the JSON API's specs (http://jsosapi.org)
58
57
  email:
59
58
  - tiagopog@gmail.com
60
59
  - douglas@beautydate.com.br
@@ -95,8 +94,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
94
  version: '0'
96
95
  requirements: []
97
96
  rubyforge_project:
98
- rubygems_version: 2.4.8
97
+ rubygems_version: 2.4.5.1
99
98
  signing_key:
100
99
  specification_version: 4
101
- summary: Full-featured JSON API serialization in a simple way
100
+ summary: JSON::Utils is a simple way to get a full-featured JSON API serialization
101
+ for your controller's responses.
102
102
  test_files: []