jsonapi_compliable 0.11.34 → 1.0.alpha.2
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 +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -2
- data/Rakefile +7 -3
- data/jsonapi_compliable.gemspec +7 -3
- data/lib/generators/jsonapi/resource_generator.rb +8 -79
- data/lib/generators/jsonapi/templates/application_resource.rb.erb +2 -1
- data/lib/generators/jsonapi/templates/controller.rb.erb +19 -64
- data/lib/generators/jsonapi/templates/resource.rb.erb +5 -47
- data/lib/generators/jsonapi/templates/resource_reads_spec.rb.erb +62 -0
- data/lib/generators/jsonapi/templates/resource_writes_spec.rb.erb +63 -0
- data/lib/jsonapi_compliable.rb +87 -18
- data/lib/jsonapi_compliable/adapters/abstract.rb +202 -45
- data/lib/jsonapi_compliable/adapters/active_record.rb +6 -130
- data/lib/jsonapi_compliable/adapters/active_record/base.rb +247 -0
- data/lib/jsonapi_compliable/adapters/active_record/belongs_to_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_many_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_one_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/inferrence.rb +12 -0
- data/lib/jsonapi_compliable/adapters/active_record/many_to_many_sideload.rb +30 -0
- data/lib/jsonapi_compliable/adapters/null.rb +177 -6
- data/lib/jsonapi_compliable/base.rb +33 -320
- data/lib/jsonapi_compliable/context.rb +16 -0
- data/lib/jsonapi_compliable/deserializer.rb +14 -39
- data/lib/jsonapi_compliable/errors.rb +227 -24
- data/lib/jsonapi_compliable/extensions/extra_attribute.rb +3 -1
- data/lib/jsonapi_compliable/filter_operators.rb +25 -0
- data/lib/jsonapi_compliable/hash_renderer.rb +57 -0
- data/lib/jsonapi_compliable/query.rb +190 -202
- data/lib/jsonapi_compliable/rails.rb +12 -6
- data/lib/jsonapi_compliable/railtie.rb +64 -0
- data/lib/jsonapi_compliable/renderer.rb +60 -0
- data/lib/jsonapi_compliable/resource.rb +35 -663
- data/lib/jsonapi_compliable/resource/configuration.rb +239 -0
- data/lib/jsonapi_compliable/resource/dsl.rb +138 -0
- data/lib/jsonapi_compliable/resource/interface.rb +32 -0
- data/lib/jsonapi_compliable/resource/polymorphism.rb +68 -0
- data/lib/jsonapi_compliable/resource/sideloading.rb +102 -0
- data/lib/jsonapi_compliable/resource_proxy.rb +127 -0
- data/lib/jsonapi_compliable/responders.rb +19 -0
- data/lib/jsonapi_compliable/runner.rb +25 -0
- data/lib/jsonapi_compliable/scope.rb +37 -79
- data/lib/jsonapi_compliable/scoping/extra_attributes.rb +29 -0
- data/lib/jsonapi_compliable/scoping/filter.rb +39 -58
- data/lib/jsonapi_compliable/scoping/filterable.rb +9 -14
- data/lib/jsonapi_compliable/scoping/paginate.rb +9 -3
- data/lib/jsonapi_compliable/scoping/sort.rb +16 -4
- data/lib/jsonapi_compliable/sideload.rb +221 -347
- data/lib/jsonapi_compliable/sideload/belongs_to.rb +34 -0
- data/lib/jsonapi_compliable/sideload/has_many.rb +16 -0
- data/lib/jsonapi_compliable/sideload/has_one.rb +9 -0
- data/lib/jsonapi_compliable/sideload/many_to_many.rb +24 -0
- data/lib/jsonapi_compliable/sideload/polymorphic_belongs_to.rb +108 -0
- data/lib/jsonapi_compliable/stats/payload.rb +4 -8
- data/lib/jsonapi_compliable/types.rb +172 -0
- data/lib/jsonapi_compliable/util/attribute_check.rb +88 -0
- data/lib/jsonapi_compliable/util/persistence.rb +29 -7
- data/lib/jsonapi_compliable/util/relationship_payload.rb +4 -4
- data/lib/jsonapi_compliable/util/render_options.rb +4 -32
- data/lib/jsonapi_compliable/util/serializer_attributes.rb +98 -0
- data/lib/jsonapi_compliable/util/validation_response.rb +15 -9
- data/lib/jsonapi_compliable/version.rb +1 -1
- metadata +105 -24
- data/lib/generators/jsonapi/field_generator.rb +0 -0
- data/lib/generators/jsonapi/templates/create_request_spec.rb.erb +0 -29
- data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +0 -20
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/payload.rb.erb +0 -39
- data/lib/generators/jsonapi/templates/serializer.rb.erb +0 -25
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -19
- data/lib/generators/jsonapi/templates/update_request_spec.rb.erb +0 -33
- data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +0 -152
- data/lib/jsonapi_compliable/scoping/extra_fields.rb +0 -58
@@ -0,0 +1,30 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
class ManyToManySideload < Sideload::ManyToMany
|
5
|
+
def default_base_scope
|
6
|
+
resource_class.model.all
|
7
|
+
end
|
8
|
+
|
9
|
+
def through_table_name
|
10
|
+
@through_table_name ||= parent_resource_class.model
|
11
|
+
.reflections[through.to_s].klass.table_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def scope(parent_ids)
|
15
|
+
base_scope
|
16
|
+
.includes(through)
|
17
|
+
.where(through_table_name => { true_foreign_key => parent_ids })
|
18
|
+
.distinct
|
19
|
+
end
|
20
|
+
|
21
|
+
def infer_foreign_key
|
22
|
+
parent_model = parent_resource_class.model
|
23
|
+
key = parent_model.reflections[name.to_s].options[:through]
|
24
|
+
value = parent_model.reflections[key.to_s].foreign_key.to_sym
|
25
|
+
{ key => value }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -4,11 +4,186 @@ module JsonapiCompliable
|
|
4
4
|
# Useful when your customization does not support all possible
|
5
5
|
# configuration (e.g. the service you hit does not support sorting)
|
6
6
|
class Null < Abstract
|
7
|
-
|
8
|
-
def filter(scope, attribute, value)
|
7
|
+
def filter_string_eq(scope, attribute, value)
|
9
8
|
scope
|
10
9
|
end
|
11
10
|
|
11
|
+
def filter_string_eql(scope, attribute, value)
|
12
|
+
scope
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter_string_not_eq(scope, attribute, value)
|
16
|
+
scope
|
17
|
+
end
|
18
|
+
|
19
|
+
def filter_string_not_eql(scope, attribute, value)
|
20
|
+
scope
|
21
|
+
end
|
22
|
+
|
23
|
+
def filter_string_prefix_eq(scope, attribute, value)
|
24
|
+
scope
|
25
|
+
end
|
26
|
+
|
27
|
+
def filter_string_not_prefix_eq(scope, attribute, value)
|
28
|
+
scope
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter_string_suffix_eq(scope, attribute, value)
|
32
|
+
scope
|
33
|
+
end
|
34
|
+
|
35
|
+
def filter_string_not_suffix_eq(scope, attribute, value)
|
36
|
+
scope
|
37
|
+
end
|
38
|
+
|
39
|
+
def filter_string_like_eq(scope, attribute, value)
|
40
|
+
scope
|
41
|
+
end
|
42
|
+
|
43
|
+
def filter_string_not_like_eq(scope, attribute, value)
|
44
|
+
scope
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter_integer_eq(scope, attribute, value)
|
48
|
+
scope
|
49
|
+
end
|
50
|
+
|
51
|
+
def filter_integer_not_eq(scope, attribute, value)
|
52
|
+
scope
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_integer_gt(scope, attribute, value)
|
56
|
+
scope
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter_integer_gte(scope, attribute, value)
|
60
|
+
scope
|
61
|
+
end
|
62
|
+
|
63
|
+
def filter_integer_lt(scope, attribute, value)
|
64
|
+
scope
|
65
|
+
end
|
66
|
+
|
67
|
+
def filter_integer_lte(scope, attribute, value)
|
68
|
+
scope
|
69
|
+
end
|
70
|
+
|
71
|
+
def filter_datetime_eq(scope, attribute, value)
|
72
|
+
scope
|
73
|
+
end
|
74
|
+
|
75
|
+
def filter_datetime_not_eq(scope, attribute, value)
|
76
|
+
scope
|
77
|
+
end
|
78
|
+
|
79
|
+
def filter_datetime_lte(scope, attribute, value)
|
80
|
+
scope
|
81
|
+
end
|
82
|
+
|
83
|
+
def filter_float_eq(scope, attribute, value)
|
84
|
+
scope
|
85
|
+
end
|
86
|
+
|
87
|
+
def filter_float_not_eq(scope, attribute, value)
|
88
|
+
scope
|
89
|
+
end
|
90
|
+
|
91
|
+
def filter_float_gt(scope, attribute, value)
|
92
|
+
scope
|
93
|
+
end
|
94
|
+
|
95
|
+
def filter_float_gte(scope, attribute, value)
|
96
|
+
scope
|
97
|
+
end
|
98
|
+
|
99
|
+
def filter_float_lt(scope, attribute, value)
|
100
|
+
scope
|
101
|
+
end
|
102
|
+
|
103
|
+
def filter_float_lte(scope, attribute, value)
|
104
|
+
scope
|
105
|
+
end
|
106
|
+
|
107
|
+
def filter_decimal_eq(scope, attribute, value)
|
108
|
+
scope
|
109
|
+
end
|
110
|
+
|
111
|
+
def filter_decimal_not_eq(scope, attribute, value)
|
112
|
+
scope
|
113
|
+
end
|
114
|
+
|
115
|
+
def filter_decimal_gt(scope, attribute, value)
|
116
|
+
scope
|
117
|
+
end
|
118
|
+
|
119
|
+
def filter_decimal_gte(scope, attribute, value)
|
120
|
+
scope
|
121
|
+
end
|
122
|
+
|
123
|
+
def filter_decimal_lt(scope, attribute, value)
|
124
|
+
scope
|
125
|
+
end
|
126
|
+
|
127
|
+
def filter_decimal_lte(scope, attribute, value)
|
128
|
+
scope
|
129
|
+
end
|
130
|
+
|
131
|
+
def filter_datetime_eq(scope, attribute, value)
|
132
|
+
scope
|
133
|
+
end
|
134
|
+
|
135
|
+
def filter_datetime_not_eq(scope, attribute, value)
|
136
|
+
scope
|
137
|
+
end
|
138
|
+
|
139
|
+
def filter_datetime_gt(scope, attribute, value)
|
140
|
+
scope
|
141
|
+
end
|
142
|
+
|
143
|
+
def filter_datetime_gte(scope, attribute, value)
|
144
|
+
scope
|
145
|
+
end
|
146
|
+
|
147
|
+
def filter_datetime_lt(scope, attribute, value)
|
148
|
+
scope
|
149
|
+
end
|
150
|
+
|
151
|
+
def filter_datetime_lte(scope, attribute, value)
|
152
|
+
scope
|
153
|
+
end
|
154
|
+
|
155
|
+
def filter_date_eq(scope, attribute, value)
|
156
|
+
scope
|
157
|
+
end
|
158
|
+
|
159
|
+
def filter_date_not_eq(scope, attribute, value)
|
160
|
+
scope
|
161
|
+
end
|
162
|
+
|
163
|
+
def filter_date_gt(scope, attribute, value)
|
164
|
+
scope
|
165
|
+
end
|
166
|
+
|
167
|
+
def filter_date_gte(scope, attribute, value)
|
168
|
+
scope
|
169
|
+
end
|
170
|
+
|
171
|
+
def filter_date_lt(scope, attribute, value)
|
172
|
+
scope
|
173
|
+
end
|
174
|
+
|
175
|
+
def filter_date_lte(scope, attribute, value)
|
176
|
+
scope
|
177
|
+
end
|
178
|
+
|
179
|
+
def filter_boolean_eq(scope, attribute, value)
|
180
|
+
scope
|
181
|
+
end
|
182
|
+
|
183
|
+
def base_scope(model)
|
184
|
+
raise 'Null adapter has no base scope!'
|
185
|
+
end
|
186
|
+
|
12
187
|
# (see Adapters::Abstract#order)
|
13
188
|
def order(scope, attribute, direction)
|
14
189
|
scope
|
@@ -56,10 +231,6 @@ module JsonapiCompliable
|
|
56
231
|
def resolve(scope)
|
57
232
|
scope
|
58
233
|
end
|
59
|
-
|
60
|
-
# (see Adapters::Abstract#associate)
|
61
|
-
def associate(parent, child, association_name, association_type)
|
62
|
-
end
|
63
234
|
end
|
64
235
|
end
|
65
236
|
end
|
@@ -1,357 +1,70 @@
|
|
1
1
|
module JsonapiCompliable
|
2
|
-
# Provides main interface to jsonapi_compliable
|
3
|
-
#
|
4
|
-
# This gets mixed in to a "context" class, such as a Rails controller.
|
5
2
|
module Base
|
6
3
|
extend ActiveSupport::Concern
|
7
4
|
|
8
|
-
included do
|
9
|
-
class << self
|
10
|
-
attr_accessor :_jsonapi_compliable, :_sideload_whitelist
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.inherited(klass)
|
14
|
-
super
|
15
|
-
klass._jsonapi_compliable = Class.new(_jsonapi_compliable)
|
16
|
-
klass._sideload_whitelist = _sideload_whitelist.dup if _sideload_whitelist
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# @!classmethods
|
21
|
-
module ClassMethods
|
22
|
-
# Define your JSONAPI configuration
|
23
|
-
#
|
24
|
-
# @example Inline Resource
|
25
|
-
# # 'Quick and Dirty' solution that does not require a separate
|
26
|
-
# # Resource object
|
27
|
-
# class PostsController < ApplicationController
|
28
|
-
# jsonapi do
|
29
|
-
# type :posts
|
30
|
-
# use_adapter JsonapiCompliable::Adapters::ActiveRecord
|
31
|
-
#
|
32
|
-
# allow_filter :title
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# @example Resource Class (preferred)
|
37
|
-
# # Make code reusable by encapsulating it in a Resource class
|
38
|
-
# class PostsController < ApplicationController
|
39
|
-
# jsonapi resource: PostResource
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# @see Resource
|
43
|
-
# @param resource [Resource] the Resource class associated to this endpoint
|
44
|
-
# @return [void]
|
45
|
-
def jsonapi(foo = 'bar', resource: nil, &blk)
|
46
|
-
if resource
|
47
|
-
self._jsonapi_compliable = resource
|
48
|
-
else
|
49
|
-
if !self._jsonapi_compliable
|
50
|
-
self._jsonapi_compliable = Class.new(JsonapiCompliable::Resource)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
self._jsonapi_compliable.class_eval(&blk) if blk
|
55
|
-
end
|
56
|
-
|
57
|
-
# Set the sideload whitelist. You may want to omit sideloads for
|
58
|
-
# security or performance reasons.
|
59
|
-
#
|
60
|
-
# Uses JSONAPI::IncludeDirective from {{http://jsonapi-rb.org jsonapi-rb}}
|
61
|
-
#
|
62
|
-
# @example Whitelisting Relationships
|
63
|
-
# # Given the following whitelist
|
64
|
-
# class PostsController < ApplicationResource
|
65
|
-
# jsonapi resource: MyResource
|
66
|
-
#
|
67
|
-
# sideload_whitelist({
|
68
|
-
# index: [:blog],
|
69
|
-
# show: [:blog, { comments: :author }]
|
70
|
-
# })
|
71
|
-
#
|
72
|
-
# # ... code ...
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# # A request to sideload 'tags'
|
76
|
-
# #
|
77
|
-
# # GET /posts/1?include=tags
|
78
|
-
# #
|
79
|
-
# # ...will silently fail.
|
80
|
-
# #
|
81
|
-
# # A request for comments and tags:
|
82
|
-
# #
|
83
|
-
# # GET /posts/1?include=tags,comments
|
84
|
-
# #
|
85
|
-
# # ...will only sideload comments
|
86
|
-
#
|
87
|
-
# @param [Hash, Array, Symbol] whitelist
|
88
|
-
# @see Query#include_hash
|
89
|
-
def sideload_whitelist(hash)
|
90
|
-
self._sideload_whitelist = JSONAPI::IncludeDirective.new(hash).to_hash
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# @api private
|
95
|
-
def sideload_whitelist
|
96
|
-
self.class._sideload_whitelist || {}
|
97
|
-
end
|
98
|
-
|
99
|
-
# Returns an instance of the associated Resource
|
100
|
-
#
|
101
|
-
# In other words, if you configured your controller as:
|
102
|
-
#
|
103
|
-
# jsonapi resource: MyResource
|
104
|
-
#
|
105
|
-
# This returns MyResource.new
|
106
|
-
#
|
107
|
-
# @return [Resource] the configured Resource for this controller
|
108
5
|
def jsonapi_resource
|
109
|
-
@jsonapi_resource
|
110
|
-
resource = self.class._jsonapi_compliable
|
111
|
-
if resource.is_a?(Hash)
|
112
|
-
resource[action_name.to_sym].new
|
113
|
-
else
|
114
|
-
resource.new
|
115
|
-
end
|
116
|
-
end
|
6
|
+
@jsonapi_resource
|
117
7
|
end
|
118
8
|
|
119
|
-
# Instantiates the relevant Query object
|
120
|
-
#
|
121
|
-
# @see Query
|
122
|
-
# @return [Query] the Query object for this resource/params
|
123
9
|
def query
|
124
10
|
@query ||= Query.new(jsonapi_resource, params)
|
125
11
|
end
|
126
12
|
|
127
|
-
# @see Query#to_hash
|
128
|
-
# @return [Hash] the normalized query hash for only the *current* resource
|
129
13
|
def query_hash
|
130
|
-
@query_hash ||= query.to_hash
|
14
|
+
@query_hash ||= query.to_hash
|
131
15
|
end
|
132
16
|
|
133
|
-
# Tracks the current context so we can refer to it within any
|
134
|
-
# random object. Helpful for easy-access to things like the current
|
135
|
-
# user.
|
136
|
-
#
|
137
|
-
# @api private
|
138
|
-
# @yieldreturn Code to run within the current context
|
139
17
|
def wrap_context
|
140
|
-
|
18
|
+
JsonapiCompliable.with_context(jsonapi_context, action_name.to_sym) do
|
141
19
|
yield
|
142
20
|
end
|
143
21
|
end
|
144
22
|
|
145
|
-
# Override if you'd like the "context" to be something other than
|
146
|
-
# an instance of this class.
|
147
|
-
# In Rails, context is the controller instance.
|
148
|
-
#
|
149
|
-
# @example Overriding
|
150
|
-
# # within your controller
|
151
|
-
# def jsonapi_context
|
152
|
-
# current_user
|
153
|
-
# end
|
154
|
-
#
|
155
|
-
# # within a resource
|
156
|
-
# default_filter :by_user do |scope, context|
|
157
|
-
# scope.where(user_id: context.id)
|
158
|
-
# end
|
159
|
-
#
|
160
|
-
# @return the context object you'd like
|
161
23
|
def jsonapi_context
|
162
24
|
self
|
163
25
|
end
|
164
26
|
|
165
|
-
# Use when direct, low-level access to the scope is required.
|
166
|
-
#
|
167
|
-
# @example Show Action
|
168
|
-
# # Scope#resolve returns an array, but we only want to render
|
169
|
-
# # one object, not an array
|
170
|
-
# scope = jsonapi_scope(Employee.where(id: params[:id]))
|
171
|
-
# render_jsonapi(scope.resolve.first, scope: false)
|
172
|
-
#
|
173
|
-
# @example Scope Chaining
|
174
|
-
# # Chain onto scope after running through typical DSL
|
175
|
-
# # Here, we'll add active: true to our hash if the user
|
176
|
-
# # is filtering on something
|
177
|
-
# scope = jsonapi_scope({})
|
178
|
-
# scope.object.merge!(active: true) if scope.object[:filter]
|
179
|
-
#
|
180
|
-
# @see Resource#build_scope
|
181
|
-
# @return [Scope] the configured scope
|
182
27
|
def jsonapi_scope(scope, opts = {})
|
183
28
|
jsonapi_resource.build_scope(scope, query, opts)
|
184
29
|
end
|
185
30
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
# Create the resource model and process all nested relationships via the
|
193
|
-
# serialized parameters. Any error, including validation errors, will roll
|
194
|
-
# back the transaction.
|
195
|
-
#
|
196
|
-
# @example Basic Rails
|
197
|
-
# # Example Resource must have 'model'
|
198
|
-
# #
|
199
|
-
# # class PostResource < ApplicationResource
|
200
|
-
# # model Post
|
201
|
-
# # end
|
202
|
-
# def create
|
203
|
-
# post, success = jsonapi_create.to_a
|
204
|
-
#
|
205
|
-
# if success
|
206
|
-
# render_jsonapi(post, scope: false)
|
207
|
-
# else
|
208
|
-
# render_errors_for(post)
|
209
|
-
# end
|
210
|
-
# end
|
211
|
-
#
|
212
|
-
# @see Resource.model
|
213
|
-
# @see #resource
|
214
|
-
# @see #deserialized_params
|
215
|
-
# @return [Util::ValidationResponse]
|
216
|
-
def jsonapi_create
|
217
|
-
_persist do
|
218
|
-
jsonapi_resource.persist_with_relationships \
|
219
|
-
deserialized_params.meta,
|
220
|
-
deserialized_params.attributes,
|
221
|
-
deserialized_params.relationships
|
31
|
+
def normalized_params
|
32
|
+
normalized = params
|
33
|
+
if normalized.respond_to?(:to_unsafe_h)
|
34
|
+
normalized = normalized.to_unsafe_h.deep_symbolize_keys
|
222
35
|
end
|
36
|
+
normalized
|
223
37
|
end
|
224
38
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
# # Example Resource must have 'model'
|
231
|
-
# #
|
232
|
-
# # class PostResource < ApplicationResource
|
233
|
-
# # model Post
|
234
|
-
# # end
|
235
|
-
# def update
|
236
|
-
# post, success = jsonapi_update.to_a
|
237
|
-
#
|
238
|
-
# if success
|
239
|
-
# render_jsonapi(post, scope: false)
|
240
|
-
# else
|
241
|
-
# render_errors_for(post)
|
242
|
-
# end
|
243
|
-
# end
|
244
|
-
#
|
245
|
-
# @see #jsonapi_create
|
246
|
-
# @return [Util::ValidationResponse]
|
247
|
-
def jsonapi_update
|
248
|
-
_persist do
|
249
|
-
jsonapi_resource.persist_with_relationships \
|
250
|
-
deserialized_params.meta,
|
251
|
-
deserialized_params.attributes,
|
252
|
-
deserialized_params.relationships
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
# Delete the model
|
257
|
-
# Any error, including validation errors, will roll back the transaction.
|
258
|
-
#
|
259
|
-
# Note: +before_commit+ hooks still run unless excluded
|
260
|
-
#
|
261
|
-
# @return [Util::ValidationResponse]
|
262
|
-
def jsonapi_destroy
|
263
|
-
jsonapi_resource.transaction do
|
264
|
-
model = jsonapi_resource.destroy(params[:id])
|
265
|
-
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
266
|
-
model, deserialized_params
|
267
|
-
validator.validate!
|
268
|
-
jsonapi_resource.before_commit(model, :destroy)
|
269
|
-
validator
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
# Similar to +render :json+ or +render :jsonapi+
|
274
|
-
#
|
275
|
-
# By default, this will "build" the scope via +#jsonapi_scope+. To avoid
|
276
|
-
# this, pass +scope: false+
|
277
|
-
#
|
278
|
-
# This builds relevant options and sends them to
|
279
|
-
# +JSONAPI::Serializable::SuccessRenderer#render+from
|
280
|
-
# {http://jsonapi-rb.org jsonapi-rb}
|
281
|
-
#
|
282
|
-
# @example Build Scope by Default
|
283
|
-
# # Employee.all returns an ActiveRecord::Relation. No SQL is fired at this point.
|
284
|
-
# # We further 'chain' onto this scope, applying pagination, sorting,
|
285
|
-
# # filters, etc that the user has requested.
|
286
|
-
# def index
|
287
|
-
# employees = Employee.all
|
288
|
-
# render_jsonapi(employees)
|
289
|
-
# end
|
290
|
-
#
|
291
|
-
# @example Avoid Building Scope by Default
|
292
|
-
# # Maybe we already manually scoped, and don't want to fire the logic twice
|
293
|
-
# # This code is equivalent to the above example
|
294
|
-
# def index
|
295
|
-
# scope = jsonapi_scope(Employee.all)
|
296
|
-
# # ... do other things with the scope ...
|
297
|
-
# render_jsonapi(scope.resolve, scope: false)
|
298
|
-
# end
|
299
|
-
#
|
300
|
-
# @param scope [Scope, Object] the scope to build or render.
|
301
|
-
# @param [Hash] opts the render options passed to {http://jsonapi-rb.org jsonapi-rb}
|
302
|
-
# @option opts [Boolean] :scope Default: true. Should we call #jsonapi_scope on this object?
|
303
|
-
# @see #jsonapi_scope
|
304
|
-
def render_jsonapi(scope, opts = {})
|
305
|
-
scope = jsonapi_scope(scope) unless opts[:scope] == false || scope.is_a?(JsonapiCompliable::Scope)
|
306
|
-
opts = default_jsonapi_render_options.merge(opts)
|
307
|
-
opts = Util::RenderOptions.generate(scope, query_hash, opts)
|
308
|
-
opts[:expose][:context] = self
|
309
|
-
|
310
|
-
if opts[:include].empty? && force_includes?
|
311
|
-
opts[:include] = deserialized_params.include_directive
|
312
|
-
end
|
313
|
-
|
314
|
-
perform_render_jsonapi(opts)
|
315
|
-
end
|
316
|
-
|
317
|
-
# Define a hash that will be automatically merged into your
|
318
|
-
# render_jsonapi call
|
319
|
-
#
|
320
|
-
# @example
|
321
|
-
# # this
|
322
|
-
# render_jsonapi(foo)
|
323
|
-
# # is equivalent to this
|
324
|
-
# render jsonapi: foo, default_jsonapi_render_options
|
325
|
-
#
|
326
|
-
# @see #render_jsonapi
|
327
|
-
# @return [Hash] the options hash you define
|
328
|
-
def default_jsonapi_render_options
|
329
|
-
{}.tap do |options|
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
private
|
334
|
-
|
335
|
-
def _persist
|
336
|
-
jsonapi_resource.transaction do
|
337
|
-
::JsonapiCompliable::Util::Hooks.record do
|
338
|
-
model = yield
|
339
|
-
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
340
|
-
model, deserialized_params
|
341
|
-
validator.validate!
|
342
|
-
validator
|
39
|
+
def deserialized_params
|
40
|
+
@deserialized_params ||= begin
|
41
|
+
payload = normalized_params
|
42
|
+
if payload[:data] && payload[:data][:type]
|
43
|
+
JsonapiCompliable::Deserializer.new(payload)
|
343
44
|
end
|
344
45
|
end
|
345
46
|
end
|
346
47
|
|
347
|
-
def
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
48
|
+
def jsonapi_render_options
|
49
|
+
options = {}
|
50
|
+
options.merge!(default_jsonapi_render_options)
|
51
|
+
options[:meta] ||= {}
|
52
|
+
options[:expose] ||= {}
|
53
|
+
options[:expose][:context] = jsonapi_context
|
54
|
+
options
|
55
|
+
end
|
56
|
+
|
57
|
+
def proxy(base = nil, opts = {})
|
58
|
+
base ||= jsonapi_resource.base_scope
|
59
|
+
scope_opts = opts.slice :sideload_parent_length,
|
60
|
+
:default_paginate, :after_resolve
|
61
|
+
scope = jsonapi_scope(base, scope_opts)
|
62
|
+
ResourceProxy.new jsonapi_resource,
|
63
|
+
scope,
|
64
|
+
query,
|
65
|
+
payload: deserialized_params,
|
66
|
+
single: opts[:single],
|
67
|
+
raise_on_missing: opts[:raise_on_missing]
|
355
68
|
end
|
356
69
|
end
|
357
70
|
end
|