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,25 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class FilterOperators
|
3
|
+
class Catchall
|
4
|
+
attr_reader :procs
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@procs = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *args, &blk)
|
11
|
+
@procs[name] = blk
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_hash
|
15
|
+
@procs
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build(&blk)
|
20
|
+
c = Catchall.new
|
21
|
+
c.instance_eval(&blk) if blk
|
22
|
+
c.to_hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
module SerializableHash
|
3
|
+
def to_hash(fields: nil, include: {})
|
4
|
+
{}.tap do |hash|
|
5
|
+
_fields = fields[jsonapi_type] if fields
|
6
|
+
attrs = requested_attributes(_fields).each_with_object({}) do |(k, v), h|
|
7
|
+
h[k] = instance_eval(&v)
|
8
|
+
end
|
9
|
+
rels = @_relationships.select { |k,v| !!include[k] }
|
10
|
+
rels.each_with_object({}) do |(k, v), h|
|
11
|
+
serializers = v.send(:resources)
|
12
|
+
attrs[k] = if serializers.is_a?(Array)
|
13
|
+
serializers.map do |rr| # use private method to avoid array casting
|
14
|
+
rr.to_hash(fields: fields, include: include[k])
|
15
|
+
end
|
16
|
+
elsif serializers.nil?
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
serializers.to_hash(fields: fields, include: include[k])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
hash[:id] = jsonapi_id
|
24
|
+
hash.merge!(attrs) if attrs.any?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
JSONAPI::Serializable::Resource.send(:include, SerializableHash)
|
29
|
+
|
30
|
+
class HashRenderer
|
31
|
+
def initialize(resource)
|
32
|
+
@resource = resource
|
33
|
+
end
|
34
|
+
|
35
|
+
def render(options)
|
36
|
+
serializers = options[:data]
|
37
|
+
opts = options.slice(:fields, :include)
|
38
|
+
to_hash(serializers, opts).tap do |hash|
|
39
|
+
hash.merge!(options.slice(:meta)) if !options[:meta].empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def to_hash(serializers, opts)
|
46
|
+
{}.tap do |hash|
|
47
|
+
if serializers.is_a?(Array)
|
48
|
+
hash[@resource.type] = serializers.map do |s|
|
49
|
+
s.to_hash(opts)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
hash[@resource.type] = serializers.to_hash(opts)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,263 +1,251 @@
|
|
1
1
|
module JsonapiCompliable
|
2
|
-
# @attr_reader [Hash] params hash of query parameters with symbolized keys
|
3
|
-
# @attr_reader [Resource] resource the corresponding Resource object
|
4
2
|
class Query
|
5
|
-
|
6
|
-
attr_reader :params, :resource
|
7
|
-
|
8
|
-
# This is the structure of +Query#to_hash+ used elsewhere in the library
|
9
|
-
# @see #to_hash
|
10
|
-
# @api private
|
11
|
-
# @return [Hash] the default hash
|
12
|
-
def self.default_hash
|
13
|
-
{
|
14
|
-
filter: {},
|
15
|
-
sort: [],
|
16
|
-
page: {},
|
17
|
-
include: {},
|
18
|
-
stats: {},
|
19
|
-
fields: {},
|
20
|
-
extra_fields: {}
|
21
|
-
}
|
22
|
-
end
|
3
|
+
attr_reader :resource, :include_hash, :association_name
|
23
4
|
|
24
|
-
def initialize(resource, params)
|
5
|
+
def initialize(resource, params, association_name = nil, nested_include = nil, parents = [])
|
25
6
|
@resource = resource
|
7
|
+
@association_name = association_name
|
26
8
|
@params = params
|
27
9
|
@params = @params.permit! if @params.respond_to?(:permit!)
|
10
|
+
@params = @params.to_h if @params.respond_to?(:to_h)
|
11
|
+
@params = @params.deep_symbolize_keys
|
12
|
+
@include_param = nested_include || @params[:include]
|
13
|
+
@parents = parents
|
28
14
|
end
|
29
15
|
|
30
|
-
|
31
|
-
|
32
|
-
# @return [JSONAPI::IncludeDirective]
|
33
|
-
def include_directive
|
34
|
-
@include_directive ||= JSONAPI::IncludeDirective.new(params[:include])
|
35
|
-
end
|
36
|
-
|
37
|
-
# The include, directive, as a hash. For instance
|
38
|
-
#
|
39
|
-
# { posts: { comments: {} } }
|
40
|
-
#
|
41
|
-
# This will only include relationships that are
|
42
|
-
#
|
43
|
-
# * Available on the Resource
|
44
|
-
# * Whitelisted (when specified)
|
45
|
-
#
|
46
|
-
# So that users can't simply request your entire object graph.
|
47
|
-
#
|
48
|
-
# @see Util::IncludeParams
|
49
|
-
# @return [Hash] the scrubbed include directive as a hash
|
50
|
-
def include_hash
|
51
|
-
@include_hash ||= begin
|
52
|
-
requested = include_directive.to_hash
|
53
|
-
|
54
|
-
whitelist = nil
|
55
|
-
if resource.context
|
56
|
-
whitelist = resource.context.sideload_whitelist
|
57
|
-
whitelist = whitelist[resource.context_namespace] if whitelist
|
58
|
-
end
|
59
|
-
|
60
|
-
whitelist ? Util::IncludeParams.scrub(requested, whitelist) : requested
|
61
|
-
end
|
16
|
+
def association?
|
17
|
+
!!@association_name
|
62
18
|
end
|
63
19
|
|
64
|
-
|
65
|
-
|
66
|
-
# For example, let's say we had
|
67
|
-
#
|
68
|
-
# { posts: { comments: {} }
|
69
|
-
#
|
70
|
-
# +#association_names+ would return
|
71
|
-
#
|
72
|
-
# [:posts, :comments]
|
73
|
-
#
|
74
|
-
# @return [Array<Symbol>] all association names, recursive
|
75
|
-
def association_names
|
76
|
-
@association_names ||= Util::Hash.keys(include_hash)
|
20
|
+
def top_level?
|
21
|
+
not association?
|
77
22
|
end
|
78
23
|
|
79
|
-
# A flat hash of sanitized query parameters.
|
80
|
-
# All relationship names are top-level:
|
81
|
-
#
|
82
|
-
# {
|
83
|
-
# posts: { filter, sort, ... }
|
84
|
-
# comments: { filter, sort, ... }
|
85
|
-
# }
|
86
|
-
#
|
87
|
-
# @example sorting
|
88
|
-
# # GET /posts?sort=-title
|
89
|
-
# { posts: { sort: { title: :desc } } }
|
90
|
-
#
|
91
|
-
# @example pagination
|
92
|
-
# # GET /posts?page[number]=2&page[size]=10
|
93
|
-
# { posts: { page: { number: 2, size: 10 } }
|
94
|
-
#
|
95
|
-
# @example filtering
|
96
|
-
# # GET /posts?filter[title]=Foo
|
97
|
-
# { posts: { filter: { title: 'Foo' } }
|
98
|
-
#
|
99
|
-
# @example include
|
100
|
-
# # GET /posts?include=comments.author
|
101
|
-
# { posts: { include: { comments: { author: {} } } } }
|
102
|
-
#
|
103
|
-
# @example stats
|
104
|
-
# # GET /posts?stats[likes]=count,average
|
105
|
-
# { posts: { stats: [:count, :average] } }
|
106
|
-
#
|
107
|
-
# @example fields
|
108
|
-
# # GET /posts?fields=foo,bar
|
109
|
-
# { posts: { fields: [:foo, :bar] } }
|
110
|
-
#
|
111
|
-
# @example extra fields
|
112
|
-
# # GET /posts?fields=foo,bar
|
113
|
-
# { posts: { extra_fields: [:foo, :bar] } }
|
114
|
-
#
|
115
|
-
# @example nested parameters
|
116
|
-
# # GET /posts?include=comments&sort=comments.created_at&page[comments][size]=3
|
117
|
-
# {
|
118
|
-
# posts: { ... },
|
119
|
-
# comments: { page: { size: 3 }, sort: { created_at: :asc } }
|
120
|
-
#
|
121
|
-
# @see #default_hash
|
122
|
-
# @see Base#query_hash
|
123
|
-
# @return [Hash] the normalized query hash
|
124
24
|
def to_hash
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
hash[
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
hash[
|
135
|
-
hash[type][:extra_fields] = extra_fields
|
25
|
+
{}.tap do |hash|
|
26
|
+
hash[:filter] = filters unless filters.empty?
|
27
|
+
hash[:sort] = sorts unless sorts.empty?
|
28
|
+
hash[:page] = pagination unless pagination.empty?
|
29
|
+
unless association?
|
30
|
+
hash[:fields] = fields unless fields.empty?
|
31
|
+
hash[:extra_fields] = extra_fields unless extra_fields.empty?
|
32
|
+
end
|
33
|
+
hash[:stats] = stats unless stats.empty?
|
34
|
+
hash[:include] = sideload_hash unless sideload_hash.empty?
|
136
35
|
end
|
137
|
-
|
138
|
-
parse_filter(hash)
|
139
|
-
parse_sort(hash)
|
140
|
-
parse_pagination(hash)
|
141
|
-
|
142
|
-
parse_include(hash, include_hash, resource.type)
|
143
|
-
parse_stats(hash)
|
144
|
-
|
145
|
-
hash
|
146
36
|
end
|
147
37
|
|
148
|
-
# Check if the user has requested 0 actual results
|
149
|
-
# They may have done this to get, say, the total count
|
150
|
-
# without the overhead of fetching actual records.
|
151
|
-
#
|
152
|
-
# @example Total Count, 0 Results
|
153
|
-
# # GET /posts?page[size]=0&stats[total]=count
|
154
|
-
# # Response:
|
155
|
-
# {
|
156
|
-
# data: [],
|
157
|
-
# meta: {
|
158
|
-
# stats: { total: { count: 100 } }
|
159
|
-
# }
|
160
|
-
# }
|
161
|
-
#
|
162
|
-
# @return [Boolean] were 0 results requested?
|
163
38
|
def zero_results?
|
164
39
|
!@params[:page].nil? &&
|
165
40
|
!@params[:page][:size].nil? &&
|
166
41
|
@params[:page][:size].to_i == 0
|
167
42
|
end
|
168
43
|
|
169
|
-
|
44
|
+
def sideload_hash
|
45
|
+
@sideload_hash = begin
|
46
|
+
{}.tap do |hash|
|
47
|
+
sideloads.each_pair do |key, value|
|
48
|
+
hash[key] = sideloads[key].to_hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
170
53
|
|
171
|
-
def
|
172
|
-
|
54
|
+
def sideloads
|
55
|
+
@sideloads ||= begin
|
56
|
+
{}.tap do |hash|
|
57
|
+
include_hash.each_pair do |key, sub_hash|
|
58
|
+
sideload = @resource.class.sideload(key)
|
59
|
+
if sideload
|
60
|
+
_parents = parents + [self]
|
61
|
+
hash[key] = Query.new(sideload.resource, @params, key, sub_hash, _parents)
|
62
|
+
else
|
63
|
+
handle_missing_sideload(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
173
68
|
end
|
174
69
|
|
175
|
-
def
|
176
|
-
|
177
|
-
|
70
|
+
def parents
|
71
|
+
@parents ||= []
|
72
|
+
end
|
178
73
|
|
179
|
-
|
180
|
-
|
181
|
-
|
74
|
+
def fields
|
75
|
+
@fields ||= begin
|
76
|
+
hash = parse_fieldset(@params[:fields] || {})
|
77
|
+
hash.each_pair do |type, fields|
|
78
|
+
hash[type] += extra_fields[type] if extra_fields[type]
|
79
|
+
end
|
80
|
+
hash
|
182
81
|
end
|
82
|
+
end
|
183
83
|
|
184
|
-
|
84
|
+
def extra_fields
|
85
|
+
@extra_fields ||= parse_fieldset(@params[:extra_fields] || {})
|
185
86
|
end
|
186
87
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
88
|
+
def filters
|
89
|
+
@filters ||= begin
|
90
|
+
{}.tap do |hash|
|
91
|
+
(@params[:filter] || {}).each_pair do |name, value|
|
92
|
+
name = name.to_sym
|
93
|
+
|
94
|
+
if legacy_nested?(name)
|
95
|
+
filter_name = value.keys.first.to_sym
|
96
|
+
filter_value = value.values.first
|
97
|
+
if @resource.get_attr!(filter_name, :filterable, request: true)
|
98
|
+
hash[filter_name] = filter_value
|
99
|
+
end
|
100
|
+
elsif nested?(name)
|
101
|
+
name = name.to_s.split('.').last.to_sym
|
102
|
+
validate!(name, :filterable)
|
103
|
+
hash[name] = value
|
104
|
+
elsif top_level? && validate!(name, :filterable)
|
105
|
+
hash[name] = value
|
193
106
|
end
|
194
|
-
else
|
195
|
-
hash[resource.type][:stats][namespace] = calculations.split(',').map(&:to_sym)
|
196
107
|
end
|
197
108
|
end
|
198
109
|
end
|
199
110
|
end
|
200
111
|
|
201
|
-
def
|
202
|
-
|
203
|
-
|
112
|
+
def sorts
|
113
|
+
@sorts ||= begin
|
114
|
+
return @params[:sort] if @params[:sort].is_a?(Array)
|
115
|
+
return [] if @params[:sort].nil?
|
116
|
+
|
117
|
+
[].tap do |arr|
|
118
|
+
sort_hashes do |key, value, type|
|
119
|
+
if legacy_nested?(type)
|
120
|
+
@resource.get_attr!(key, :sortable, request: true)
|
121
|
+
arr << { key => value }
|
122
|
+
elsif !type && top_level? && validate!(key, :sortable)
|
123
|
+
arr << { key => value }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
204
128
|
end
|
205
129
|
|
206
|
-
def
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
130
|
+
def pagination
|
131
|
+
@pagination ||= begin
|
132
|
+
{}.tap do |hash|
|
133
|
+
(@params[:page] || {}).each_pair do |name, value|
|
134
|
+
if legacy_nested?(name)
|
135
|
+
value.each_pair do |k,v|
|
136
|
+
hash[k.to_sym] = v.to_i
|
137
|
+
end
|
138
|
+
elsif top_level? && [:number, :size].include?(name.to_sym)
|
139
|
+
hash[name.to_sym] = value.to_i
|
214
140
|
end
|
215
|
-
hash[key][:filter].merge!(symbolized_hash)
|
216
|
-
else
|
217
|
-
hash[resource.type][:filter][key] = value
|
218
141
|
end
|
219
142
|
end
|
220
143
|
end
|
221
144
|
end
|
222
145
|
|
223
|
-
def
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
146
|
+
def include_hash
|
147
|
+
@include_hash ||= begin
|
148
|
+
requested = include_directive.to_hash
|
149
|
+
|
150
|
+
whitelist = nil
|
151
|
+
if @resource.context && @resource.context.respond_to?(:sideload_whitelist)
|
152
|
+
whitelist = @resource.context.sideload_whitelist
|
153
|
+
whitelist = whitelist[@resource.context_namespace] if whitelist
|
154
|
+
end
|
155
|
+
|
156
|
+
whitelist ? Util::IncludeParams.scrub(requested, whitelist) : requested
|
157
|
+
end
|
158
|
+
end
|
233
159
|
|
234
|
-
|
235
|
-
|
236
|
-
|
160
|
+
def stats
|
161
|
+
@stats ||= begin
|
162
|
+
{}.tap do |hash|
|
163
|
+
(@params[:stats] || {}).each_pair do |k, v|
|
164
|
+
if legacy_nested?(k)
|
165
|
+
raise NotImplementedError.new('Association statistics are not currently supported')
|
166
|
+
elsif top_level?
|
167
|
+
v = v.split(',') if v.is_a?(String)
|
168
|
+
hash[k.to_sym] = Array(v).flatten.map(&:to_sym)
|
169
|
+
end
|
237
170
|
end
|
238
171
|
end
|
239
172
|
end
|
240
173
|
end
|
241
174
|
|
242
|
-
|
243
|
-
if pagination = params[:page]
|
244
|
-
pagination.each_pair do |key, value|
|
245
|
-
key = key.to_sym
|
175
|
+
private
|
246
176
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
177
|
+
def validate!(name, flag)
|
178
|
+
return false if name.to_s.include?('.') # nested
|
179
|
+
|
180
|
+
not_associated_name = !@resource.class.association_names.include?(name)
|
181
|
+
not_associated_type = !@resource.class.association_types.include?(name)
|
182
|
+
|
183
|
+
if not_associated_name && not_associated_type
|
184
|
+
@resource.get_attr!(name, flag, request: true)
|
185
|
+
return true
|
186
|
+
end
|
187
|
+
false
|
188
|
+
end
|
189
|
+
|
190
|
+
def nested?(name)
|
191
|
+
return false unless association?
|
192
|
+
split = name.to_s.split('.')
|
193
|
+
query_names = split[0..split.length-2].map(&:to_sym)
|
194
|
+
my_names = parents.map(&:association_name).compact + [association_name].compact
|
195
|
+
query_names == my_names
|
196
|
+
end
|
197
|
+
|
198
|
+
def legacy_nested?(name)
|
199
|
+
association? &&
|
200
|
+
(name == @resource.type || name == @association_name)
|
201
|
+
end
|
202
|
+
|
203
|
+
def parse_fieldset(fieldset)
|
204
|
+
{}.tap do |hash|
|
205
|
+
fieldset.each_pair do |type, fields|
|
206
|
+
type = type.to_sym
|
207
|
+
fields = fields.split(',') unless fields.is_a?(Array)
|
208
|
+
hash[type] = fields.map(&:to_sym)
|
252
209
|
end
|
253
210
|
end
|
254
211
|
end
|
255
212
|
|
256
|
-
def
|
257
|
-
|
213
|
+
def include_directive
|
214
|
+
@include_directive ||= JSONAPI::IncludeDirective.new(@include_param)
|
215
|
+
end
|
216
|
+
|
217
|
+
def handle_missing_sideload(name)
|
218
|
+
if JsonapiCompliable.config.raise_on_missing_sideload
|
219
|
+
raise JsonapiCompliable::Errors::InvalidInclude
|
220
|
+
.new(name, @resource.type)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def sort_hash(attr)
|
225
|
+
value = attr[0] == '-' ? :desc : :asc
|
258
226
|
key = attr.sub('-', '').to_sym
|
259
227
|
|
260
228
|
{ key => value }
|
261
229
|
end
|
230
|
+
|
231
|
+
def sort_hashes
|
232
|
+
sorts = @params[:sort].split(',')
|
233
|
+
sorts.each do |s|
|
234
|
+
type, attr = s.split('.')
|
235
|
+
|
236
|
+
if attr.nil? # top-level
|
237
|
+
next if @association_name
|
238
|
+
hash = sort_hash(type)
|
239
|
+
yield hash.keys.first.to_sym, hash.values.first
|
240
|
+
else
|
241
|
+
if type[0] == '-'
|
242
|
+
type = type.sub('-', '')
|
243
|
+
attr = "-#{attr}"
|
244
|
+
end
|
245
|
+
hash = sort_hash(attr)
|
246
|
+
yield hash.keys.first.to_sym, hash.values.first, type.to_sym
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
262
250
|
end
|
263
251
|
end
|