plutonium 0.18.4 → 0.18.6

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/plutonium.js +1 -1
  3. data/app/assets/plutonium.js.map +2 -2
  4. data/app/assets/plutonium.min.js +1 -1
  5. data/app/assets/plutonium.min.js.map +2 -2
  6. data/app/views/resource/interactive_bulk_action.html.erb +1 -1
  7. data/app/views/resource/interactive_resource_action.html.erb +1 -1
  8. data/lib/generators/pu/eject/layout/layout_generator.rb +1 -2
  9. data/lib/generators/pu/eject/shell/shell_generator.rb +1 -3
  10. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +4 -0
  11. data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +80 -0
  12. data/lib/generators/pu/lib/plutonium_generators/concerns/resource_selector.rb +48 -0
  13. data/lib/generators/pu/lib/plutonium_generators/generator.rb +2 -42
  14. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +1 -4
  15. data/lib/generators/pu/res/conn/conn_generator.rb +9 -21
  16. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +10 -5
  17. data/lib/plutonium/core/controller.rb +2 -1
  18. data/lib/plutonium/resource/controller.rb +21 -7
  19. data/lib/plutonium/resource/controllers/authorizable.rb +8 -2
  20. data/lib/plutonium/resource/controllers/crud_actions.rb +17 -17
  21. data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
  22. data/lib/plutonium/resource/controllers/presentable.rb +2 -2
  23. data/lib/plutonium/resource/record/associated_with.rb +83 -0
  24. data/lib/plutonium/resource/record/associations.rb +92 -0
  25. data/lib/plutonium/resource/record/field_names.rb +83 -0
  26. data/lib/plutonium/resource/record/labeling.rb +19 -0
  27. data/lib/plutonium/resource/record/routes.rb +66 -0
  28. data/lib/plutonium/resource/record.rb +6 -258
  29. data/lib/plutonium/routing/mapper_extensions.rb +2 -2
  30. data/lib/plutonium/routing/route_set_extensions.rb +7 -4
  31. data/lib/plutonium/ui/breadcrumbs.rb +4 -4
  32. data/lib/plutonium/ui/component/methods.rb +4 -12
  33. data/lib/plutonium/ui/form/base.rb +22 -10
  34. data/lib/plutonium/ui/form/components/secure_association.rb +112 -0
  35. data/lib/plutonium/ui/form/components/secure_polymorphic_association.rb +54 -0
  36. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +0 -1
  37. data/lib/plutonium/ui/form/theme.rb +12 -1
  38. data/lib/plutonium/ui/page/show.rb +1 -1
  39. data/lib/plutonium/ui/page_header.rb +1 -1
  40. data/lib/plutonium/version.rb +1 -1
  41. data/package-lock.json +2 -2
  42. data/package.json +1 -1
  43. data/src/js/controllers/slim_select_controller.js +3 -1
  44. metadata +11 -4
  45. data/lib/plutonium/ui/form/components/belongs_to.rb +0 -66
  46. data/lib/plutonium/ui/form/components/has_many.rb +0 -66
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/plutonium/resource/associations.rb
4
+ module Plutonium
5
+ module Resource
6
+ module Record
7
+ module AssociatedWith
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ scope :associated_with, ->(record) do
12
+ named_scope = :"associated_with_#{record.model_name.singular}"
13
+ return send(named_scope, record) if respond_to?(named_scope)
14
+
15
+ own_association = klass.find_association_from_self_to_record(record)
16
+ if own_association
17
+ return klass.query_based_on_association(own_association, record)
18
+ end
19
+
20
+ record_association = klass.find_association_to_self_from_record(record)
21
+ if record_association
22
+ Plutonium.logger.warn do
23
+ [
24
+ "Using indirect association from #{record.class} to #{klass.name}",
25
+ "via '#{record_association.name}'.",
26
+ "This may result in poor query performance for large datasets",
27
+ "as it requires loading records to perform the association.",
28
+ "",
29
+ "Consider defining a direct association or implementing",
30
+ "a custom scope '#{named_scope}' for better performance."
31
+ ].join("\n")
32
+ end
33
+ return where(id: record.public_send(record_association.name))
34
+ end
35
+
36
+ klass.raise_unresolvable_association_error(record, named_scope)
37
+ end
38
+ end
39
+
40
+ class_methods do
41
+ def find_association_from_self_to_record(record)
42
+ reflect_on_all_associations.find do |assoc|
43
+ assoc.klass.name == record.class.name unless assoc.polymorphic?
44
+ rescue
45
+ assoc.check_validity!
46
+ raise
47
+ end
48
+ end
49
+
50
+ def find_association_to_self_from_record(record)
51
+ record.class.reflect_on_all_associations.find do |assoc|
52
+ assoc.klass.name == name
53
+ rescue
54
+ assoc.check_validity!
55
+ raise
56
+ end
57
+ end
58
+
59
+ def query_based_on_association(assoc, record)
60
+ case assoc.macro
61
+ when :has_one
62
+ joins(assoc.name).where(assoc.name => {record.class.primary_key => record.id})
63
+ when :belongs_to
64
+ where(assoc.name => record)
65
+ when :has_many
66
+ joins(assoc.name).where(assoc.klass.table_name => record)
67
+ else
68
+ raise NotImplementedError, "associated_with->##{assoc.macro}"
69
+ end
70
+ end
71
+
72
+ def raise_unresolvable_association_error(record, named_scope)
73
+ raise "Could not resolve the association between '#{name}' and '#{record.class.name}'\n\n" \
74
+ "Define\n" \
75
+ " 1. the associations between the models\n" \
76
+ " 2. a named scope on #{name} e.g.\n\n" \
77
+ "scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/plutonium/resource/associations.rb
4
+ module Plutonium
5
+ module Resource
6
+ module Record
7
+ module Associations
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def belongs_to(name, scope = nil, **options)
12
+ super
13
+ include_secure_association_methods(:belongs_to, name)
14
+ end
15
+
16
+ def has_one(name, scope = nil, **options)
17
+ super
18
+ include_secure_association_methods(:has_one, name)
19
+ end
20
+
21
+ def has_many(name, scope = nil, **options)
22
+ super
23
+ include_secure_association_methods(:has_many, name)
24
+ end
25
+
26
+ def has_and_belongs_to_many(name, scope = nil, **options)
27
+ super
28
+ include_secure_association_methods(:has_and_belongs_to_many, name)
29
+ end
30
+
31
+ private
32
+
33
+ def include_secure_association_methods(association_type, name)
34
+ mod = Module.new
35
+ mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
36
+ extend ActiveSupport::Concern
37
+
38
+ #{generate_sgid_methods(association_type, name)}
39
+
40
+ define_singleton_method(:to_s) { "Plutonium::Resource::Record::Associations::#{association_type.to_s.camelize}(:#{name})" }
41
+ define_singleton_method(:inspect) { "Plutonium::Resource::Record::Associations::#{association_type.to_s.camelize}(:#{name})" }
42
+ RUBY
43
+ include mod
44
+ end
45
+
46
+ def generate_sgid_methods(association_type, name)
47
+ case association_type
48
+ when :belongs_to, :has_one
49
+ generate_singular_sgid_methods(name)
50
+ when :has_many, :has_and_belongs_to_many
51
+ generate_collection_sgid_methods(name)
52
+ end
53
+ end
54
+
55
+ def generate_singular_sgid_methods(name)
56
+ <<-RUBY
57
+ def #{name}_sgid
58
+ #{name}&.to_signed_global_id
59
+ end
60
+
61
+ def #{name}_sgid=(sgid)
62
+ self.#{name} = GlobalID::Locator.locate_signed(sgid)
63
+ end
64
+ RUBY
65
+ end
66
+
67
+ def generate_collection_sgid_methods(name)
68
+ <<-RUBY
69
+ def #{name.to_s.singularize}_sgids
70
+ #{name}.map(&:to_signed_global_id)
71
+ end
72
+
73
+ def #{name.to_s.singularize}_sgids=(sgids)
74
+ self.#{name} = GlobalID::Locator.locate_many_signed(sgids)
75
+ end
76
+
77
+ def add_#{name.to_s.singularize}_sgid(sgid)
78
+ record = GlobalID::Locator.locate_signed(sgid)
79
+ #{name} << record if record
80
+ end
81
+
82
+ def remove_#{name.to_s.singularize}_sgid(sgid)
83
+ record = GlobalID::Locator.locate_signed(sgid)
84
+ #{name}.delete(record) if record
85
+ end
86
+ RUBY
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/plutonium/resource/associations.rb
4
+ module Plutonium
5
+ module Resource
6
+ module Record
7
+ module FieldNames
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def resource_field_names
12
+ return @resource_field_names if defined?(@resource_field_names) && !Rails.env.local?
13
+
14
+ @resource_field_names = gather_resource_field_names
15
+ end
16
+
17
+ def belongs_to_association_field_names
18
+ return @belongs_to_association_field_names if defined?(@belongs_to_association_field_names) && !Rails.env.local?
19
+
20
+ @belongs_to_association_field_names = reflect_on_all_associations(:belongs_to).map(&:name)
21
+ end
22
+
23
+ def has_one_association_field_names
24
+ return @has_one_association_field_names if defined?(@has_one_association_field_names) && !Rails.env.local?
25
+
26
+ @has_one_association_field_names = reflect_on_all_associations(:has_one)
27
+ .map { |assoc| (!/_attachment$|_blob$/.match?(assoc.name)) ? assoc.name : nil }
28
+ .compact
29
+ end
30
+
31
+ def has_many_association_field_names
32
+ return @has_many_association_field_names if defined?(@has_many_association_field_names) && !Rails.env.local?
33
+
34
+ @has_many_association_field_names = reflect_on_all_associations(:has_many)
35
+ .map { |assoc| (!/_attachments$|_blobs$/.match?(assoc.name)) ? assoc.name : nil }
36
+ .compact
37
+ end
38
+
39
+ def has_one_attached_field_names
40
+ return @has_one_attached_field_names if defined?(@has_one_attached_field_names) && !Rails.env.local?
41
+
42
+ @has_one_attached_field_names = if respond_to?(:reflect_on_all_attachments)
43
+ reflect_on_all_attachments
44
+ .select { |a| a.macro == :has_one_attached }
45
+ .map(&:name)
46
+ else
47
+ []
48
+ end
49
+ end
50
+
51
+ def has_many_attached_field_names
52
+ return @has_many_attached_field_names if defined?(@has_many_attached_field_names) && !Rails.env.local?
53
+
54
+ @has_many_attached_field_names = if respond_to?(:reflect_on_all_attachments)
55
+ reflect_on_all_attachments
56
+ .select { |a| a.macro == :has_many_attached }
57
+ .map(&:name)
58
+ else
59
+ []
60
+ end
61
+ end
62
+
63
+ def content_column_field_names
64
+ return @content_column_field_names if defined?(@content_column_field_names) && !Rails.env.local?
65
+
66
+ @content_column_field_names = content_columns.map { |col| col.name.to_sym }
67
+ end
68
+
69
+ private
70
+
71
+ def gather_resource_field_names
72
+ belongs_to_association_field_names +
73
+ has_one_attached_field_names +
74
+ has_one_association_field_names +
75
+ has_many_attached_field_names +
76
+ has_many_association_field_names +
77
+ content_column_field_names
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/plutonium/resource/associations.rb
4
+ module Plutonium
5
+ module Resource
6
+ module Record
7
+ module Labeling
8
+ def to_label
9
+ %i[name title].each do |method|
10
+ name = send(method) if respond_to?(method)
11
+ return name if name.present?
12
+ end
13
+
14
+ "#{model_name.human} ##{to_param}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/plutonium/resource/associations.rb
4
+ module Plutonium
5
+ module Resource
6
+ module Record
7
+ module Routes
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ scope :from_path_param, ->(param) { where(id: param) }
12
+ end
13
+
14
+ class_methods do
15
+ def has_many_association_routes
16
+ return @has_many_association_routes if defined?(@has_many_association_routes) && !Rails.env.local?
17
+
18
+ @has_many_association_routes = reflect_on_all_associations(:has_many).map { |assoc| assoc.klass.model_name.plural }
19
+ end
20
+
21
+ def all_nested_attributes_options
22
+ unless Rails.env.local?
23
+ return @all_nested_attributes_options if defined?(@all_nested_attributes_options)
24
+ end
25
+
26
+ @all_nested_attributes_options = reflect_on_all_associations.map do |association|
27
+ setter_method = "#{association.name}_attributes="
28
+ if method_defined?(setter_method)
29
+ [association.name, nested_attributes_options[association.name].merge(
30
+ macro: association.macro,
31
+ class: association.polymorphic? ? nil : association.klass
32
+ )]
33
+ end
34
+ end.compact.to_h
35
+ end
36
+
37
+ private
38
+
39
+ def path_parameter(param_name)
40
+ param_name = param_name.to_sym
41
+
42
+ scope :from_path_param, ->(param) { where(param_name => param) }
43
+
44
+ define_method :to_param do
45
+ return nil unless persisted?
46
+
47
+ send(param_name)
48
+ end
49
+ end
50
+
51
+ def dynamic_path_parameter(param_name)
52
+ param_name = param_name.to_sym
53
+
54
+ scope :from_path_param, ->(param) { where(id: param.split("-").first) }
55
+
56
+ define_method :to_param do
57
+ return nil unless persisted?
58
+
59
+ "#{id}-#{send(param_name)}".parameterize
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -2,265 +2,13 @@ module Plutonium
2
2
  module Resource
3
3
  module Record
4
4
  extend ActiveSupport::Concern
5
- include Plutonium::Models::HasCents
6
-
7
- included do
8
- scope :from_path_param, ->(param) { where(id: param) }
9
-
10
- scope :associated_with, ->(record) do
11
- named_scope = :"associated_with_#{record.model_name.singular}"
12
- return send(named_scope, record) if respond_to?(named_scope)
13
-
14
- # TODO: add support for polymorphic associations
15
- # TODO: add logging
16
- # TODO: memoize this
17
-
18
- own_association = klass.find_association_from_self_to_record(record)
19
- if own_association
20
- return klass.query_based_on_association(own_association, record)
21
- end
22
-
23
- record_association = klass.find_association_to_self_from_record(record)
24
- if record_association
25
- # TODO: add a warning here about a potentially poor performing query
26
- return where(id: record.public_send(record_association.name))
27
- end
28
-
29
- klass.raise_association_error(record, named_scope)
30
- end
31
- end
32
-
33
- class_methods do
34
- # Returns the resource field names
35
- # @return [Array<Symbol>]
36
- def resource_field_names
37
- return @resource_field_names if defined?(@resource_field_names) && !Rails.env.local?
38
-
39
- @resource_field_names = gather_resource_field_names
40
- end
41
-
42
- # Overrides belongs_to to add support for polymorphic associations
43
- # @param [Symbol] name The name of the association
44
- # @param [Proc] scope The scope for the association
45
- # @param [Hash] options The options for the association
46
- def belongs_to(name, scope = nil, **options)
47
- super
48
-
49
- return unless options[:polymorphic]
50
-
51
- mod = Module.new
52
- mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
53
- extend ActiveSupport::Concern
54
-
55
- def #{name}_sgid
56
- #{name}&.to_signed_global_id
57
- end
58
-
59
- def #{name}_sgid=(sgid)
60
- self.#{name} = GlobalID::Locator.locate_signed(sgid)
61
- end
62
-
63
- define_singleton_method(:to_s) { "Plutonium::Polymorphic::BelongsTo(:#{name})" }
64
- define_singleton_method(:inspect) { "Plutonium::Polymorphic::BelongsTo(:#{name})" }
65
- RUBY
66
- include mod
67
- end
68
-
69
- # Returns the names of belongs_to associations
70
- # @return [Array<Symbol>]
71
- def belongs_to_association_field_names
72
- return @belongs_to_association_field_names if defined?(@belongs_to_association_field_names) && !Rails.env.local?
73
-
74
- @belongs_to_association_field_names = reflect_on_all_associations(:belongs_to).map(&:name)
75
- end
76
-
77
- # Returns the names of has_one associations
78
- # @return [Array<Symbol>]
79
- def has_one_association_field_names
80
- return @has_one_association_field_names if defined?(@has_one_association_field_names) && !Rails.env.local?
81
-
82
- @has_one_association_field_names = reflect_on_all_associations(:has_one)
83
- .map { |assoc| (!/_attachment$|_blob$/.match?(assoc.name)) ? assoc.name : nil }
84
- .compact
85
- end
86
-
87
- # Returns the names of has_many associations
88
- # @return [Array<Symbol>]
89
- def has_many_association_field_names
90
- return @has_many_association_field_names if defined?(@has_many_association_field_names) && !Rails.env.local?
91
-
92
- @has_many_association_field_names = reflect_on_all_associations(:has_many)
93
- .map { |assoc| (!/_attachments$|_blobs$/.match?(assoc.name)) ? assoc.name : nil }
94
- .compact
95
- end
96
-
97
- # Returns the names of has_one attached associations
98
- # @return [Array<Symbol>]
99
- def has_one_attached_field_names
100
- return @has_one_attached_field_names if defined?(@has_one_attached_field_names) && !Rails.env.local?
101
-
102
- @has_one_attached_field_names = if respond_to?(:reflect_on_all_attachments)
103
- reflect_on_all_attachments
104
- .select { |a| a.macro == :has_one_attached }
105
- .map(&:name)
106
- else
107
- []
108
- end
109
- end
110
-
111
- # Returns the names of has_many attached associations
112
- # @return [Array<Symbol>]
113
- def has_many_attached_field_names
114
- return @has_many_attached_field_names if defined?(@has_many_attached_field_names) && !Rails.env.local?
115
5
 
116
- @has_many_attached_field_names = if respond_to?(:reflect_on_all_attachments)
117
- reflect_on_all_attachments
118
- .select { |a| a.macro == :has_many_attached }
119
- .map(&:name)
120
- else
121
- []
122
- end
123
- end
124
-
125
- # Returns the names of content columns
126
- # @return [Array<Symbol>]
127
- def content_column_field_names
128
- return @content_column_field_names if defined?(@content_column_field_names) && !Rails.env.local?
129
-
130
- @content_column_field_names = content_columns.map { |col| col.name.to_sym }
131
- end
132
-
133
- # Returns the routes for has_many associations
134
- # @return [Array<Symbol>]
135
- def has_many_association_routes
136
- return @has_many_association_routes if defined?(@has_many_association_routes) && !Rails.env.local?
137
-
138
- @has_many_association_routes = reflect_on_all_associations(:has_many).map { |assoc| assoc.klass.model_name.plural }
139
- end
140
-
141
- # Returns all nested attributes options
142
- # @return [Hash]
143
- def all_nested_attributes_options
144
- unless Rails.env.local?
145
- return @all_nested_attributes_options if defined?(@all_nested_attributes_options)
146
- end
147
-
148
- @all_nested_attributes_options = reflect_on_all_associations.map do |association|
149
- setter_method = "#{association.name}_attributes="
150
- if method_defined?(setter_method)
151
- [association.name, nested_attributes_options[association.name].merge(
152
- macro: association.macro,
153
- class: association.polymorphic? ? nil : association.klass
154
- )]
155
- end
156
- end.compact.to_h
157
- end
158
-
159
- # Finds the association to the given record
160
- # @param [ActiveRecord::Base] record The record to find the association with
161
- # @return [ActiveRecord::Reflection::AssociationReflection, nil]
162
- def find_association_from_self_to_record(record)
163
- reflect_on_all_associations.find do |assoc|
164
- assoc.klass.name == record.class.name unless assoc.polymorphic?
165
- rescue
166
- assoc.check_validity!
167
- raise
168
- end
169
- end
170
-
171
- # Finds the association to self in the given record
172
- # @param [ActiveRecord::Base] record The record to find the association with
173
- # @return [ActiveRecord::Reflection::AssociationReflection, nil]
174
- def find_association_to_self_from_record(record)
175
- record.class.reflect_on_all_associations.find do |assoc|
176
- assoc.klass.name == name
177
- rescue
178
- assoc.check_validity!
179
- raise
180
- end
181
- end
182
-
183
- # Queries based on the association type
184
- # @param [ActiveRecord::Reflection::AssociationReflection] assoc The association
185
- # @param [ActiveRecord::Base] record The record to query with
186
- # @return [ActiveRecord::Relation]
187
- def query_based_on_association(assoc, record)
188
- case assoc.macro
189
- when :has_one
190
- joins(assoc.name).where(assoc.name => {record.class.primary_key => record.id})
191
- when :belongs_to
192
- where(assoc.name => record)
193
- when :has_many
194
- joins(assoc.name).where(assoc.klass.table_name => record)
195
- else
196
- raise NotImplementedError, "associated_with->##{assoc.macro}"
197
- end
198
- end
199
-
200
- # Raises an error for unresolved associations
201
- # @param [ActiveRecord::Base] record The record with unresolved association
202
- # @param [Symbol] named_scope The named scope
203
- # @raise [RuntimeError]
204
- def raise_association_error(record, named_scope)
205
- raise "Could not resolve the association between '#{name}' and '#{record.class.name}'\n\n" \
206
- "Define\n" \
207
- " 1. the associations between the models\n" \
208
- " 2. a named scope on #{name} e.g.\n\n" \
209
- "scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
210
- end
211
-
212
- private
213
-
214
- # Defines a scope and method for path parameters
215
- # @param [Symbol] param_name The name of the parameter
216
- def path_parameter(param_name)
217
- param_name = param_name.to_sym
218
-
219
- scope :from_path_param, ->(param) { where(param_name => param) }
220
-
221
- define_method :to_param do
222
- return nil unless persisted?
223
-
224
- send(param_name)
225
- end
226
- end
227
-
228
- # Defines a scope and method for dynamic path parameters
229
- # @param [Symbol] param_name The name of the parameter
230
- def dynamic_path_parameter(param_name)
231
- param_name = param_name.to_sym
232
-
233
- scope :from_path_param, ->(param) { where(id: param.split("-").first) }
234
-
235
- define_method :to_param do
236
- return nil unless persisted?
237
-
238
- "#{id}-#{send(param_name)}".parameterize
239
- end
240
- end
241
-
242
- # Gathers all resource field names
243
- # @return [Array<Symbol>]
244
- def gather_resource_field_names
245
- belongs_to_association_field_names +
246
- has_one_attached_field_names +
247
- has_one_association_field_names +
248
- has_many_attached_field_names +
249
- has_many_association_field_names +
250
- content_column_field_names
251
- end
252
- end
253
-
254
- # Returns a label for the record
255
- # @return [String]
256
- def to_label
257
- %i[name title].each do |method|
258
- name = send(method) if respond_to?(method)
259
- return name if name.present?
260
- end
261
-
262
- "#{model_name.human} ##{to_param}"
263
- end
6
+ include Plutonium::Models::HasCents
7
+ include Plutonium::Resource::Record::Routes
8
+ include Plutonium::Resource::Record::Labeling
9
+ include Plutonium::Resource::Record::FieldNames
10
+ include Plutonium::Resource::Record::Associations
11
+ include Plutonium::Resource::Record::AssociatedWith
264
12
  end
265
13
  end
266
14
  end
@@ -19,7 +19,7 @@ module Plutonium
19
19
  # @yield An optional block for additional resource configuration.
20
20
  # @return [void]
21
21
  def register_resource(resource, options = {}, &)
22
- route_config = route_set.register_resource(resource, &)
22
+ route_config = route_set.register_resource(resource, options, &)
23
23
  define_resource_routes(route_config, resource)
24
24
  resource_route_concern_names << route_config[:concern_name]
25
25
  end
@@ -65,7 +65,7 @@ module Plutonium
65
65
  # @return [void]
66
66
  def define_resource_routes(route_config, resource)
67
67
  concern route_config[:concern_name] do
68
- resources route_config[:route_name], **route_config[:route_options] do
68
+ send route_config[:route_type], route_config[:route_name], **route_config[:route_options] do
69
69
  instance_exec(&route_config[:block]) if route_config[:block]
70
70
  define_nested_resource_routes(resource)
71
71
  end
@@ -49,17 +49,18 @@ module Plutonium
49
49
  # Registers a resource for routing.
50
50
  #
51
51
  # @param resource [Class] The resource class to be registered.
52
+ # @param options [Hash] Additional options for resource registration.
52
53
  # @yield An optional block for additional resource configuration.
53
54
  # @return [Hash] The configuration for the registered resource.
54
55
  # @raise [ArgumentError] If the engine is not supported.
55
- def register_resource(resource, &)
56
+ def register_resource(resource, options = {}, &)
56
57
  self.class.validate_engine! engine
57
58
  engine.resource_register.register(resource)
58
59
 
59
60
  route_name = resource.model_name.plural
60
61
  concern_name = :"#{route_name}_routes"
61
62
 
62
- config = create_resource_config(resource, route_name, concern_name, &)
63
+ config = create_resource_config(resource, route_name, concern_name, options, &)
63
64
  resource_route_config_lookup[route_name] = config
64
65
 
65
66
  config
@@ -101,15 +102,17 @@ module Plutonium
101
102
  # @param resource_name [String] The name of the resource.
102
103
  # @param route_name [String] The pluralized name for routes.
103
104
  # @param concern_name [Symbol] The name of the concern for this resource.
105
+ # @param options [Hash] Additional options for resource registration.
104
106
  # @yield An optional block for additional resource configuration.
105
107
  # @return [Hash] The complete resource configuration.
106
- def create_resource_config(resource, route_name, concern_name, &block)
108
+ def create_resource_config(resource, route_name, concern_name, options = {}, &block)
107
109
  {
110
+ route_type: options[:singular] ? :resource : :resources,
108
111
  route_name: route_name,
109
112
  concern_name: concern_name,
110
113
  route_options: {
111
114
  controller: resource.to_s.pluralize.underscore,
112
- path: resource.model_name.collection,
115
+ path: options[:singular] ? resource.model_name.singular : resource.model_name.collection,
113
116
  concerns: %i[interactive_resource_actions]
114
117
  },
115
118
  block: block