jsonapi_compliable 0.11.34 → 1.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|