jsonapi-resources 0.9.3 → 0.10.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/README.md +34 -11
  4. data/lib/bug_report_templates/rails_5_latest.rb +125 -0
  5. data/lib/bug_report_templates/rails_5_master.rb +140 -0
  6. data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
  7. data/lib/jsonapi/active_relation/join_manager.rb +297 -0
  8. data/lib/jsonapi/active_relation_resource.rb +879 -0
  9. data/lib/jsonapi/acts_as_resource_controller.rb +122 -105
  10. data/lib/jsonapi/basic_resource.rb +1162 -0
  11. data/lib/jsonapi/cached_response_fragment.rb +127 -0
  12. data/lib/jsonapi/compiled_json.rb +11 -1
  13. data/lib/jsonapi/configuration.rb +63 -8
  14. data/lib/jsonapi/error.rb +27 -0
  15. data/lib/jsonapi/error_codes.rb +2 -0
  16. data/lib/jsonapi/exceptions.rb +63 -40
  17. data/lib/jsonapi/formatter.rb +3 -3
  18. data/lib/jsonapi/include_directives.rb +18 -65
  19. data/lib/jsonapi/link_builder.rb +74 -80
  20. data/lib/jsonapi/operation.rb +16 -5
  21. data/lib/jsonapi/operation_result.rb +74 -16
  22. data/lib/jsonapi/path.rb +43 -0
  23. data/lib/jsonapi/path_segment.rb +76 -0
  24. data/lib/jsonapi/processor.rb +237 -110
  25. data/lib/jsonapi/relationship.rb +144 -15
  26. data/lib/jsonapi/request_parser.rb +412 -357
  27. data/lib/jsonapi/resource.rb +3 -1263
  28. data/lib/jsonapi/resource_controller_metal.rb +5 -2
  29. data/lib/jsonapi/resource_fragment.rb +47 -0
  30. data/lib/jsonapi/resource_id_tree.rb +112 -0
  31. data/lib/jsonapi/resource_identity.rb +42 -0
  32. data/lib/jsonapi/resource_serializer.rb +143 -285
  33. data/lib/jsonapi/resource_set.rb +176 -0
  34. data/lib/jsonapi/resources/railtie.rb +9 -0
  35. data/lib/jsonapi/resources/version.rb +1 -1
  36. data/lib/jsonapi/response_document.rb +105 -83
  37. data/lib/jsonapi/routing_ext.rb +48 -26
  38. data/lib/jsonapi-resources.rb +20 -4
  39. data/lib/tasks/check_upgrade.rake +52 -0
  40. metadata +47 -17
  41. data/lib/jsonapi/cached_resource_fragment.rb +0 -127
  42. data/lib/jsonapi/operation_dispatcher.rb +0 -88
  43. data/lib/jsonapi/operation_results.rb +0 -35
  44. data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -0,0 +1,127 @@
1
+ module JSONAPI
2
+ class CachedResponseFragment
3
+
4
+ Lookup = Struct.new(:resource_klass, :serializer_config_key, :context, :context_key, :cache_ids) do
5
+
6
+ def type
7
+ resource_klass._type
8
+ end
9
+
10
+ def keys
11
+ cache_ids.map do |(id, cache_key)|
12
+ [type, id, cache_key, serializer_config_key, context_key]
13
+ end
14
+ end
15
+ end
16
+
17
+ Write = Struct.new(:resource_klass, :resource, :serializer, :serializer_config_key, :context, :context_key, :relationship_data) do
18
+ def to_key_value
19
+
20
+ (id, cache_key) = resource.cache_id
21
+
22
+ json = serializer.object_hash(resource, relationship_data)
23
+
24
+ cr = CachedResponseFragment.new(
25
+ resource_klass,
26
+ id,
27
+ json['type'],
28
+ context,
29
+ resource.fetchable_fields,
30
+ json['relationships'],
31
+ json['links'],
32
+ json['attributes'],
33
+ json['meta']
34
+ )
35
+
36
+ key = [resource_klass._type, id, cache_key, serializer_config_key, context_key]
37
+
38
+ [key, cr]
39
+ end
40
+ end
41
+
42
+ attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships,
43
+ :links_json, :attributes_json, :meta_json
44
+
45
+ def initialize(resource_klass, id, type, context, fetchable_fields, relationships,
46
+ links_json, attributes_json, meta_json)
47
+ @resource_klass = resource_klass
48
+ @id = id
49
+ @type = type
50
+ @context = context
51
+ @fetchable_fields = Set.new(fetchable_fields)
52
+
53
+ # Relationships left uncompiled because we'll often want to insert included ids on retrieval
54
+ @relationships = relationships
55
+
56
+ @links_json = CompiledJson.of(links_json)
57
+ @attributes_json = CompiledJson.of(attributes_json)
58
+ @meta_json = CompiledJson.of(meta_json)
59
+ end
60
+
61
+ def to_cache_value
62
+ {
63
+ id: id,
64
+ type: type,
65
+ fetchable: fetchable_fields,
66
+ rels: relationships,
67
+ links: links_json.try(:to_s),
68
+ attrs: attributes_json.try(:to_s),
69
+ meta: meta_json.try(:to_s)
70
+ }
71
+ end
72
+
73
+ # @param [Lookup[]] lookups
74
+ # @return [Hash<Class<Resource>, Hash<ID, CachedResourceFragment>>]
75
+ def self.lookup(lookups, context)
76
+ type_to_klass = lookups.map {|l| [l.type, l.resource_klass]}.to_h
77
+
78
+ keys = lookups.map(&:keys).flatten(1)
79
+
80
+ hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject {|_, v| v.nil?}
81
+
82
+ return keys.inject({}) do |hash, key|
83
+ (type, id, _, _) = key
84
+ resource_klass = type_to_klass[type]
85
+ hash[resource_klass] ||= {}
86
+
87
+ if hits.has_key?(key)
88
+ hash[resource_klass][id] = self.from_cache_value(resource_klass, context, hits[key])
89
+ else
90
+ hash[resource_klass][id] = nil
91
+ end
92
+
93
+ hash
94
+ end
95
+ end
96
+
97
+ # @param [Write[]] lookups
98
+ def self.write(writes)
99
+ key_values = writes.map(&:to_key_value)
100
+
101
+ to_write = key_values.map {|(k, v)| [k, v.to_cache_value]}.to_h
102
+
103
+ if JSONAPI.configuration.resource_cache.respond_to? :write_multi
104
+ JSONAPI.configuration.resource_cache.write_multi(to_write)
105
+ else
106
+ to_write.each do |key, value|
107
+ JSONAPI.configuration.resource_cache.write(key, value)
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ def self.from_cache_value(resource_klass, context, h)
114
+ new(
115
+ resource_klass,
116
+ h.fetch(:id),
117
+ h.fetch(:type),
118
+ context,
119
+ h.fetch(:fetchable),
120
+ h.fetch(:rels, nil),
121
+ h.fetch(:links, nil),
122
+ h.fetch(:attrs, nil),
123
+ h.fetch(:meta, nil)
124
+ )
125
+ end
126
+ end
127
+ end
@@ -5,6 +5,7 @@ module JSONAPI
5
5
  end
6
6
 
7
7
  def self.of(obj)
8
+ # :nocov:
8
9
  case obj
9
10
  when NilClass then nil
10
11
  when CompiledJson then obj
@@ -12,6 +13,7 @@ module JSONAPI
12
13
  when Hash then CompiledJson.compile(obj)
13
14
  else raise "Can't figure out how to turn #{obj.inspect} into CompiledJson"
14
15
  end
16
+ # :nocov:
15
17
  end
16
18
 
17
19
  def initialize(json, h = nil)
@@ -19,7 +21,7 @@ module JSONAPI
19
21
  @h = h
20
22
  end
21
23
 
22
- def to_json(*args)
24
+ def to_json(*_args)
23
25
  @json
24
26
  end
25
27
 
@@ -27,9 +29,17 @@ module JSONAPI
27
29
  @json
28
30
  end
29
31
 
32
+ # :nocov:
30
33
  def to_h
31
34
  @h ||= JSON.parse(@json)
32
35
  end
36
+ # :nocov:
37
+
38
+ def [](key)
39
+ # :nocov:
40
+ to_h[key]
41
+ # :nocov:
42
+ end
33
43
 
34
44
  undef_method :as_json
35
45
  end
@@ -8,13 +8,17 @@ module JSONAPI
8
8
  :resource_key_type,
9
9
  :route_format,
10
10
  :raise_if_parameters_not_allowed,
11
- :allow_include,
11
+ :warn_on_route_setup_issues,
12
+ :warn_on_missing_routes,
13
+ :warn_on_performance_issues,
14
+ :default_allow_include_to_one,
15
+ :default_allow_include_to_many,
12
16
  :allow_sort,
13
17
  :allow_filter,
14
18
  :default_paginator,
15
19
  :default_page_size,
16
20
  :maximum_page_size,
17
- :default_processor_klass,
21
+ :default_processor_klass_name,
18
22
  :use_text_errors,
19
23
  :top_level_links_include_pagination,
20
24
  :top_level_meta_include_record_count,
@@ -23,6 +27,7 @@ module JSONAPI
23
27
  :top_level_meta_page_count_key,
24
28
  :allow_transactions,
25
29
  :include_backtraces_in_errors,
30
+ :include_application_backtraces_in_errors,
26
31
  :exception_class_whitelist,
27
32
  :whitelist_all_exceptions,
28
33
  :always_include_to_one_linkage_data,
@@ -30,9 +35,11 @@ module JSONAPI
30
35
  :cache_formatters,
31
36
  :use_relationship_reflection,
32
37
  :resource_cache,
38
+ :default_caching,
33
39
  :default_resource_cache_field,
34
40
  :resource_cache_digest_function,
35
- :resource_cache_usage_report_function
41
+ :resource_cache_usage_report_function,
42
+ :default_exclude_links
36
43
 
37
44
  def initialize
38
45
  #:underscored_key, :camelized_key, :dasherized_key, or custom
@@ -45,12 +52,17 @@ module JSONAPI
45
52
  self.resource_key_type = :integer
46
53
 
47
54
  # optional request features
48
- self.allow_include = true
55
+ self.default_allow_include_to_one = true
56
+ self.default_allow_include_to_many = true
49
57
  self.allow_sort = true
50
58
  self.allow_filter = true
51
59
 
52
60
  self.raise_if_parameters_not_allowed = true
53
61
 
62
+ self.warn_on_route_setup_issues = true
63
+ self.warn_on_missing_routes = true
64
+ self.warn_on_performance_issues = true
65
+
54
66
  # :none, :offset, :paged, or a custom paginator name
55
67
  self.default_paginator = :none
56
68
 
@@ -71,8 +83,12 @@ module JSONAPI
71
83
  self.use_text_errors = false
72
84
 
73
85
  # Whether or not to include exception backtraces in JSONAPI error
74
- # responses. Defaults to `false` in production, and `true` otherwise.
75
- self.include_backtraces_in_errors = !Rails.env.production?
86
+ # responses. Defaults to `false` in anything other than development or test.
87
+ self.include_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
88
+
89
+ # Whether or not to include exception application backtraces in JSONAPI error
90
+ # responses. Defaults to `false` in anything other than development or test.
91
+ self.include_application_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
76
92
 
77
93
  # List of classes that should not be rescued by the operations processor.
78
94
  # For example, if you use Pundit for authorization, you might
@@ -94,7 +110,7 @@ module JSONAPI
94
110
 
95
111
  # The default Operation Processor to use if one is not defined specifically
96
112
  # for a Resource.
97
- self.default_processor_klass = JSONAPI::Processor
113
+ self.default_processor_klass_name = 'JSONAPI::Processor'
98
114
 
99
115
  # Allows transactions for creating and updating records
100
116
  # Set this to false if your backend does not support transactions (e.g. Mongodb)
@@ -117,6 +133,11 @@ module JSONAPI
117
133
  # Rails cache store.
118
134
  self.resource_cache = nil
119
135
 
136
+ # Cache resources by default
137
+ # Cache resources by default. Individual resources can be excluded from caching by calling:
138
+ # `caching false`
139
+ self.default_caching = false
140
+
120
141
  # Default resource cache field
121
142
  # On Resources with caching enabled, this field will be used to check for out-of-date
122
143
  # cache entries, unless overridden on a specific Resource. Defaults to "updated_at".
@@ -131,6 +152,12 @@ module JSONAPI
131
152
  # Optionally provide a callable which JSONAPI will call with information about cache
132
153
  # performance. Should accept three arguments: resource name, hits count, misses count.
133
154
  self.resource_cache_usage_report_function = nil
155
+
156
+ # Global configuration for links exclusion
157
+ # Controls whether to generate links like `self`, `related` with all the resources
158
+ # and relationships. Accepts either `:default`, `:none`, or array containing the
159
+ # specific default links to exclude, which may be `:self` and `:related`.
160
+ self.default_exclude_links = :none
134
161
  end
135
162
 
136
163
  def cache_formatters=(bool)
@@ -198,10 +225,26 @@ module JSONAPI
198
225
  end
199
226
 
200
227
  def default_processor_klass=(default_processor_klass)
228
+ ActiveSupport::Deprecation.warn('`default_processor_klass` has been replaced by `default_processor_klass_name`.')
201
229
  @default_processor_klass = default_processor_klass
202
230
  end
203
231
 
204
- attr_writer :allow_include, :allow_sort, :allow_filter
232
+ def default_processor_klass
233
+ @default_processor_klass ||= default_processor_klass_name.safe_constantize
234
+ end
235
+
236
+ def default_processor_klass_name=(default_processor_klass_name)
237
+ @default_processor_klass = nil
238
+ @default_processor_klass_name = default_processor_klass_name
239
+ end
240
+
241
+ def allow_include=(allow_include)
242
+ ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
243
+ @default_allow_include_to_one = allow_include
244
+ @default_allow_include_to_many = allow_include
245
+ end
246
+
247
+ attr_writer :allow_sort, :allow_filter, :default_allow_include_to_one, :default_allow_include_to_many
205
248
 
206
249
  attr_writer :default_paginator
207
250
 
@@ -225,6 +268,8 @@ module JSONAPI
225
268
 
226
269
  attr_writer :include_backtraces_in_errors
227
270
 
271
+ attr_writer :include_application_backtraces_in_errors
272
+
228
273
  attr_writer :exception_class_whitelist
229
274
 
230
275
  attr_writer :whitelist_all_exceptions
@@ -235,15 +280,25 @@ module JSONAPI
235
280
 
236
281
  attr_writer :raise_if_parameters_not_allowed
237
282
 
283
+ attr_writer :warn_on_route_setup_issues
284
+
285
+ attr_writer :warn_on_missing_routes
286
+
287
+ attr_writer :warn_on_performance_issues
288
+
238
289
  attr_writer :use_relationship_reflection
239
290
 
240
291
  attr_writer :resource_cache
241
292
 
293
+ attr_writer :default_caching
294
+
242
295
  attr_writer :default_resource_cache_field
243
296
 
244
297
  attr_writer :resource_cache_digest_function
245
298
 
246
299
  attr_writer :resource_cache_usage_report_function
300
+
301
+ attr_writer :default_exclude_links
247
302
  end
248
303
 
249
304
  class << self
data/lib/jsonapi/error.rb CHANGED
@@ -24,6 +24,33 @@ module JSONAPI
24
24
  instance_variables.each {|var| hash[var.to_s.delete('@')] = instance_variable_get(var) unless instance_variable_get(var).nil? }
25
25
  hash
26
26
  end
27
+
28
+ def update_with_overrides(error_object_overrides)
29
+ @title = error_object_overrides[:title] || @title
30
+ @detail = error_object_overrides[:detail] || @detail
31
+ @id = error_object_overrides[:id] || @id
32
+ @href = error_object_overrides[:href] || href
33
+
34
+ if error_object_overrides[:code]
35
+ # :nocov:
36
+ @code = if JSONAPI.configuration.use_text_errors
37
+ TEXT_ERRORS[error_object_overrides[:code]]
38
+ else
39
+ error_object_overrides[:code]
40
+ end
41
+ # :nocov:
42
+ end
43
+
44
+ @source = error_object_overrides[:source] || @source
45
+ @links = error_object_overrides[:links] || @links
46
+
47
+ if error_object_overrides[:status]
48
+ # :nocov:
49
+ @status = Rack::Utils::SYMBOL_TO_STATUS_CODE[error_object_overrides[:status]].to_s
50
+ # :nocov:
51
+ end
52
+ @meta = error_object_overrides[:meta] || @meta
53
+ end
27
54
  end
28
55
 
29
56
  class Warning
@@ -20,6 +20,7 @@ module JSONAPI
20
20
  INVALID_FILTERS_SYNTAX = '120'
21
21
  SAVE_FAILED = '121'
22
22
  INVALID_DATA_FORMAT = '122'
23
+ INVALID_RELATIONSHIP = '123'
23
24
  BAD_REQUEST = '400'
24
25
  FORBIDDEN = '403'
25
26
  RECORD_NOT_FOUND = '404'
@@ -50,6 +51,7 @@ module JSONAPI
50
51
  INVALID_FILTERS_SYNTAX => 'INVALID_FILTERS_SYNTAX',
51
52
  SAVE_FAILED => 'SAVE_FAILED',
52
53
  INVALID_DATA_FORMAT => 'INVALID_DATA_FORMAT',
54
+ INVALID_RELATIONSHIP => 'INVALID_RELATIONSHIP',
53
55
  FORBIDDEN => 'FORBIDDEN',
54
56
  RECORD_NOT_FOUND => 'RECORD_NOT_FOUND',
55
57
  NOT_ACCEPTABLE => 'NOT_ACCEPTABLE',
@@ -1,7 +1,7 @@
1
1
  module JSONAPI
2
2
  module Exceptions
3
3
  class Error < RuntimeError
4
- attr :error_object_overrides
4
+ attr_reader :error_object_overrides
5
5
 
6
6
  def initialize(error_object_overrides = {})
7
7
  @error_object_overrides = error_object_overrides
@@ -18,6 +18,22 @@ module JSONAPI
18
18
  end
19
19
  end
20
20
 
21
+ class Errors < Error
22
+ def initialize(errors, error_object_overrides = {})
23
+ @errors = errors
24
+
25
+ @errors.each do |error|
26
+ error.update_with_overrides(error_object_overrides)
27
+ end
28
+
29
+ super(error_object_overrides)
30
+ end
31
+
32
+ def errors
33
+ @errors
34
+ end
35
+ end
36
+
21
37
  class InternalServerError < Error
22
38
  attr_accessor :exception
23
39
 
@@ -33,6 +49,12 @@ module JSONAPI
33
49
  meta[:backtrace] = exception.backtrace
34
50
  end
35
51
 
52
+ if JSONAPI.configuration.include_application_backtraces_in_errors
53
+ meta ||= Hash.new
54
+ meta[:exception] ||= exception.message
55
+ meta[:application_backtrace] = exception.backtrace.select{|line| line =~ /#{Rails.root}/}
56
+ end
57
+
36
58
  [create_error_object(code: JSONAPI::INTERNAL_SERVER_ERROR,
37
59
  status: :internal_server_error,
38
60
  title: I18n.t('jsonapi-resources.exceptions.internal_server_error.title',
@@ -119,49 +141,30 @@ module JSONAPI
119
141
  end
120
142
  end
121
143
 
122
-
123
- class HasManyRelationExists < Error
124
- attr_accessor :id
125
-
126
- def initialize(id, error_object_overrides = {})
127
- @id = id
128
- super(error_object_overrides)
129
- end
130
-
131
- def errors
132
- [create_error_object(code: JSONAPI::RELATION_EXISTS,
133
- status: :bad_request,
134
- title: I18n.translate('jsonapi-resources.exceptions.has_many_relation.title',
135
- default: 'Relation exists'),
136
- detail: I18n.translate('jsonapi-resources.exceptions.has_many_relation.detail',
137
- default: "The relation to #{id} already exists.",
138
- id: id))]
139
- end
140
- end
141
-
142
144
  class BadRequest < Error
143
- def initialize(exception)
145
+ def initialize(exception, error_object_overrides = {})
144
146
  @exception = exception
147
+ super(error_object_overrides)
145
148
  end
146
149
 
147
150
  def errors
148
- [JSONAPI::Error.new(code: JSONAPI::BAD_REQUEST,
149
- status: :bad_request,
150
- title: I18n.translate('jsonapi-resources.exceptions.bad_request.title',
151
- default: 'Bad Request'),
152
- detail: I18n.translate('jsonapi-resources.exceptions.bad_request.detail',
153
- default: @exception))]
151
+ [create_error_object(code: JSONAPI::BAD_REQUEST,
152
+ status: :bad_request,
153
+ title: I18n.translate('jsonapi-resources.exceptions.bad_request.title',
154
+ default: 'Bad Request'),
155
+ detail: I18n.translate('jsonapi-resources.exceptions.bad_request.detail',
156
+ default: @exception))]
154
157
  end
155
158
  end
156
159
 
157
160
  class InvalidRequestFormat < Error
158
161
  def errors
159
- [JSONAPI::Error.new(code: JSONAPI::BAD_REQUEST,
160
- status: :bad_request,
161
- title: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.title',
162
- default: 'Bad Request'),
163
- detail: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.detail',
164
- default: 'Request must be a hash'))]
162
+ [create_error_object(code: JSONAPI::BAD_REQUEST,
163
+ status: :bad_request,
164
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.title',
165
+ default: 'Bad Request'),
166
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.detail',
167
+ default: 'Request must be a hash'))]
165
168
  end
166
169
  end
167
170
 
@@ -324,6 +327,26 @@ module JSONAPI
324
327
  end
325
328
  end
326
329
 
330
+ class InvalidRelationship < Error
331
+ attr_accessor :relationship_name, :type
332
+
333
+ def initialize(type, relationship_name, error_object_overrides = {})
334
+ @relationship_name = relationship_name
335
+ @type = type
336
+ super(error_object_overrides)
337
+ end
338
+
339
+ def errors
340
+ [create_error_object(code: JSONAPI::INVALID_RELATIONSHIP,
341
+ status: :bad_request,
342
+ title: I18n.translate('jsonapi-resources.exceptions.invalid_relationship.title',
343
+ default: 'Invalid relationship'),
344
+ detail: I18n.translate('jsonapi-resources.exceptions.invalid_relationship.detail',
345
+ default: "#{relationship_name} is not a valid field for #{type}.",
346
+ relationship_name: relationship_name, type: type))]
347
+ end
348
+ end
349
+
327
350
  class InvalidInclude < Error
328
351
  attr_accessor :relationship, :resource
329
352
 
@@ -339,7 +362,7 @@ module JSONAPI
339
362
  title: I18n.translate('jsonapi-resources.exceptions.invalid_include.title',
340
363
  default: 'Invalid field'),
341
364
  detail: I18n.translate('jsonapi-resources.exceptions.invalid_include.detail',
342
- default: "#{relationship} is not a valid relationship of #{resource}",
365
+ default: "#{relationship} is not a valid includable relationship of #{resource}",
343
366
  relationship: relationship, resource: resource))]
344
367
  end
345
368
  end
@@ -374,11 +397,11 @@ module JSONAPI
374
397
 
375
398
  def errors
376
399
  [create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
377
- status: :bad_request,
378
- title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
379
- default: 'Param not allowed'),
380
- detail: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.detail',
381
- default: "#{param} is not allowed.", param: param))]
400
+ status: :bad_request,
401
+ title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
402
+ default: 'Param not allowed'),
403
+ detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
404
+ default: "#{param} is not allowed.", param: param))]
382
405
  end
383
406
  end
384
407
 
@@ -108,7 +108,7 @@ end
108
108
 
109
109
  class DasherizedKeyFormatter < JSONAPI::KeyFormatter
110
110
  class << self
111
- def format(key)
111
+ def format(_key)
112
112
  super.underscore.dasherize
113
113
  end
114
114
 
@@ -146,7 +146,7 @@ end
146
146
 
147
147
  class CamelizedRouteFormatter < JSONAPI::RouteFormatter
148
148
  class << self
149
- def format(route)
149
+ def format(_route)
150
150
  super.camelize(:lower)
151
151
  end
152
152
 
@@ -158,7 +158,7 @@ end
158
158
 
159
159
  class DasherizedRouteFormatter < JSONAPI::RouteFormatter
160
160
  class << self
161
- def format(route)
161
+ def format(_route)
162
162
  super.dasherize
163
163
  end
164
164
 
@@ -4,14 +4,12 @@ module JSONAPI
4
4
  # For example ['posts.comments.tags']
5
5
  # will transform into =>
6
6
  # {
7
- # posts:{
8
- # include:true,
9
- # include_related:{
7
+ # posts: {
8
+ # include_related: {
10
9
  # comments:{
11
- # include:true,
12
- # include_related:{
13
- # tags:{
14
- # include:true
10
+ # include_related: {
11
+ # tags: {
12
+ # include_related: {}
15
13
  # }
16
14
  # }
17
15
  # }
@@ -19,9 +17,8 @@ module JSONAPI
19
17
  # }
20
18
  # }
21
19
 
22
- def initialize(resource_klass, includes_array, force_eager_load: false)
20
+ def initialize(resource_klass, includes_array)
23
21
  @resource_klass = resource_klass
24
- @force_eager_load = force_eager_load
25
22
  @include_directives_hash = { include_related: {} }
26
23
  includes_array.each do |include|
27
24
  parse_include(include)
@@ -32,69 +29,25 @@ module JSONAPI
32
29
  @include_directives_hash
33
30
  end
34
31
 
35
- def model_includes
36
- get_includes(@include_directives_hash)
37
- end
38
-
39
- def paths
40
- delve_paths(get_includes(@include_directives_hash, false))
41
- end
42
-
43
32
  private
44
33
 
45
- def get_related(current_path)
46
- current = @include_directives_hash
47
- current_resource_klass = @resource_klass
48
- current_path.split('.').each do |fragment|
49
- fragment = fragment.to_sym
50
-
51
- if current_resource_klass
52
- current_relationship = current_resource_klass._relationships[fragment]
53
- current_resource_klass = current_relationship.try(:resource_klass)
54
- else
55
- raise JSONAPI::Exceptions::InvalidInclude.new(current_resource_klass, current_path)
56
- end
57
-
58
- include_in_join = @force_eager_load || !current_relationship || current_relationship.eager_load_on_include
59
-
60
- current[:include_related][fragment] ||= { include: false, include_related: {}, include_in_join: include_in_join }
61
- current = current[:include_related][fragment]
62
- end
63
- current
64
- end
65
-
66
- def get_includes(directive, only_joined_includes = true)
67
- ir = directive[:include_related]
68
- ir = ir.select { |k,v| v[:include_in_join] } if only_joined_includes
34
+ def parse_include(include)
35
+ path = JSONAPI::Path.new(resource_klass: @resource_klass,
36
+ path_string: include,
37
+ ensure_default_field: false,
38
+ parse_fields: false)
69
39
 
70
- ir.map do |name, sub_directive|
71
- sub = get_includes(sub_directive, only_joined_includes)
72
- sub.any? ? { name => sub } : name
73
- end
74
- end
40
+ current = @include_directives_hash
75
41
 
76
- def parse_include(include)
77
- parts = include.split('.')
78
- local_path = ''
42
+ path.segments.each do |segment|
43
+ relationship_name = segment.relationship.name.to_sym
79
44
 
80
- parts.each do |name|
81
- local_path += local_path.length > 0 ? ".#{name}" : name
82
- related = get_related(local_path)
83
- related[:include] = true
45
+ current[:include_related][relationship_name] ||= { include_related: {} }
46
+ current = current[:include_related][relationship_name]
84
47
  end
85
- end
86
48
 
87
- def delve_paths(obj)
88
- case obj
89
- when Array
90
- obj.map{|elem| delve_paths(elem)}.flatten(1)
91
- when Hash
92
- obj.map{|k,v| [[k]] + delve_paths(v).map{|path| [k] + path } }.flatten(1)
93
- when Symbol, String
94
- [[obj]]
95
- else
96
- raise "delve_paths cannot descend into #{obj.class.name}"
97
- end
49
+ rescue JSONAPI::Exceptions::InvalidRelationship => _e
50
+ raise JSONAPI::Exceptions::InvalidInclude.new(@resource_klass, include)
98
51
  end
99
52
  end
100
53
  end