jsonapi-utils 0.2.0 → 0.2.1

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 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: []