plutonium 0.14.0 → 0.15.0.pre.rc1

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/README copy.md +1 -1
  3. data/README.md +1 -1
  4. data/app/assets/plutonium.css +1 -1
  5. data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
  6. data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
  7. data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
  8. data/app/views/resource/_resource_details.html.erb +1 -36
  9. data/app/views/resource/_resource_form.html.erb +1 -5
  10. data/app/views/resource/_resource_table.html.erb +315 -85
  11. data/app/views/resource/edit.html.erb +1 -5
  12. data/app/views/resource/index.html.erb +1 -5
  13. data/app/views/resource/new.html.erb +1 -5
  14. data/app/views/resource/show.html.erb +1 -5
  15. data/config/initializers/pagy.rb +1 -0
  16. data/config/initializers/rabl.rb +27 -20
  17. data/gemfiles/rails_7.gemfile.lock +5 -1
  18. data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
  19. data/lib/generators/pu/core/install/install_generator.rb +0 -3
  20. data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
  21. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
  22. data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
  23. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
  24. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
  25. data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
  26. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
  27. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
  28. data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
  29. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
  30. data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
  31. data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
  32. data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
  33. data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
  34. data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
  35. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
  36. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  37. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
  38. data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
  39. data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
  40. data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
  41. data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
  42. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  43. data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
  44. data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
  45. data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
  46. data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
  47. data/lib/generators/pu/res/model/model_generator.rb +0 -7
  48. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
  49. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
  50. data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
  51. data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
  52. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
  53. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
  54. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
  55. data/lib/plutonium/action/README.md +0 -0
  56. data/lib/plutonium/action/base.rb +103 -0
  57. data/lib/plutonium/action/interactive.rb +117 -0
  58. data/lib/plutonium/action/route_options.rb +65 -0
  59. data/lib/plutonium/action/simple.rb +8 -0
  60. data/lib/plutonium/auth.rb +1 -1
  61. data/lib/plutonium/configuration.rb +130 -0
  62. data/lib/plutonium/core/actions/collection.rb +1 -1
  63. data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
  64. data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
  65. data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
  66. data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
  67. data/lib/plutonium/core/controller.rb +110 -0
  68. data/lib/plutonium/core/controllers/authorizable.rb +12 -35
  69. data/lib/plutonium/core/controllers/bootable.rb +38 -7
  70. data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
  71. data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
  72. data/lib/plutonium/core/ui/collection.rb +1 -1
  73. data/lib/plutonium/core/ui/detail.rb +1 -1
  74. data/lib/plutonium/core/ui/form.rb +1 -1
  75. data/lib/plutonium/definition/actions.rb +50 -0
  76. data/lib/plutonium/definition/base.rb +92 -0
  77. data/lib/plutonium/definition/config_attr.rb +30 -0
  78. data/lib/plutonium/definition/defineable_props.rb +96 -0
  79. data/lib/plutonium/definition/search.rb +21 -0
  80. data/lib/plutonium/engine/validator.rb +30 -0
  81. data/lib/plutonium/engine.rb +25 -0
  82. data/lib/plutonium/helpers/assets_helper.rb +73 -20
  83. data/lib/plutonium/helpers/form_helper.rb +1 -3
  84. data/lib/plutonium/interaction/README.md +369 -0
  85. data/lib/plutonium/interaction/base.rb +75 -0
  86. data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
  87. data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
  88. data/lib/plutonium/interaction/outcome.rb +129 -0
  89. data/lib/plutonium/interaction/response/base.rb +63 -0
  90. data/lib/plutonium/interaction/response/null.rb +33 -0
  91. data/lib/plutonium/interaction/response/redirect.rb +30 -0
  92. data/lib/plutonium/interaction/response/render.rb +28 -0
  93. data/lib/plutonium/lib/bit_flags.rb +70 -9
  94. data/lib/plutonium/lib/overlayed_hash.rb +86 -0
  95. data/lib/plutonium/lib/smart_cache.rb +171 -0
  96. data/lib/plutonium/models/has_cents.rb +170 -0
  97. data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
  98. data/lib/plutonium/{application → portal}/controller.rb +3 -11
  99. data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
  100. data/lib/plutonium/portal/engine.rb +15 -0
  101. data/lib/plutonium/railtie.rb +35 -15
  102. data/lib/plutonium/reloader.rb +71 -29
  103. data/lib/plutonium/resource/controller.rb +51 -34
  104. data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
  105. data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
  106. data/lib/plutonium/resource/controllers/defineable.rb +26 -0
  107. data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
  108. data/lib/plutonium/resource/controllers/presentable.rb +41 -0
  109. data/lib/plutonium/resource/controllers/queryable.rb +44 -0
  110. data/lib/plutonium/resource/definition.rb +6 -0
  111. data/lib/plutonium/resource/policy.rb +25 -13
  112. data/lib/plutonium/resource/query_object.rb +50 -51
  113. data/lib/plutonium/resource/record.rb +6 -89
  114. data/lib/plutonium/resource/register.rb +82 -0
  115. data/lib/plutonium/routing/mapper_extensions.rb +1 -1
  116. data/lib/plutonium/routing/resource_registration.rb +1 -1
  117. data/lib/plutonium/routing/route_set_extensions.rb +6 -18
  118. data/lib/plutonium/ui/action_button.rb +125 -0
  119. data/lib/plutonium/ui/breadcrumbs.rb +163 -0
  120. data/lib/plutonium/ui/component/base.rb +13 -0
  121. data/lib/plutonium/ui/component/behaviour.rb +38 -0
  122. data/lib/plutonium/ui/component/kit.rb +31 -0
  123. data/lib/plutonium/ui/component/methods.rb +54 -0
  124. data/lib/plutonium/ui/display/base.rb +25 -0
  125. data/lib/plutonium/ui/display/component/association.rb +26 -0
  126. data/lib/plutonium/ui/display/resource.rb +77 -0
  127. data/lib/plutonium/ui/display/theme.rb +27 -0
  128. data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
  129. data/lib/plutonium/ui/empty_card.rb +20 -0
  130. data/lib/plutonium/ui/form/base.rb +37 -0
  131. data/lib/plutonium/ui/form/resource.rb +75 -0
  132. data/lib/plutonium/ui/form/theme.rb +42 -0
  133. data/lib/plutonium/ui/page/base.rb +112 -0
  134. data/lib/plutonium/ui/page/edit.rb +23 -0
  135. data/lib/plutonium/ui/page/index.rb +27 -0
  136. data/lib/plutonium/ui/page/new.rb +23 -0
  137. data/lib/plutonium/ui/page/show.rb +27 -0
  138. data/lib/plutonium/ui/page_header.rb +49 -0
  139. data/lib/plutonium/ui/table/base.rb +13 -0
  140. data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
  141. data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
  142. data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
  143. data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
  144. data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
  145. data/lib/plutonium/ui/table/display_theme.rb +21 -0
  146. data/lib/plutonium/ui/table/resource.rb +98 -0
  147. data/lib/plutonium/ui/table/theme.rb +35 -0
  148. data/lib/plutonium/ui.rb +9 -0
  149. data/lib/plutonium/version.rb +5 -1
  150. data/lib/plutonium.rb +53 -26
  151. data/package-lock.json +19 -22
  152. data/package.json +4 -4
  153. data/sig/.keep +0 -0
  154. data/src/css/plutonium.css +15 -0
  155. data/tailwind.options.js +11 -3
  156. metadata +220 -81
  157. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
  158. data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
  159. data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
  160. data/lib/plutonium/concerns/resource_validatable.rb +0 -34
  161. data/lib/plutonium/config.rb +0 -9
  162. data/lib/plutonium/core/controllers/base.rb +0 -101
  163. data/lib/plutonium/core/controllers/presentable.rb +0 -65
  164. data/lib/plutonium/core/controllers/queryable.rb +0 -28
  165. data/lib/plutonium/pkg/app.rb +0 -35
  166. data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
  167. data/lib/plutonium/pkg/feature.rb +0 -18
  168. data/lib/plutonium/policy/initializer.rb +0 -22
  169. data/lib/plutonium/policy/scope.rb +0 -19
  170. data/lib/plutonium/pundit/context.rb +0 -18
  171. data/lib/plutonium/pundit/policy_finder.rb +0 -25
  172. data/lib/plutonium/resource/policy_context.rb +0 -5
  173. data/lib/plutonium/resource_register.rb +0 -83
  174. data/lib/plutonium/smart_cache.rb +0 -151
  175. data/sig/plutonium.rbs +0 -12
  176. /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
  177. /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
  178. /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
  179. /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
  180. /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
  181. /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
  182. /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
  183. /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  184. /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
  185. /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
  186. /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  187. /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
  188. /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
  189. /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
  190. /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- module Core
2
+ module Resource
3
3
  module Controllers
4
4
  module InteractiveActions
5
5
  extend ActiveSupport::Concern
@@ -7,17 +7,17 @@ module Plutonium
7
7
  included do
8
8
  helper_method :current_interactive_action
9
9
 
10
- before_action :validate_interactive_resource_action, only: %i[
10
+ before_action :validate_interactive_resource_action!, only: %i[
11
11
  begin_interactive_resource_record_action commit_interactive_resource_record_action
12
12
  begin_interactive_resource_collection_action commit_interactive_resource_collection_action
13
13
  begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
14
14
  ]
15
15
 
16
- before_action :authorize_interactive_resource_record_action, only: %i[
16
+ before_action :authorize_interactive_resource_record_action!, only: %i[
17
17
  begin_interactive_resource_record_action commit_interactive_resource_record_action
18
18
  ]
19
19
 
20
- before_action :authorize_interactive_resource_action, only: %i[
20
+ before_action :authorize_interactive_resource_action!, only: %i[
21
21
  begin_interactive_resource_collection_action commit_interactive_resource_collection_action
22
22
  begin_interactive_resource_recordless_action commit_interactive_resource_recordless_action
23
23
  ]
@@ -123,7 +123,7 @@ module Plutonium
123
123
 
124
124
  # GET /resources/actions/:interactive_action
125
125
  def begin_interactive_resource_recordless_action
126
- skip_policy_scope
126
+ # skip_policy_scope
127
127
 
128
128
  @interaction = current_interactive_action.interaction.new interaction_params
129
129
 
@@ -136,7 +136,7 @@ module Plutonium
136
136
 
137
137
  # POST /resources/actions/:interactive_action
138
138
  def commit_interactive_resource_recordless_action
139
- skip_policy_scope
139
+ # skip_policy_scope
140
140
 
141
141
  respond_to do |format|
142
142
  inputs = interaction_params
@@ -182,25 +182,25 @@ module Plutonium
182
182
  @interactive_resource_actions ||= current_presenter.actions.except :new, :show, :edit, :destroy
183
183
  end
184
184
 
185
- def validate_interactive_resource_action
185
+ def validate_interactive_resource_action!
186
186
  interactive_resource_action = params[:interactive_action]&.to_sym
187
187
  unless interactive_resource_actions.key?(interactive_resource_action)
188
188
  raise ::AbstractController::ActionNotFound, "Unknown action '#{interactive_resource_action}'"
189
189
  end
190
190
  end
191
191
 
192
- def authorize_interactive_resource_record_action
192
+ def authorize_interactive_resource_record_action!
193
193
  interactive_resource_action = params[:interactive_action]&.to_sym
194
- authorize resource_record, :"#{interactive_resource_action}?"
194
+ authorize_current! resource_record, to: :"#{interactive_resource_action}?"
195
195
  end
196
196
 
197
- def authorize_interactive_resource_action
197
+ def authorize_interactive_resource_action!
198
198
  interactive_resource_action = params[:interactive_action]&.to_sym
199
- authorize resource_class, :"#{interactive_resource_action}?"
199
+ authorize_current! resource_class, to: :"#{interactive_resource_action}?"
200
200
  end
201
201
 
202
202
  def interactive_resource_collection
203
- @interactive_resource_collection ||= policy_scope(resource_class).from_path_param(params.require(:ids)).all
203
+ @interactive_resource_collection ||= current_authorized_scope.from_path_param(params.require(:ids))
204
204
  end
205
205
 
206
206
  def interaction_params
@@ -0,0 +1,41 @@
1
+ module Plutonium
2
+ module Resource
3
+ module Controllers
4
+ module Presentable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :presentable_attributes, :present_associations?
9
+ helper_method :build_form, :build_detail, :build_collection
10
+ end
11
+
12
+ private
13
+
14
+ def presentable_attributes
15
+ @presentable_attributes ||= begin
16
+ presentable_attributes = permitted_attributes
17
+ presentable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"] if scoped_to_entity?
18
+ presentable_attributes -= [parent_input_param, :"#{parent_input_param}_id"] if current_parent.present?
19
+ presentable_attributes
20
+ end
21
+ end
22
+
23
+ def build_collection
24
+ current_definition.collection_class.new(@resource_records, resource_fields: presentable_attributes, resource_definition: current_definition)
25
+ end
26
+
27
+ def build_detail
28
+ current_definition.detail_class.new(resource_record, resource_fields: presentable_attributes, resource_associations: permitted_associations, resource_definition: current_definition)
29
+ end
30
+
31
+ def build_form
32
+ current_definition.form_class.new(resource_record, resource_fields: presentable_attributes, resource_definition: current_definition)
33
+ end
34
+
35
+ def present_associations?
36
+ current_parent.nil?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ using Plutonium::Refinements::ParameterRefinements
2
+
3
+ module Plutonium
4
+ module Resource
5
+ module Controllers
6
+ module Queryable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ helper_method :resource_query_params, :current_query_object
11
+ end
12
+
13
+ def resource_query_object(resource_class, params)
14
+ query_object_class = "#{resource_class}QueryObject".constantize
15
+ query_object_class.new resource_context, params
16
+ end
17
+
18
+ def current_query_object
19
+ @current_query_object ||= Plutonium::Resource::QueryObject.new(resource_context, resource_query_params) do |query_object|
20
+ if current_definition.search_definition
21
+ query_object.define_search proc { |scope, search:|
22
+ current_definition.search_definition.call(scope, search)
23
+ }
24
+ end
25
+
26
+ current_definition.defined_scopes.each do |key, value|
27
+ query_object.define_scope key, value[:block]
28
+ end
29
+
30
+ current_definition.defined_sorts.each do |key, value|
31
+ query_object.define_sorter key, value[:block]
32
+ end
33
+
34
+ query_object
35
+ end
36
+ end
37
+
38
+ def resource_query_params
39
+ (params[:q]&.nilify&.to_unsafe_h || {}).with_indifferent_access
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,6 @@
1
+ module Plutonium
2
+ module Resource
3
+ class Definition < Plutonium::Definition::Base
4
+ end
5
+ end
6
+ end
@@ -1,13 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Plutonium
2
4
  module Resource
3
5
  # Policy class to define permissions and attributes for a resource.
4
6
  # This class provides methods to check permissions for various actions
5
7
  # and to retrieve permitted attributes for these actions.
6
- class Policy
7
- include Plutonium::Policy::Initializer
8
+ class Policy < ActionPolicy::Base
9
+ authorize :user, allow_nil: false
10
+ authorize :scope, allow_nil: true
8
11
 
9
- # Scope class to define the scope of the policy.
10
- class Scope < Plutonium::Policy::Scope
12
+ relation_scope do |relation|
13
+ if scope.present?
14
+ relation = relation.associated_with(scope)
15
+ end
16
+ relation
11
17
  end
12
18
 
13
19
  # Sends a method and raises an error if the method is not implemented.
@@ -95,7 +101,7 @@ module Plutonium
95
101
  # @return [Array<Symbol>] The permitted attributes.
96
102
  def permitted_attributes_for_create
97
103
  autodetect_permitted_fields(:permitted_attributes_for_create) - [
98
- context.resource_context.resource_class.primary_key.to_sym, # primary_key
104
+ resource_class.primary_key.to_sym, # primary_key
99
105
  :created_at, :updated_at # timestamps
100
106
  ]
101
107
  end
@@ -153,13 +159,17 @@ module Plutonium
153
159
 
154
160
  private
155
161
 
162
+ def resource_class
163
+ record.instance_of?(Class) ? record : record.class
164
+ end
165
+
156
166
  # Autodetects the permitted fields for a given method.
157
167
  #
158
168
  # @param method_name [Symbol] The name of the method.
159
169
  # @return [Array<Symbol>] The auto-detected permitted fields.
160
170
  def autodetect_permitted_fields(method_name)
161
171
  warn_about_autodetect_usage(method_name)
162
- context.resource_context.resource_class.resource_field_names
172
+ resource_class.resource_field_names
163
173
  end
164
174
 
165
175
  # Warns about the usage of auto-detection of fields.
@@ -171,16 +181,18 @@ module Plutonium
171
181
  raise "Resource field auto-detection: #{self.class}##{method} outside development"
172
182
  end
173
183
 
174
- Plutonium.logger.warn %(
175
- 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
184
+ Plutonium.logger.warn {
185
+ %(
186
+ 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
176
187
 
177
- Resource field auto-detection: #{self.class}##{method}
188
+ Resource field auto-detection: #{self.class}##{method}
178
189
 
179
- Auto-detected resource fields result in security holes and will fail outside of development.
180
- Override #{context.resource_context.resource_class}Policy or #{self.class} with your own ##{method} method.
190
+ Auto-detected resource fields result in security holes and will fail outside of development.
191
+ Override #{resource_class}Policy or #{self.class} with your own ##{method} method.
181
192
 
182
- 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
183
- )
193
+ 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
194
+ )
195
+ }
184
196
  end
185
197
  end
186
198
  end
@@ -102,7 +102,7 @@ module Plutonium
102
102
  #
103
103
  # @param context [Object] The context in which the query object is used.
104
104
  # @param params [Hash] The parameters for initialization.
105
- def initialize(context, params)
105
+ def initialize(context, params, &)
106
106
  @context = context
107
107
 
108
108
  define_standard_queries
@@ -110,10 +110,54 @@ module Plutonium
110
110
  define_filters
111
111
  define_sorters
112
112
 
113
+ yield self if block_given?
114
+
113
115
  extract_filter_params(params)
114
116
  extract_sort_params(params)
115
117
  end
116
118
 
119
+ # Defines a filter with the given name and body.
120
+ #
121
+ # @param name [Symbol] The name of the filter.
122
+ # @param body [Proc, nil] The body of the filter.
123
+ def define_filter(name, body = nil, &)
124
+ body ||= name
125
+ filter_definitions[name] = build_query(body, &)
126
+ end
127
+
128
+ # Defines a scope with the given name and body.
129
+ #
130
+ # @param name [Symbol] The name of the scope.
131
+ # @param body [Proc, nil] The body of the scope.
132
+ def define_scope(name, body = nil)
133
+ body ||= name
134
+ scope_definitions[name] = build_query(body)
135
+ end
136
+
137
+ # Defines a sort with the given name and body.
138
+ #
139
+ # @param name [Symbol] The name of the sort.
140
+ # @param body [Proc, nil] The body of the sort.
141
+ def define_sorter(name, body = nil)
142
+ if body.nil?
143
+ sort_field = determine_sort_field(name)
144
+ body = ->(scope, direction:) { scope.order(sort_field => direction) }
145
+ end
146
+
147
+ sort_definitions[name] = build_query(body) do |query|
148
+ query.define_field_input :direction
149
+ end
150
+ end
151
+
152
+ # Defines a search filter with the given body.
153
+ #
154
+ # @param body [Proc, Symbol] The body of the search filter.
155
+ def define_search(body)
156
+ @search_filter = build_query(body) do |query|
157
+ query.define_field_input :search
158
+ end
159
+ end
160
+
117
161
  # Builds a URL with the given options for search and sorting.
118
162
  #
119
163
  # @param options [Hash] The options for building the URL.
@@ -186,48 +230,6 @@ module Plutonium
186
230
  define_search(:search) if resource_class.respond_to?(:search)
187
231
  end
188
232
 
189
- # Defines a filter with the given name and body.
190
- #
191
- # @param name [Symbol] The name of the filter.
192
- # @param body [Proc, nil] The body of the filter.
193
- def define_filter(name, body = nil, &)
194
- body ||= name
195
- filter_definitions[name] = build_query(body, &)
196
- end
197
-
198
- # Defines a scope with the given name and body.
199
- #
200
- # @param name [Symbol] The name of the scope.
201
- # @param body [Proc, nil] The body of the scope.
202
- def define_scope(name, body = nil)
203
- body ||= name
204
- scope_definitions[name] = build_query(body)
205
- end
206
-
207
- # Defines a sort with the given name and body.
208
- #
209
- # @param name [Symbol] The name of the sort.
210
- # @param body [Proc, nil] The body of the sort.
211
- def define_sorter(name, body = nil)
212
- if body.nil?
213
- sort_field = determine_sort_field(name)
214
- body = ->(scope, direction:) { scope.order(sort_field => direction) }
215
- end
216
-
217
- sort_definitions[name] = build_query(body) do |query|
218
- query.define_field_input :direction
219
- end
220
- end
221
-
222
- # Defines a search filter with the given body.
223
- #
224
- # @param body [Proc, Symbol] The body of the search filter.
225
- def define_search(body)
226
- @search_filter = build_query(body) do |query|
227
- query.define_field_input :search
228
- end
229
- end
230
-
231
233
  # Extracts filter parameters from the given params.
232
234
  #
233
235
  # @param params [Hash] The parameters to extract.
@@ -308,14 +310,11 @@ module Plutonium
308
310
  query_params[:sort_fields] << sort.to_s unless query_params[:sort_fields].include?(sort.to_s)
309
311
 
310
312
  sort_direction = selected_sort_directions[sort]
311
- if sort_direction.nil?
312
- query_params[:sort_directions][sort] = "ASC"
313
- elsif sort_direction == "ASC"
314
- query_params[:sort_directions][sort] = "DESC"
315
- else
316
- query_params[:sort_fields].delete_if { |e| e == sort.to_s }
317
- query_params[:sort_directions].delete(sort)
318
- end
313
+ query_params[:sort_directions][sort] = (sort_direction == "ASC") ? "DESC" : "ASC"
314
+ # else
315
+ # query_params[:sort_fields].delete_if { |e| e == sort.to_s }
316
+ # query_params[:sort_directions].delete(sort)
317
+ # end
319
318
  end
320
319
  end
321
320
 
@@ -2,6 +2,7 @@ module Plutonium
2
2
  module Resource
3
3
  module Record
4
4
  extend ActiveSupport::Concern
5
+ include Plutonium::Models::HasCents
5
6
 
6
7
  included do
7
8
  scope :from_path_param, ->(param) { where(id: param) }
@@ -14,12 +15,12 @@ module Plutonium
14
15
  # TODO: add logging
15
16
  # TODO: memoize this
16
17
 
17
- own_association = klass.find_association_to_record(record)
18
+ own_association = klass.find_association_from_self_to_record(record)
18
19
  if own_association
19
20
  return klass.query_based_on_association(own_association, record)
20
21
  end
21
22
 
22
- record_association = klass.find_association_to_self(record)
23
+ record_association = klass.find_association_to_self_from_record(record)
23
24
  if record_association
24
25
  # TODO: add a warning here about a potentially poor performing query
25
26
  return where(id: record.public_send(record_association.name))
@@ -43,7 +44,7 @@ module Plutonium
43
44
  # @param [Proc] scope The scope for the association
44
45
  # @param [Hash] options The options for the association
45
46
  def belongs_to(name, scope = nil, **options)
46
- super(name, scope, **options)
47
+ super
47
48
 
48
49
  return unless options[:polymorphic]
49
50
 
@@ -155,31 +156,10 @@ module Plutonium
155
156
  end.compact.to_h
156
157
  end
157
158
 
158
- # Returns the strong parameters definition for the given attribute names
159
- # @param [Array<Symbol>] *attributes Attribute names
160
- # @return [Array<Symbol, Hash>] A strong parameters compatible array
161
- def strong_parameters_for(*attributes)
162
- unbacked = attributes - strong_parameters_definition.keys
163
-
164
- backed = strong_parameters_definition.slice(*attributes).values.reduce(:merge)
165
- &.map { |key, value| value.nil? ? key : {key => value} } || {}
166
-
167
- case backed.presence
168
- when Hash
169
- [*unbacked, **backed]
170
- when Array
171
- [*unbacked, *backed]
172
- when nil
173
- unbacked
174
- else
175
- raise "Unexpected strong parameters definition: #{backed.class}"
176
- end
177
- end
178
-
179
159
  # Finds the association to the given record
180
160
  # @param [ActiveRecord::Base] record The record to find the association with
181
161
  # @return [ActiveRecord::Reflection::AssociationReflection, nil]
182
- def find_association_to_record(record)
162
+ def find_association_from_self_to_record(record)
183
163
  reflect_on_all_associations.find do |assoc|
184
164
  assoc.klass.name == record.class.name unless assoc.polymorphic?
185
165
  rescue
@@ -191,7 +171,7 @@ module Plutonium
191
171
  # Finds the association to self in the given record
192
172
  # @param [ActiveRecord::Base] record The record to find the association with
193
173
  # @return [ActiveRecord::Reflection::AssociationReflection, nil]
194
- def find_association_to_self(record)
174
+ def find_association_to_self_from_record(record)
195
175
  record.class.reflect_on_all_associations.find do |assoc|
196
176
  assoc.klass.name == name
197
177
  rescue
@@ -231,69 +211,6 @@ module Plutonium
231
211
 
232
212
  private
233
213
 
234
- # Defines the strong parameters
235
- # @return [Hash]
236
- def strong_parameters_definition
237
- unless Rails.env.local?
238
- return @strong_parameters if defined?(@strong_parameters)
239
- end
240
-
241
- @strong_parameters = build_strong_parameters
242
- end
243
-
244
- # Builds the strong parameters hash
245
- # @return [Hash]
246
- def build_strong_parameters
247
- parameters = content_column_field_names.map do |name|
248
- column = columns_hash[name.to_s]
249
- type = if column.try(:array?)
250
- []
251
- else
252
- ([:json, :jsonb].include?(column&.type) ? {} : nil)
253
- end
254
- [name, {name => type}]
255
- end.to_h
256
-
257
- parameters.merge!(belongs_to_association_parameters)
258
- parameters.merge!(has_many_association_parameters)
259
- parameters.merge!(attachment_parameters)
260
- parameters.merge!(nested_attributes_parameters)
261
-
262
- parameters
263
- end
264
-
265
- # Returns the parameters for belongs_to associations
266
- # @return [Hash]
267
- def belongs_to_association_parameters
268
- reflect_on_all_associations(:belongs_to).map do |reflection|
269
- input_param = reflection.options[:foreign_key] || :"#{reflection.name}_id"
270
- [reflection.name, {input_param => nil, :"#{reflection.name}_sgid" => nil}]
271
- end.to_h
272
- end
273
-
274
- # Returns the parameters for has_many associations
275
- # @return [Hash]
276
- def has_many_association_parameters
277
- has_many_association_field_names.map do |name|
278
- [name, {"#{name.to_s.singularize}_ids": []}]
279
- end.to_h
280
- end
281
-
282
- # Returns the parameters for attachments
283
- # @return [Hash]
284
- def attachment_parameters
285
- has_many_attached_field_names.map { |name| [name, {name => []}] }.to_h
286
- .merge(has_one_attached_field_names.map { |name| [name, {name => nil}] }.to_h)
287
- end
288
-
289
- # Returns the parameters for nested attributes
290
- # @return [Hash]
291
- def nested_attributes_parameters
292
- all_nested_attributes_options.keys.map do |name|
293
- [name, {"#{name}_attributes" => {}}]
294
- end.to_h
295
- end
296
-
297
214
  # Defines a scope and method for path parameters
298
215
  # @param [Symbol] param_name The name of the parameter
299
216
  def path_parameter(param_name)
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Resource
5
+ # Resource register manages the registration and lookup of resources.
6
+ class Register
7
+ include Plutonium::Lib::SmartCache
8
+
9
+ # Custom error class for frozen register operations
10
+ class FrozenRegisterError < StandardError; end
11
+
12
+ def initialize
13
+ @resources = Set.new
14
+ @frozen = false
15
+ end
16
+
17
+ # Registers a new resource with the register.
18
+ #
19
+ # @param resource [Class] The resource class to be registered.
20
+ # @raise [FrozenRegisterError] If the register is frozen.
21
+ # @return [void]
22
+ def register(resource)
23
+ raise FrozenRegisterError, "Cannot modify frozen resource register" if @frozen
24
+
25
+ @resources.add(resource.to_s)
26
+ end
27
+
28
+ # Returns an array of all registered resource classes and freezes the register.
29
+ #
30
+ # @return [Array<Class>] An array of registered resource classes.
31
+ def resources
32
+ freeze
33
+ @resources.map(&:constantize)
34
+ end
35
+ memoize_unless_reloading :resources
36
+
37
+ # Returns a hash mapping route keys to their corresponding resource classes.
38
+ # This method will freeze the register if it hasn't been frozen already.
39
+ #
40
+ # @return [Hash{Symbol => Class}] A hash where keys are route keys and values are resource classes.
41
+ def route_key_lookup
42
+ freeze
43
+ resources.to_h do |resource|
44
+ [resource.model_name.singular_route_key.to_sym, resource]
45
+ end
46
+ end
47
+ memoize_unless_reloading :route_key_lookup
48
+
49
+ # Clears all registered resources and invalidates the cache.
50
+ #
51
+ # @return [void]
52
+ def clear
53
+ @resources.clear
54
+ @frozen = false
55
+ invalidate_cache
56
+ end
57
+
58
+ # Checks if the register is frozen.
59
+ #
60
+ # @return [Boolean] True if the register is frozen, false otherwise.
61
+ def frozen?
62
+ @frozen
63
+ end
64
+
65
+ private
66
+
67
+ # Freezes the register
68
+ #
69
+ # @return [Boolean] Always returns true
70
+ def freeze
71
+ @frozen ||= true
72
+ end
73
+
74
+ # Invalidates the memoization cache
75
+ #
76
+ # @return [void]
77
+ def invalidate_cache
78
+ flush_smart_cache
79
+ end
80
+ end
81
+ end
82
+ end
@@ -79,7 +79,7 @@ module Plutonium
79
79
  def define_nested_resource_routes(resource)
80
80
  nested_configs = route_set.resource_route_config_for(*resource.has_many_association_routes)
81
81
  nested_configs.each do |nested_config|
82
- resources nested_config[:route_name], **nested_config[:route_options] do
82
+ resources "nested_#{nested_config[:route_name]}", **nested_config[:route_options] do
83
83
  instance_exec(&nested_config[:block]) if nested_config[:block]
84
84
  end
85
85
  end
@@ -8,7 +8,7 @@ module Plutonium
8
8
 
9
9
  class_methods do
10
10
  def resource_register
11
- @resource_register ||= Plutonium::ResourceRegister.new
11
+ @resource_register ||= Plutonium::Resource::Register.new
12
12
  end
13
13
  end
14
14
  end
@@ -12,6 +12,9 @@ module Plutonium
12
12
  # register_resource SomeModel
13
13
  # end
14
14
  module RouteSetExtensions
15
+ extend ActiveSupport::Concern
16
+ include Plutonium::Engine::Validator
17
+
15
18
  # Clears all registered resources and route configurations.
16
19
  #
17
20
  # This method should be called when you want to reset all registered resources
@@ -30,7 +33,7 @@ module Plutonium
30
33
  # @return [void]
31
34
  # @yield Executes the given block in the context of route drawing.
32
35
  def draw(&block)
33
- if supported_engine?
36
+ if self.class.supported_engine?(engine)
34
37
  ActiveSupport::Notifications.instrument("plutonium.resource_routes.draw", app: engine.to_s) do
35
38
  super do
36
39
  setup_shared_resource_concerns
@@ -48,9 +51,9 @@ module Plutonium
48
51
  # @param resource [Class] The resource class to be registered.
49
52
  # @yield An optional block for additional resource configuration.
50
53
  # @return [Hash] The configuration for the registered resource.
51
- # @raise [ArgumentError] If the engine doesn't support Plutonium::Pkg::App.
54
+ # @raise [ArgumentError] If the engine is not supported.
52
55
  def register_resource(resource, &)
53
- validate_engine!
56
+ self.class.validate_engine! engine
54
57
  engine.resource_register.register(resource)
55
58
 
56
59
  route_name = resource.model_name.plural
@@ -85,21 +88,6 @@ module Plutonium
85
88
  @resource_route_config_lookup ||= {}
86
89
  end
87
90
 
88
- # Validates that the current engine supports Plutonium features.
89
- #
90
- # @raise [ArgumentError] If the engine doesn't include Plutonium::Pkg::App.
91
- # @return [void]
92
- def validate_engine!
93
- raise ArgumentError, "#{engine} must include Plutonium::Pkg::App to register resources" unless supported_engine?
94
- end
95
-
96
- # Checks if the current engine supports Plutonium features.
97
- #
98
- # @return [Boolean] True if the engine includes Plutonium::Pkg::App, false otherwise.
99
- def supported_engine?
100
- engine.include?(Plutonium::Pkg::App)
101
- end
102
-
103
91
  # Determines the appropriate engine based on the current scope.
104
92
  #
105
93
  # @return [Class] The determined engine class.