Skip to content

Commit 14a9421

Browse files
authored
Refactor. (#56)
1 parent 27aeea8 commit 14a9421

8 files changed

+198
-158
lines changed

lib/jsonapi/rails.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
require 'jsonapi/deserializable'
22
require 'jsonapi/serializable'
3-
require 'jsonapi/rails/configuration'
43
require 'jsonapi/rails/railtie'
54

65
module JSONAPI
76
module Rails
7+
require 'jsonapi/rails/configuration'
8+
require 'jsonapi/rails/logging'
9+
810
extend Configurable
11+
extend Logging
912
end
1013
end

lib/jsonapi/rails/controller.rb

+4-154
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,13 @@
1-
require 'jsonapi/deserializable'
2-
require 'jsonapi/parser'
3-
require 'jsonapi/rails/configuration'
1+
require 'jsonapi/rails/controller/deserialization'
2+
require 'jsonapi/rails/controller/hooks'
43

54
module JSONAPI
65
module Rails
7-
module Deserializable
8-
# @private
9-
class Resource < JSONAPI::Deserializable::Resource
10-
id
11-
type
12-
attributes
13-
has_one do |_rel, id, type, key|
14-
type = type.to_s.singularize.camelize
15-
{ "#{key}_id".to_sym => id, "#{key}_type".to_sym => type }
16-
end
17-
has_many do |_rel, ids, types, key|
18-
key = key.to_s.singularize
19-
types = types.map { |t| t.to_s.singularize.camelize }
20-
{ "#{key}_ids".to_sym => ids, "#{key}_types".to_sym => types }
21-
end
22-
end
23-
end
24-
256
# ActionController methods and hooks for JSON API deserialization and
267
# rendering.
278
module Controller
28-
extend ActiveSupport::Concern
29-
30-
JSONAPI_POINTERS_KEY = 'jsonapi-rails.jsonapi_pointers'.freeze
31-
32-
class_methods do
33-
# Declare a deserializable resource.
34-
#
35-
# @param key [Symbol] The key under which the deserialized hash will be
36-
# available within the `params` hash.
37-
# @param options [Hash]
38-
# @option class [Class] A custom deserializer class. Optional.
39-
# @option only List of actions for which deserialization should happen.
40-
# Optional.
41-
# @option except List of actions for which deserialization should not
42-
# happen. Optional.
43-
# @yieldreturn Optional block for in-line definition of custom
44-
# deserializers.
45-
#
46-
# @example
47-
# class ArticlesController < ActionController::Base
48-
# deserializable_resource :article, only: [:create, :update]
49-
#
50-
# def create
51-
# article = Article.new(params[:article])
52-
#
53-
# if article.save
54-
# render jsonapi: article
55-
# else
56-
# render jsonapi_errors: article.errors
57-
# end
58-
# end
59-
#
60-
# # ...
61-
# end
62-
#
63-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
64-
def deserializable_resource(key, options = {}, &block)
65-
options = options.dup
66-
klass = options.delete(:class) ||
67-
Class.new(JSONAPI::Rails::Deserializable::Resource, &block)
68-
69-
before_action(options) do |controller|
70-
hash = controller.params.to_unsafe_hash[:_jsonapi]
71-
if hash.nil?
72-
JSONAPI::Rails.config[:logger].warn do
73-
"Unable to deserialize #{key} because no JSON API payload was" \
74-
" found. (#{controller.controller_name}##{params[:action]})"
75-
end
76-
next
77-
end
78-
79-
ActiveSupport::Notifications
80-
.instrument('parse.jsonapi-rails',
81-
key: key, payload: hash, class: klass) do
82-
JSONAPI::Parser::Resource.parse!(hash)
83-
resource = klass.new(hash[:data])
84-
controller.request.env[JSONAPI_POINTERS_KEY] =
85-
resource.reverse_mapping
86-
controller.params[key.to_sym] = resource.to_hash
87-
end
88-
end
89-
end
90-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
91-
end
92-
93-
# Hook for serializable class mapping (for resources).
94-
# Overridden by the `class` renderer option.
95-
# @return [Hash{Symbol=>Class}]
96-
def jsonapi_class
97-
JSONAPI::Rails.config[:jsonapi_class].dup
98-
end
99-
100-
# Hook for serializable class mapping (for errors).
101-
# Overridden by the `class` renderer option.
102-
# @return [Hash{Symbol=>Class}]
103-
def jsonapi_errors_class
104-
JSONAPI::Rails.config[:jsonapi_errors_class].dup
105-
end
106-
107-
# Hook for the jsonapi object.
108-
# Overridden by the `jsonapi_object` renderer option.
109-
# @return [Hash,nil]
110-
def jsonapi_object
111-
JSONAPI::Rails.config[:jsonapi_object]
112-
end
113-
114-
# Hook for default exposures.
115-
# @return [Hash]
116-
def jsonapi_expose
117-
instance_exec(&JSONAPI::Rails.config[:jsonapi_expose])
118-
end
119-
120-
# Hook for default cache.
121-
# @return [#fetch_multi]
122-
def jsonapi_cache
123-
instance_exec(&JSONAPI::Rails.config[:jsonapi_cache])
124-
end
125-
126-
# Hook for default fields.
127-
# @return [Hash{Symbol=>Array<Symbol>},nil]
128-
def jsonapi_fields
129-
instance_exec(&JSONAPI::Rails.config[:jsonapi_fields])
130-
end
131-
132-
# Hook for default includes.
133-
# @return [IncludeDirective]
134-
def jsonapi_include
135-
instance_exec(&JSONAPI::Rails.config[:jsonapi_include])
136-
end
137-
138-
# Hook for default links.
139-
# @return [Hash]
140-
def jsonapi_links
141-
instance_exec(&JSONAPI::Rails.config[:jsonapi_links])
142-
end
143-
144-
# Hook for default meta.
145-
# @return [Hash,nil]
146-
def jsonapi_meta
147-
instance_exec(&JSONAPI::Rails.config[:jsonapi_meta])
148-
end
149-
150-
# Hook for pagination scheme.
151-
# @return [Hash]
152-
def jsonapi_pagination(resources)
153-
instance_exec(resources, &JSONAPI::Rails.config[:jsonapi_pagination])
154-
end
155-
156-
# JSON pointers for deserialized fields.
157-
# @return [Hash{Symbol=>String}]
158-
def jsonapi_pointers
159-
request.env[JSONAPI_POINTERS_KEY] || {}
160-
end
9+
include Deserialization
10+
include Hooks
16111
end
16212
end
16313
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
require 'jsonapi/parser'
2+
require 'jsonapi/rails/deserializable_resource'
3+
4+
module JSONAPI
5+
module Rails
6+
module Controller
7+
# Controller class and instance methods for deserialization of incoming
8+
# JSON API payloads.
9+
module Deserialization
10+
extend ActiveSupport::Concern
11+
12+
JSONAPI_POINTERS_KEY = 'jsonapi-rails.jsonapi_pointers'.freeze
13+
14+
class_methods do
15+
# Declare a deserializable resource.
16+
#
17+
# @param key [Symbol] The key under which the deserialized hash will be
18+
# available within the `params` hash.
19+
# @param options [Hash]
20+
# @option class [Class] A custom deserializer class. Optional.
21+
# @option only List of actions for which deserialization should happen.
22+
# Optional.
23+
# @option except List of actions for which deserialization should not
24+
# happen. Optional.
25+
# @yieldreturn Optional block for in-line definition of custom
26+
# deserializers.
27+
#
28+
# @example
29+
# class ArticlesController < ActionController::Base
30+
# deserializable_resource :article, only: [:create, :update]
31+
#
32+
# def create
33+
# article = Article.new(params[:article])
34+
#
35+
# if article.save
36+
# render jsonapi: article
37+
# else
38+
# render jsonapi_errors: article.errors
39+
# end
40+
# end
41+
#
42+
# # ...
43+
# end
44+
#
45+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
46+
def deserializable_resource(key, options = {}, &block)
47+
options = options.dup
48+
klass = options.delete(:class) ||
49+
Class.new(JSONAPI::Rails::DeserializableResource, &block)
50+
51+
before_action(options) do |controller|
52+
hash = controller.params.to_unsafe_hash[:_jsonapi]
53+
if hash.nil?
54+
JSONAPI::Rails.logger.warn do
55+
"Unable to deserialize #{key} because no JSON API payload was" \
56+
" found. (#{controller.controller_name}##{params[:action]})"
57+
end
58+
next
59+
end
60+
61+
ActiveSupport::Notifications
62+
.instrument('parse.jsonapi-rails',
63+
key: key, payload: hash, class: klass) do
64+
JSONAPI::Parser::Resource.parse!(hash)
65+
resource = klass.new(hash[:data])
66+
controller.request.env[JSONAPI_POINTERS_KEY] =
67+
resource.reverse_mapping
68+
controller.params[key.to_sym] = resource.to_hash
69+
end
70+
end
71+
end
72+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
73+
end
74+
75+
# JSON pointers for deserialized fields.
76+
# @return [Hash{Symbol=>String}]
77+
def jsonapi_pointers
78+
request.env[JSONAPI_POINTERS_KEY] || {}
79+
end
80+
end
81+
end
82+
end
83+
end

lib/jsonapi/rails/controller/hooks.rb

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
require 'jsonapi/rails/configuration'
2+
3+
module JSONAPI
4+
module Rails
5+
module Controller
6+
extend ActiveSupport::Concern
7+
8+
# Hooks for customizing rendering default options at controller-level.
9+
module Hooks
10+
# Hook for serializable class mapping (for resources).
11+
# Overridden by the `class` renderer option.
12+
# @return [Hash{Symbol=>Class}]
13+
def jsonapi_class
14+
JSONAPI::Rails.config[:jsonapi_class].dup
15+
end
16+
17+
# Hook for serializable class mapping (for errors).
18+
# Overridden by the `class` renderer option.
19+
# @return [Hash{Symbol=>Class}]
20+
def jsonapi_errors_class
21+
JSONAPI::Rails.config[:jsonapi_errors_class].dup
22+
end
23+
24+
# Hook for the jsonapi object.
25+
# Overridden by the `jsonapi_object` renderer option.
26+
# @return [Hash,nil]
27+
def jsonapi_object
28+
JSONAPI::Rails.config[:jsonapi_object]
29+
end
30+
31+
# Hook for default exposures.
32+
# @return [Hash]
33+
def jsonapi_expose
34+
instance_exec(&JSONAPI::Rails.config[:jsonapi_expose])
35+
end
36+
37+
# Hook for default cache.
38+
# @return [#fetch_multi]
39+
def jsonapi_cache
40+
instance_exec(&JSONAPI::Rails.config[:jsonapi_cache])
41+
end
42+
43+
# Hook for default fields.
44+
# @return [Hash{Symbol=>Array<Symbol>},nil]
45+
def jsonapi_fields
46+
instance_exec(&JSONAPI::Rails.config[:jsonapi_fields])
47+
end
48+
49+
# Hook for default includes.
50+
# @return [IncludeDirective]
51+
def jsonapi_include
52+
instance_exec(&JSONAPI::Rails.config[:jsonapi_include])
53+
end
54+
55+
# Hook for default links.
56+
# @return [Hash]
57+
def jsonapi_links
58+
instance_exec(&JSONAPI::Rails.config[:jsonapi_links])
59+
end
60+
61+
# Hook for default meta.
62+
# @return [Hash,nil]
63+
def jsonapi_meta
64+
instance_exec(&JSONAPI::Rails.config[:jsonapi_meta])
65+
end
66+
67+
# Hook for pagination scheme.
68+
# @return [Hash]
69+
def jsonapi_pagination(resources)
70+
instance_exec(resources, &JSONAPI::Rails.config[:jsonapi_pagination])
71+
end
72+
end
73+
end
74+
end
75+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
require 'jsonapi/deserializable/resource'
2+
3+
module JSONAPI
4+
module Rails
5+
# Customized deserializable resource class to match ActiveRecord's API.
6+
class DeserializableResource < JSONAPI::Deserializable::Resource
7+
id
8+
type
9+
attributes
10+
has_one do |_rel, id, type, key|
11+
type = type.to_s.singularize.camelize
12+
{ "#{key}_id".to_sym => id, "#{key}_type".to_sym => type }
13+
end
14+
has_many do |_rel, ids, types, key|
15+
key = key.to_s.singularize
16+
types = types.map { |t| t.to_s.singularize.camelize }
17+
{ "#{key}_ids".to_sym => ids, "#{key}_types".to_sym => types }
18+
end
19+
end
20+
end
21+
end

lib/jsonapi/rails/log_subscriber.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def parse(event)
1616
end
1717

1818
def logger
19-
JSONAPI::Rails.config[:logger]
19+
JSONAPI::Rails.logger
2020
end
2121
end
2222
end

lib/jsonapi/rails/logging.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module JSONAPI
2+
module Rails
3+
# @private
4+
module Logging
5+
def logger
6+
config[:logger]
7+
end
8+
end
9+
end
10+
end

lib/jsonapi/rails/railtie.rb

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
require 'rails/railtie'
2-
require 'action_controller'
3-
require 'active_support'
42

53
require 'jsonapi/rails/log_subscriber'
64
require 'jsonapi/rails/renderer'

0 commit comments

Comments
 (0)