Skip to content

Commit 0da3e8d

Browse files
authored
Add configurations/hooks for fields, include, and links. (#49)
1 parent 5bd5419 commit 0da3e8d

22 files changed

+547
-230
lines changed

CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
### Added
2+
3+
* Default configuration and hooks for the `links` parameter.
4+
* Default configuration and hooks for the `fields` parameter.
5+
* Default configuration and hooks for the `include` parameter.
6+
7+
# v0.3.1
8+
9+
### Fixed
10+
11+
* Broken default inferrer.
12+
* Broken initializer generator.
13+
14+
# v0.3.0
15+
16+
### Added
17+
18+
* Default configuration and hooks for the `class` parameter (for resources and
19+
errors).
20+
* Default configuration and hooks for the `expose` parameter.
21+
* Default configuration and hooks for the `jsonapi_object` parameter.
22+
* Default configuration and hooks for pagination links.
23+
* Support for errors rendering.
24+
* Automatic `ActionController::Errors` serialization.
25+
* Add configuration and initializer generator (`$ rails g jsonapi:initializer`).
26+
* Fragment caching.
27+
* Deserialization reverse-mapping available in controller via
28+
`jsonapi_pointers`.
29+
30+
### Changed
31+
32+
* `class` renderer parameter now always takes a hash/lambda.
33+
* Rename `JSONAPI::Rails::ActionController` to `JSONAPI::Rails::Controller`.
34+
35+
### Removed
36+
37+
* `namespace` and `inferrer` renderer parameter.
38+
39+
# v0.2.1
40+
41+
# v0.2.0
42+
43+
# v0.1.2
44+
45+
# v0.1.1

jsonapi-rails.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
2121
spec.add_development_dependency 'sqlite3'
2222
spec.add_development_dependency 'rake', '~> 11.3'
2323
spec.add_development_dependency 'rspec-rails', '~> 3.5'
24+
spec.add_development_dependency 'with_model', '~> 2.0'
2425
spec.add_development_dependency 'simplecov'
2526
end

lib/generators/jsonapi/initializer/templates/initializer.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@
2727
# { url_helpers: ::Rails.application.routes.url_helpers }
2828
# }
2929
#
30+
# # Set default fields.
31+
# # A lambda/proc that will be eval'd in the controller context.
32+
# config.jsonapi_fields = ->() { nil }
33+
#
34+
# # Uncomment the following to have it default to the `fields` query
35+
# # parameter.
36+
# config.jsonapi_fields = lambda {
37+
# fields_param = params.to_unsafe_hash.fetch(:fields, {})
38+
# Hash[fields_param.map { |k, v| [k.to_sym, v.split(',').map!(&:to_sym)] }]
39+
# }
40+
#
41+
# # Set default include.
42+
# # A lambda/proc that will be eval'd in the controller context.
43+
# config.jsonapi_include = ->() { nil }
44+
#
45+
# # Uncomment the following to have it default to the `include` query
46+
# # parameter.
47+
# config.jsonapi_include = lambda {
48+
# params[:include]
49+
# }
50+
#
3051
# # Set a default pagination scheme.
31-
# config.jsonapi_pagination = ->(_) { nil }
52+
# config.jsonapi_pagination = ->(_) { {} }
3253
end

lib/jsonapi/rails.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,9 @@
22
require 'jsonapi/serializable'
33
require 'jsonapi/rails/configuration'
44
require 'jsonapi/rails/railtie'
5+
6+
module JSONAPI
7+
module Rails
8+
extend Configurable
9+
end
10+
end

lib/jsonapi/rails/configuration.rb

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,55 @@
44
module JSONAPI
55
module Rails
66
class Configuration < ActiveSupport::InheritableOptions; end
7-
DEFAULT_JSONAPI_CLASS = Hash.new do |h, k|
8-
names = k.to_s.split('::')
9-
klass = names.pop
10-
h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize
11-
end.freeze
12-
13-
DEFAULT_JSONAPI_ERRORS_CLASS = DEFAULT_JSONAPI_CLASS.dup.merge!(
14-
'ActiveModel::Errors'.to_sym =>
7+
8+
# @private
9+
module Configurable
10+
DEFAULT_JSONAPI_CLASS = Hash.new do |h, k|
11+
names = k.to_s.split('::')
12+
klass = names.pop
13+
h[k] = [*names, "Serializable#{klass}"].join('::').safe_constantize
14+
end.freeze
15+
16+
DEFAULT_JSONAPI_ERRORS_CLASS = DEFAULT_JSONAPI_CLASS.dup.merge!(
17+
'ActiveModel::Errors'.to_sym =>
1518
JSONAPI::Rails::SerializableActiveModelErrors,
16-
'Hash'.to_sym => JSONAPI::Rails::SerializableErrorHash
17-
).freeze
19+
'Hash'.to_sym => JSONAPI::Rails::SerializableErrorHash
20+
).freeze
1821

19-
DEFAULT_JSONAPI_OBJECT = {
20-
version: '1.0'
21-
}.freeze
22+
DEFAULT_JSONAPI_OBJECT = {
23+
version: '1.0'
24+
}.freeze
2225

23-
DEFAULT_JSONAPI_EXPOSE = lambda {
24-
{ url_helpers: ::Rails.application.routes.url_helpers }
25-
}.freeze
26+
DEFAULT_JSONAPI_EXPOSE = lambda {
27+
{ url_helpers: ::Rails.application.routes.url_helpers }
28+
}
2629

27-
DEFAULT_JSONAPI_PAGINATION = ->(_) { nil }
30+
DEFAULT_JSONAPI_FIELDS = ->() { nil }
2831

29-
DEFAULT_CONFIG = {
30-
jsonapi_class: DEFAULT_JSONAPI_CLASS,
31-
jsonapi_errors_class: DEFAULT_JSONAPI_ERRORS_CLASS,
32-
jsonapi_object: DEFAULT_JSONAPI_OBJECT,
33-
jsonapi_expose: DEFAULT_JSONAPI_EXPOSE,
34-
jsonapi_pagination: DEFAULT_JSONAPI_PAGINATION
35-
}.freeze
32+
DEFAULT_JSONAPI_INCLUDE = ->() { nil }
3633

37-
def self.configure
38-
yield config
39-
end
34+
DEFAULT_JSONAPI_LINKS = ->() { {} }
35+
36+
DEFAULT_JSONAPI_PAGINATION = ->(_) { {} }
37+
38+
DEFAULT_CONFIG = {
39+
jsonapi_class: DEFAULT_JSONAPI_CLASS,
40+
jsonapi_errors_class: DEFAULT_JSONAPI_ERRORS_CLASS,
41+
jsonapi_expose: DEFAULT_JSONAPI_EXPOSE,
42+
jsonapi_fields: DEFAULT_JSONAPI_FIELDS,
43+
jsonapi_include: DEFAULT_JSONAPI_INCLUDE,
44+
jsonapi_links: DEFAULT_JSONAPI_LINKS,
45+
jsonapi_object: DEFAULT_JSONAPI_OBJECT,
46+
jsonapi_pagination: DEFAULT_JSONAPI_PAGINATION
47+
}.freeze
48+
49+
def configure
50+
yield config
51+
end
4052

41-
def self.config
42-
@config ||= JSONAPI::Rails::Configuration.new(DEFAULT_CONFIG)
53+
def config
54+
@config ||= JSONAPI::Rails::Configuration.new(DEFAULT_CONFIG)
55+
end
4356
end
4457
end
4558
end

lib/jsonapi/rails/controller.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,24 @@ def jsonapi_expose
110110
instance_exec(&JSONAPI::Rails.config[:jsonapi_expose])
111111
end
112112

113+
# Hook for default fields.
114+
# @return [Hash{Symbol=>Array<Symbol>}]
115+
def jsonapi_fields
116+
instance_exec(&JSONAPI::Rails.config[:jsonapi_fields])
117+
end
118+
119+
# Hook for default includes.
120+
# @return [IncludeDirective]
121+
def jsonapi_include
122+
instance_exec(&JSONAPI::Rails.config[:jsonapi_include])
123+
end
124+
125+
# Hook for default links.
126+
# @return [IncludeDirective]
127+
def jsonapi_links
128+
instance_exec(&JSONAPI::Rails.config[:jsonapi_links])
129+
end
130+
113131
# Hook for pagination scheme.
114132
# @return [Hash]
115133
def jsonapi_pagination(resources)

lib/jsonapi/rails/renderer.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@ def render(resources, options, controller)
1818

1919
private
2020

21+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
2122
def default_options(options, controller, resources)
2223
options.dup.tap do |opts|
2324
opts[:class] ||= controller.jsonapi_class
24-
if (pagination_links = controller.jsonapi_pagination(resources))
25-
opts[:links] = (opts[:links] || {}).merge(pagination_links)
26-
end
27-
opts[:expose] = controller.jsonapi_expose.merge(opts[:expose] || {})
25+
opts[:links] =
26+
controller.jsonapi_links
27+
.merge!(controller.jsonapi_pagination(resources))
28+
.merge!(opts[:links] || {})
29+
opts[:expose] = controller.jsonapi_expose.merge!(opts[:expose] || {})
30+
opts[:fields] ||= controller.jsonapi_fields
31+
opts[:include] ||= controller.jsonapi_include
2832
opts[:jsonapi] = opts.delete(:jsonapi_object) ||
2933
controller.jsonapi_object
3034
end
3135
end
36+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
3237
end
3338

3439
# @private
@@ -52,6 +57,7 @@ def render(errors, options, controller)
5257
def default_options(options, controller)
5358
options.dup.tap do |opts|
5459
opts[:class] ||= controller.jsonapi_errors_class
60+
opts[:links] = controller.jsonapi_links.merge!(opts[:links] || {})
5561
opts[:expose] =
5662
controller.jsonapi_expose
5763
.merge(opts[:expose] || {})

spec/config_spec.rb

Lines changed: 0 additions & 5 deletions
This file was deleted.

spec/controller/fields_spec.rb

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
require 'rails_helper'
2+
3+
describe ActionController::Base, type: :controller do
4+
with_model :User, scope: :all do
5+
table do |t|
6+
t.string :name
7+
t.string :email
8+
end
9+
end
10+
11+
with_serializable :SerializableUser, scope: :all do
12+
type 'users'
13+
attributes :name, :email
14+
end
15+
16+
before :all do
17+
User.create(name: 'Lucas', email: 'lucas@jsonapi-rb.org')
18+
end
19+
20+
context 'when using default configuration' do
21+
controller do
22+
def index
23+
render jsonapi: User.all
24+
end
25+
end
26+
27+
subject { JSON.parse(response.body) }
28+
29+
it 'serializes all fields' do
30+
get :index
31+
32+
expect(subject['data'][0]['attributes'].keys)
33+
.to match_array(%w[name email])
34+
end
35+
end
36+
37+
context 'when using a custom configuration' do
38+
controller do
39+
def index
40+
render jsonapi: User.all
41+
end
42+
end
43+
44+
subject { JSON.parse(response.body) }
45+
46+
it 'modifies serialized fields' do
47+
with_config(jsonapi_fields: ->() { { users: [:name] } }) do
48+
get :index
49+
end
50+
51+
expect(subject['data'][0]['attributes'].keys)
52+
.to match_array(%w[name])
53+
end
54+
end
55+
56+
context 'when customizing jsonapi_fields hook' do
57+
controller do
58+
def index
59+
render jsonapi: User.all
60+
end
61+
62+
def jsonapi_fields
63+
{ users: [:name] }
64+
end
65+
end
66+
67+
subject { JSON.parse(response.body) }
68+
69+
it 'modifies serialized fields' do
70+
get :index
71+
72+
expect(subject['data'][0]['attributes'].keys)
73+
.to match_array(%w[name])
74+
end
75+
end
76+
77+
context 'when prividing fields renderer option' do
78+
controller do
79+
def index
80+
render jsonapi: User.all,
81+
fields: { users: [:email] }
82+
end
83+
end
84+
85+
subject { JSON.parse(response.body) }
86+
87+
it 'modifies serialized fields' do
88+
get :index
89+
90+
expect(subject['data'][0]['attributes'].keys)
91+
.to match_array(%w[email])
92+
end
93+
end
94+
end

0 commit comments

Comments
 (0)