plutonium 0.18.4 → 0.18.6

Sign up to get free protection for your applications and to get access to all the features.
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