jpie 0.1.0 → 0.3.0

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.aiconfig +65 -0
  3. data/.rubocop.yml +110 -35
  4. data/CHANGELOG.md +93 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +776 -1903
  7. data/Rakefile +14 -3
  8. data/jpie.gemspec +35 -18
  9. data/lib/jpie/configuration.rb +12 -0
  10. data/lib/jpie/controller/crud_actions.rb +110 -0
  11. data/lib/jpie/controller/error_handling.rb +41 -0
  12. data/lib/jpie/controller/parameter_parsing.rb +35 -0
  13. data/lib/jpie/controller/rendering.rb +60 -0
  14. data/lib/jpie/controller.rb +18 -0
  15. data/lib/jpie/deserializer.rb +110 -0
  16. data/lib/jpie/errors.rb +70 -0
  17. data/lib/jpie/generators/resource_generator.rb +39 -0
  18. data/lib/jpie/generators/templates/resource.rb.erb +12 -0
  19. data/lib/jpie/railtie.rb +36 -0
  20. data/lib/jpie/resource/attributable.rb +98 -0
  21. data/lib/jpie/resource/inferrable.rb +43 -0
  22. data/lib/jpie/resource/sortable.rb +93 -0
  23. data/lib/jpie/resource.rb +107 -0
  24. data/lib/jpie/serializer.rb +205 -0
  25. data/lib/{json_api → jpie}/version.rb +2 -2
  26. data/lib/jpie.rb +23 -3
  27. metadata +145 -50
  28. data/.gitignore +0 -21
  29. data/.rspec +0 -3
  30. data/.travis.yml +0 -7
  31. data/Gemfile +0 -21
  32. data/Gemfile.lock +0 -312
  33. data/bin/console +0 -15
  34. data/bin/setup +0 -8
  35. data/kiln/app/resources/user_message_resource.rb +0 -2
  36. data/lib/json_api/active_storage/deserialization.rb +0 -106
  37. data/lib/json_api/active_storage/detection.rb +0 -74
  38. data/lib/json_api/active_storage/serialization.rb +0 -32
  39. data/lib/json_api/configuration.rb +0 -58
  40. data/lib/json_api/controllers/base_controller.rb +0 -26
  41. data/lib/json_api/controllers/concerns/controller_helpers.rb +0 -223
  42. data/lib/json_api/controllers/concerns/resource_actions.rb +0 -657
  43. data/lib/json_api/controllers/relationships_controller.rb +0 -504
  44. data/lib/json_api/controllers/resources_controller.rb +0 -6
  45. data/lib/json_api/errors/parameter_not_allowed.rb +0 -19
  46. data/lib/json_api/railtie.rb +0 -75
  47. data/lib/json_api/resources/active_storage_blob_resource.rb +0 -11
  48. data/lib/json_api/resources/resource.rb +0 -238
  49. data/lib/json_api/resources/resource_loader.rb +0 -35
  50. data/lib/json_api/routing.rb +0 -72
  51. data/lib/json_api/serialization/deserializer.rb +0 -362
  52. data/lib/json_api/serialization/serializer.rb +0 -320
  53. data/lib/json_api/support/active_storage_support.rb +0 -85
  54. data/lib/json_api/support/collection_query.rb +0 -406
  55. data/lib/json_api/support/instrumentation.rb +0 -42
  56. data/lib/json_api/support/param_helpers.rb +0 -51
  57. data/lib/json_api/support/relationship_guard.rb +0 -16
  58. data/lib/json_api/support/relationship_helpers.rb +0 -74
  59. data/lib/json_api/support/resource_identifier.rb +0 -87
  60. data/lib/json_api/support/responders.rb +0 -100
  61. data/lib/json_api/support/response_helpers.rb +0 -10
  62. data/lib/json_api/support/sort_parsing.rb +0 -21
  63. data/lib/json_api/support/type_conversion.rb +0 -21
  64. data/lib/json_api/testing/test_helper.rb +0 -76
  65. data/lib/json_api/testing.rb +0 -3
  66. data/lib/json_api.rb +0 -50
  67. data/lib/rubocop/cop/custom/hash_value_omission.rb +0 -53
@@ -1,504 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json_api/support/relationship_guard"
4
-
5
- module JSONAPI
6
- class RelationshipsController < BaseController
7
- skip_before_action :validate_resource_type!, only: %i[update destroy]
8
- skip_before_action :validate_resource_id!, only: %i[update destroy]
9
-
10
- before_action :set_resource_name
11
- before_action :set_resource_class
12
- before_action :set_resource
13
- before_action :set_relationship_name
14
- before_action :validate_relationship_exists
15
- before_action :validate_sort_param, only: [:show]
16
- skip_before_action :validate_resource_type!, :validate_resource_id!
17
-
18
- def show
19
- authorize_resource_action!(@resource, action: :show, context: { relationship: @relationship_name })
20
- relationship_data = serialize_relationship_data
21
- links = serialize_relationship_links
22
- meta = serialize_relationship_meta
23
-
24
- response_data = { data: relationship_data, links: }
25
- response_data[:meta] = meta if meta.present?
26
-
27
- render json: response_data, status: :ok
28
- end
29
-
30
- def update
31
- authorize_resource_action!(@resource, action: :update, context: { relationship: @relationship_name })
32
- relationship_data = parse_relationship_data
33
- update_relationship(relationship_data)
34
-
35
- if @resource.save
36
- emit_relationship_event(:updated, relationship_data)
37
- render json: {
38
- data: serialize_relationship_data,
39
- links: serialize_relationship_links,
40
- meta: serialize_relationship_meta
41
- }.compact, status: :ok
42
- else
43
- render_validation_errors(@resource)
44
- end
45
- rescue ArgumentError => e
46
- render_invalid_relationship_error(e)
47
- rescue JSONAPI::Exceptions::ParameterNotAllowed => e
48
- render_parameter_not_allowed_error(e)
49
- end
50
-
51
- def destroy
52
- authorize_resource_action!(@resource, action: :update, context: { relationship: @relationship_name })
53
- relationship_data = parse_relationship_data
54
- return head :no_content if relationship_data.nil?
55
-
56
- remove_relationship(relationship_data)
57
-
58
- # For to-many relationships, resources are updated directly in remove_from_many_relationship
59
- # For to-one relationships, the parent resource needs to be saved
60
- association = @resource.class.reflect_on_association(@relationship_name)
61
- if association && !association.collection?
62
- if @resource.save
63
- emit_relationship_event(:removed, relationship_data)
64
- head :no_content
65
- else
66
- render_validation_errors(@resource)
67
- end
68
- else
69
- emit_relationship_event(:removed, relationship_data)
70
- head :no_content
71
- end
72
- rescue ArgumentError => e
73
- render_invalid_relationship_error(e)
74
- rescue JSONAPI::Exceptions::ParameterNotAllowed => e
75
- render_parameter_not_allowed_error(e)
76
- end
77
-
78
- private
79
-
80
- def set_relationship_name
81
- @relationship_name = params[:relationship_name].to_sym
82
- end
83
-
84
- def validate_relationship_exists
85
- relationship_def = find_relationship_definition
86
- return if relationship_def
87
-
88
- render_jsonapi_error(
89
- status: 404,
90
- title: "Relationship Not Found",
91
- detail: "Relationship '#{@relationship_name}' not found on #{@resource_name}"
92
- ) and return
93
- end
94
-
95
- def find_relationship_definition
96
- RelationshipHelpers.find_relationship_definition(@resource_class, @relationship_name)
97
- end
98
-
99
- def serialize_relationship_data
100
- association = @resource.class.reflect_on_association(@relationship_name)
101
- return nil unless association
102
-
103
- related = @resource.public_send(@relationship_name)
104
-
105
- # Apply sorting for collection relationships
106
- if association.collection? && related.respond_to?(:order)
107
- related = apply_sorting_to_relationship(related, association)
108
- end
109
-
110
- if association.collection?
111
- serialize_collection_relationship(related, association)
112
- elsif related
113
- serialize_single_relationship(related, association)
114
- end
115
- end
116
-
117
- def serialize_collection_relationship(related, association)
118
- return [] if related.nil?
119
-
120
- related.map { |r| serialize_resource_identifier(r, association) }
121
- end
122
-
123
- def serialize_single_relationship(related, association)
124
- serialize_resource_identifier(related, association)
125
- end
126
-
127
- def serialize_resource_identifier(resource_instance, association)
128
- RelationshipHelpers.serialize_resource_identifier(
129
- resource_instance,
130
- association:,
131
- resource_class: @resource_class
132
- )
133
- end
134
-
135
- def serialize_relationship_links
136
- {
137
- self: relationship_self_url,
138
- related: relationship_related_url
139
- }
140
- end
141
-
142
- def relationship_self_url
143
- "/#{params[:resource_type]}/#{params[:id]}/relationships/#{params[:relationship_name]}"
144
- end
145
-
146
- def relationship_related_url
147
- "/#{params[:resource_type]}/#{params[:id]}/#{params[:relationship_name]}"
148
- end
149
-
150
- def serialize_relationship_meta
151
- relationship_def = find_relationship_definition
152
- return nil unless relationship_def
153
-
154
- relationship_def[:meta]
155
- end
156
-
157
- def parse_relationship_data
158
- raw_data = params[:data]
159
- return nil if raw_data.nil?
160
-
161
- # Handle arrays directly (for to-many relationships)
162
- return [] if raw_data.is_a?(Array) && raw_data.empty?
163
-
164
- return raw_data.map { |item| ParamHelpers.deep_symbolize_params(item) } if raw_data.is_a?(Array)
165
-
166
- # Handle hash/object (for to-one relationships or single resource identifiers)
167
- ParamHelpers.deep_symbolize_params(raw_data)
168
- end
169
-
170
- def update_relationship(relationship_data)
171
- association = @resource.class.reflect_on_association(@relationship_name)
172
- raise ArgumentError, "Association not found: #{@relationship_name}" unless association
173
-
174
- ensure_relationship_writable!(association)
175
-
176
- if association.collection?
177
- update_to_many_relationship(association, relationship_data)
178
- else
179
- update_to_one_relationship(association, relationship_data)
180
- end
181
- end
182
-
183
- def update_to_many_relationship(association, relationship_data)
184
- if relationship_data.nil? || (relationship_data.is_a?(Array) && relationship_data.empty?)
185
- @resource.public_send("#{@relationship_name}=", [])
186
- return
187
- end
188
-
189
- raise ArgumentError, "Expected array for to-many relationship" unless relationship_data.is_a?(Array)
190
-
191
- related_resources = relationship_data.map do |identifier|
192
- find_related_resource(identifier, association)
193
- end
194
-
195
- @resource.public_send("#{@relationship_name}=", related_resources)
196
- end
197
-
198
- def update_to_one_relationship(association, relationship_data)
199
- if relationship_data.nil?
200
- @resource.public_send("#{@relationship_name}=", nil)
201
- return
202
- end
203
-
204
- if relationship_data.is_a?(Array)
205
- raise ArgumentError, "Expected single resource identifier for to-one relationship"
206
- end
207
-
208
- related_resource = find_related_resource(relationship_data, association)
209
- @resource.public_send("#{@relationship_name}=", related_resource)
210
- end
211
-
212
- def find_related_resource(identifier, association)
213
- RelationshipHelpers.resolve_and_find_related_resource(
214
- identifier,
215
- association:,
216
- resource_class: @resource_class,
217
- relationship_name: @relationship_name
218
- )
219
- end
220
-
221
- def polymorphic_association?
222
- RelationshipHelpers.polymorphic_association?(@resource_class, @relationship_name)
223
- end
224
-
225
- def remove_relationship(relationship_data)
226
- # Check if this is an ActiveStorage attachment first
227
- if active_storage_attachment?(@relationship_name, @resource.class)
228
- # For ActiveStorage, we need to determine if it's has_many or has_one
229
- attachment_reflection = @resource.class.reflect_on_attachment(@relationship_name)
230
- if attachment_reflection&.macro == :has_many_attached
231
- remove_from_many_relationship(nil, relationship_data)
232
- else
233
- remove_from_one_relationship(nil, relationship_data)
234
- end
235
- return
236
- end
237
-
238
- association = @resource.class.reflect_on_association(@relationship_name)
239
- raise ArgumentError, "Association not found: #{@relationship_name}" unless association
240
-
241
- ensure_relationship_writable!(association)
242
-
243
- if association.collection?
244
- remove_from_many_relationship(association, relationship_data)
245
- else
246
- remove_from_one_relationship(association, relationship_data)
247
- end
248
- rescue ActiveRecord::NotNullViolation, ActiveRecord::RecordInvalid => e
249
- raise ArgumentError, "Cannot remove relationship: #{e.message}"
250
- end
251
-
252
- def remove_from_many_relationship(association, relationship_data)
253
- raise ArgumentError, "Expected array for to-many relationship removal" unless relationship_data.is_a?(Array)
254
-
255
- return if relationship_data.empty?
256
-
257
- # Check if this is an ActiveStorage attachment
258
- if active_storage_attachment?(@relationship_name, @resource.class)
259
- remove_active_storage_attachments(relationship_data)
260
- return
261
- end
262
-
263
- collection = @resource.public_send(@relationship_name)
264
- collection_ids = collection.pluck(:id)
265
- foreign_key = association.foreign_key
266
-
267
- ActiveRecord::Base.transaction do
268
- relationship_data.each do |identifier|
269
- resource = RelationshipHelpers.resolve_and_find_related_resource(
270
- identifier,
271
- association:,
272
- resource_class: @resource_class,
273
- relationship_name: @relationship_name
274
- )
275
- resource_id = resource.id
276
-
277
- next unless collection_ids.include?(resource_id)
278
-
279
- resource.update!(foreign_key => nil)
280
- end
281
- end
282
- end
283
-
284
- def remove_active_storage_attachments(relationship_data)
285
- attachment_proxy = @resource.public_send(@relationship_name)
286
- return unless attachment_proxy.attached?
287
-
288
- blob_ids = relationship_data.map do |identifier|
289
- type = RelationshipHelpers.extract_type_from_identifier(identifier)
290
- id = RelationshipHelpers.extract_id_from_identifier(identifier)
291
-
292
- unless self.class.active_storage_blob_type?(type)
293
- raise ArgumentError, "Expected active_storage_blobs type for ActiveStorage attachment removal, got #{type}"
294
- end
295
-
296
- id.to_i
297
- end
298
-
299
- # Find attachments that link these blobs to the resource
300
- # For has_many_attached, we need to access the underlying attachments
301
- attachments_to_remove = attachment_proxy.attachments.where(blob_id: blob_ids)
302
-
303
- # Purge/detach the attachments
304
- attachments_to_remove.each(&:purge)
305
- end
306
-
307
- def remove_from_one_relationship(association, relationship_data)
308
- if relationship_data.is_a?(Array)
309
- raise ArgumentError, "Expected single resource identifier for to-one relationship"
310
- end
311
-
312
- return if relationship_data.nil?
313
-
314
- # Check if this is an ActiveStorage attachment
315
- if active_storage_attachment?(@relationship_name, @resource.class)
316
- remove_active_storage_attachment(relationship_data)
317
- return
318
- end
319
-
320
- related_resource = find_related_resource(relationship_data, association)
321
- current_resource = @resource.public_send(@relationship_name)
322
-
323
- return unless current_resource == related_resource
324
-
325
- @resource.public_send("#{@relationship_name}=", nil)
326
- @resource.save!
327
- end
328
-
329
- def remove_active_storage_attachment(relationship_data)
330
- attachment_proxy = @resource.public_send(@relationship_name)
331
- return unless attachment_proxy.attached?
332
-
333
- type = RelationshipHelpers.extract_type_from_identifier(relationship_data)
334
- id = RelationshipHelpers.extract_id_from_identifier(relationship_data)
335
-
336
- unless self.class.active_storage_blob_type?(type)
337
- raise ArgumentError, "Expected active_storage_blobs type for ActiveStorage attachment removal, got #{type}"
338
- end
339
-
340
- blob_id = id.to_i
341
-
342
- # Find the attachment that links this blob to the resource
343
- attachment = attachment_proxy.attachments.find_by(blob_id:)
344
-
345
- # Purge/detach the attachment
346
- attachment&.purge
347
- end
348
-
349
- def apply_sorting_to_relationship(related, association)
350
- sorts = parse_sort_param
351
- return related if sorts.empty?
352
-
353
- related_model = association.klass
354
- related_resource_class = ResourceLoader.find_for_model(related_model)
355
-
356
- # Separate database columns from virtual attributes
357
- db_sorts = []
358
- virtual_sorts = []
359
-
360
- sorts.each do |sort_field|
361
- field = RelationshipHelpers.extract_sort_field_name(sort_field)
362
- if related_model.column_names.include?(field.to_s)
363
- db_sorts << sort_field
364
- else
365
- virtual_sorts << sort_field
366
- end
367
- end
368
-
369
- # Apply database sorting first
370
- db_sorts.each do |sort_field|
371
- direction = RelationshipHelpers.extract_sort_direction(sort_field)
372
- field = RelationshipHelpers.extract_sort_field_name(sort_field)
373
- related = related.order(field => direction)
374
- end
375
-
376
- # Apply virtual attribute sorting in Ruby if needed
377
- if virtual_sorts.any?
378
- records = related.to_a
379
- records = apply_virtual_attribute_sorting_to_relationship(records, virtual_sorts, related_resource_class)
380
- related = records
381
- end
382
-
383
- related
384
- end
385
-
386
- def apply_virtual_attribute_sorting_to_relationship(records, virtual_sorts, resource_class)
387
- records.sort do |a, b|
388
- compare_records_by_virtual_sorts_for_relationship(a, b, virtual_sorts, resource_class)
389
- end
390
- end
391
-
392
- def compare_records_by_virtual_sorts_for_relationship(record_a, record_b, virtual_sorts, resource_class)
393
- virtual_sorts.each do |sort_field|
394
- direction = RelationshipHelpers.extract_sort_direction(sort_field)
395
- field = RelationshipHelpers.extract_sort_field_name(sort_field)
396
-
397
- # Get values using resource getter
398
- resource_instance_a = resource_class.new(record_a, {})
399
- resource_instance_b = resource_class.new(record_b, {})
400
-
401
- value_a = get_virtual_attribute_value_for_relationship(resource_instance_a, field)
402
- value_b = get_virtual_attribute_value_for_relationship(resource_instance_b, field)
403
-
404
- # Compare values
405
- comparison = compare_values_for_relationship(value_a, value_b)
406
- next if comparison.zero? # Equal, check next sort field
407
-
408
- # Apply direction
409
- return direction == :desc ? -comparison : comparison
410
- end
411
-
412
- 0 # All fields equal
413
- end
414
-
415
- def get_virtual_attribute_value_for_relationship(resource_instance, field)
416
- field_sym = field.to_sym
417
- return resource_instance.public_send(field_sym) if resource_instance.respond_to?(field_sym, false)
418
-
419
- nil
420
- end
421
-
422
- def compare_values_for_relationship(value_a, value_b)
423
- # Handle nil values
424
- return 0 if value_a.nil? && value_b.nil?
425
- return -1 if value_a.nil?
426
- return 1 if value_b.nil?
427
-
428
- # Compare using standard Ruby comparison
429
- value_a <=> value_b
430
- end
431
-
432
- def validate_sort_param
433
- sorts = parse_sort_param
434
- return if sorts.empty?
435
-
436
- association = @resource.class.reflect_on_association(@relationship_name)
437
- return unless association&.collection?
438
-
439
- # Find resource class for the related model to check virtual attributes
440
- related_resource_class = ResourceLoader.find_for_model(association.klass)
441
- valid_fields = valid_sort_fields_for_resource(related_resource_class, association.klass)
442
- invalid_fields = invalid_sort_fields_for_columns(sorts, valid_fields)
443
- return if invalid_fields.empty?
444
-
445
- render_parameter_errors(
446
- invalid_fields,
447
- title: "Invalid Sort Field",
448
- detail_proc: ->(field) { "Invalid sort field requested: #{field}" },
449
- source_proc: ->(_field) { { parameter: "sort" } }
450
- )
451
- end
452
-
453
- def emit_relationship_event(action, relationship_data)
454
- resource_type_name = params[:resource_type] || @resource_name.pluralize
455
-
456
- related_ids = extract_related_ids(relationship_data)
457
- related_type = extract_related_type(relationship_data)
458
-
459
- JSONAPI::Instrumentation.relationship_event(
460
- action:,
461
- resource_type: resource_type_name,
462
- resource_id: @resource.id,
463
- relationship_name: @relationship_name.to_s,
464
- related_ids:,
465
- related_type:
466
- )
467
- end
468
-
469
- def extract_related_ids(relationship_data)
470
- return nil if relationship_data.nil?
471
-
472
- if relationship_data.is_a?(Array)
473
- relationship_data.map { |item| item[:id] }.compact
474
- else
475
- [relationship_data[:id]].compact
476
- end
477
- end
478
-
479
- def extract_related_type(relationship_data)
480
- return nil if relationship_data.nil?
481
-
482
- if relationship_data.is_a?(Array)
483
- first_item = relationship_data.first
484
- return nil unless first_item
485
-
486
- first_item[:type]
487
- else
488
- relationship_data[:type]
489
- end
490
- end
491
-
492
- def ensure_relationship_writable!(association)
493
- if self.class.respond_to?(:active_storage_attachment?) &&
494
- active_storage_attachment?(@relationship_name, @resource.class)
495
- return
496
- end
497
-
498
- relationship_def = find_relationship_definition
499
- readonly = relationship_def && (relationship_def[:options] || {})[:readonly] == true
500
-
501
- JSONAPI::RelationshipGuard.ensure_writable!(association, @relationship_name, readonly:)
502
- end
503
- end
504
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONAPI
4
- class ResourcesController < BaseController
5
- end
6
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONAPI
4
- module Errors
5
- class ParameterNotAllowed < JSONAPI::Error
6
- attr_reader :params
7
-
8
- def initialize(params = [])
9
- @params = params
10
- super("Parameter not allowed: #{Array(params).join(', ')}")
11
- end
12
- end
13
- end
14
-
15
- # Backward compatibility alias
16
- module Exceptions
17
- ParameterNotAllowed = Errors::ParameterNotAllowed
18
- end
19
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails"
4
-
5
- module JSONAPI
6
- class Railtie < Rails::Railtie
7
- config.before_initialize do
8
- ActiveSupport::Inflector.inflections(:en) do |inflect|
9
- inflect.acronym "JSON"
10
- inflect.acronym "API"
11
- # Ensure json_api converts to JSONAPI
12
- end
13
- end
14
-
15
- initializer "json_api.mime_type" do |_app|
16
- Mime::Type.register "application/vnd.api+json", :jsonapi
17
- end
18
-
19
- initializer "json_api.routes" do |_app|
20
- require "json_api/routing"
21
- ActionDispatch::Routing::Mapper.include JSONAPI::Routing
22
- end
23
-
24
- # Removed eager_load_namespaces registration - JSONAPI module doesn't implement eager_load!
25
- # Controllers and resources are autoloaded via Zeitwerk
26
-
27
- initializer "json_api.parameter_parsing", after: "action_dispatch.configure" do |_app|
28
- ActionDispatch::Request.parameter_parsers[:jsonapi] = lambda do |raw_post|
29
- ActiveSupport::JSON.decode(raw_post)
30
- rescue JSON::ParserError => e
31
- raise ActionDispatch::Http::Parameters::ParseError, e.message
32
- end
33
- end
34
-
35
- initializer "json_api.controllers.include_base", before: :add_routing_paths do |_app|
36
- mixin = lambda do |base|
37
- next unless JSONAPI.configuration.base_controller_overridden?
38
-
39
- # Use class name comparison instead of object equality to avoid timing issues
40
- # when ApplicationController loads before JSONAPIController
41
- expected_class_name = JSONAPI.configuration.instance_variable_get(:@base_controller_class)
42
- next unless base.name == expected_class_name
43
-
44
- base.include(JSONAPI::ControllerHelpers)
45
- base.include(JSONAPI::ResourceActions)
46
- end
47
-
48
- ActiveSupport.on_load(:action_controller_base, &mixin)
49
- ActiveSupport.on_load(:action_controller_api, &mixin)
50
- end
51
-
52
- # Inject JSON:API concerns into the configured base controller class only when it's been overridden
53
- # This allows empty controllers inheriting from ApplicationController to work automatically
54
- # We don't mix into ActionController::API by default to avoid Rails filtering action methods
55
- # (Rails treats public methods on abstract base classes as internal methods)
56
- config.to_prepare do
57
- next unless JSONAPI.configuration.base_controller_overridden?
58
-
59
- base_controller_class = JSONAPI.configuration.resolved_base_controller_class
60
-
61
- # Ensure BaseController inherits from the configured base controller
62
- # This is necessary because BaseController may be defined before the configuration is set
63
- if JSONAPI::BaseController.superclass != base_controller_class
64
- JSONAPI.send(:remove_const, :BaseController)
65
- load "json_api/controllers/base_controller.rb"
66
- # Also reload RelationshipsController so it picks up the new BaseController
67
- JSONAPI.send(:remove_const, :RelationshipsController)
68
- load "json_api/controllers/relationships_controller.rb"
69
- end
70
-
71
- base_controller_class.include(JSONAPI::ControllerHelpers)
72
- base_controller_class.include(JSONAPI::ResourceActions)
73
- end
74
- end
75
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONAPI
4
- class ActiveStorageBlobResource < Resource
5
- attributes :filename, :content_type, :byte_size, :checksum
6
-
7
- def self.model_class
8
- ::ActiveStorage::Blob
9
- end
10
- end
11
- end