|
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' |
4 | 3 |
|
5 | 4 | module JSONAPI |
6 | 5 | 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 | | - |
25 | 6 | # ActionController methods and hooks for JSON API deserialization and |
26 | 7 | # rendering. |
27 | 8 | 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 |
161 | 11 | end |
162 | 12 | end |
163 | 13 | end |
0 commit comments