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.
- checksums.yaml +4 -4
- data/README copy.md +1 -1
- data/README.md +1 -1
- data/app/assets/plutonium.css +1 -1
- data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
- data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
- data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
- data/app/views/resource/_resource_details.html.erb +1 -36
- data/app/views/resource/_resource_form.html.erb +1 -5
- data/app/views/resource/_resource_table.html.erb +315 -85
- data/app/views/resource/edit.html.erb +1 -5
- data/app/views/resource/index.html.erb +1 -5
- data/app/views/resource/new.html.erb +1 -5
- data/app/views/resource/show.html.erb +1 -5
- data/config/initializers/pagy.rb +1 -0
- data/config/initializers/rabl.rb +27 -20
- data/gemfiles/rails_7.gemfile.lock +5 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
- data/lib/generators/pu/core/install/install_generator.rb +0 -3
- data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
- data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
- data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
- data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
- data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
- data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
- data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
- data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
- data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
- data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
- data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
- data/lib/generators/pu/res/model/model_generator.rb +0 -7
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
- data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
- data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
- data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
- data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
- data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
- data/lib/plutonium/action/README.md +0 -0
- data/lib/plutonium/action/base.rb +103 -0
- data/lib/plutonium/action/interactive.rb +117 -0
- data/lib/plutonium/action/route_options.rb +65 -0
- data/lib/plutonium/action/simple.rb +8 -0
- data/lib/plutonium/auth.rb +1 -1
- data/lib/plutonium/configuration.rb +130 -0
- data/lib/plutonium/core/actions/collection.rb +1 -1
- data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
- data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/controller.rb +110 -0
- data/lib/plutonium/core/controllers/authorizable.rb +12 -35
- data/lib/plutonium/core/controllers/bootable.rb +38 -7
- data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
- data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
- data/lib/plutonium/core/ui/collection.rb +1 -1
- data/lib/plutonium/core/ui/detail.rb +1 -1
- data/lib/plutonium/core/ui/form.rb +1 -1
- data/lib/plutonium/definition/actions.rb +50 -0
- data/lib/plutonium/definition/base.rb +92 -0
- data/lib/plutonium/definition/config_attr.rb +30 -0
- data/lib/plutonium/definition/defineable_props.rb +96 -0
- data/lib/plutonium/definition/search.rb +21 -0
- data/lib/plutonium/engine/validator.rb +30 -0
- data/lib/plutonium/engine.rb +25 -0
- data/lib/plutonium/helpers/assets_helper.rb +73 -20
- data/lib/plutonium/helpers/form_helper.rb +1 -3
- data/lib/plutonium/interaction/README.md +369 -0
- data/lib/plutonium/interaction/base.rb +75 -0
- data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
- data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
- data/lib/plutonium/interaction/outcome.rb +129 -0
- data/lib/plutonium/interaction/response/base.rb +63 -0
- data/lib/plutonium/interaction/response/null.rb +33 -0
- data/lib/plutonium/interaction/response/redirect.rb +30 -0
- data/lib/plutonium/interaction/response/render.rb +28 -0
- data/lib/plutonium/lib/bit_flags.rb +70 -9
- data/lib/plutonium/lib/overlayed_hash.rb +86 -0
- data/lib/plutonium/lib/smart_cache.rb +171 -0
- data/lib/plutonium/models/has_cents.rb +170 -0
- data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
- data/lib/plutonium/{application → portal}/controller.rb +3 -11
- data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
- data/lib/plutonium/portal/engine.rb +15 -0
- data/lib/plutonium/railtie.rb +35 -15
- data/lib/plutonium/reloader.rb +71 -29
- data/lib/plutonium/resource/controller.rb +51 -34
- data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
- data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
- data/lib/plutonium/resource/controllers/defineable.rb +26 -0
- data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
- data/lib/plutonium/resource/controllers/presentable.rb +41 -0
- data/lib/plutonium/resource/controllers/queryable.rb +44 -0
- data/lib/plutonium/resource/definition.rb +6 -0
- data/lib/plutonium/resource/policy.rb +25 -13
- data/lib/plutonium/resource/query_object.rb +50 -51
- data/lib/plutonium/resource/record.rb +6 -89
- data/lib/plutonium/resource/register.rb +82 -0
- data/lib/plutonium/routing/mapper_extensions.rb +1 -1
- data/lib/plutonium/routing/resource_registration.rb +1 -1
- data/lib/plutonium/routing/route_set_extensions.rb +6 -18
- data/lib/plutonium/ui/action_button.rb +125 -0
- data/lib/plutonium/ui/breadcrumbs.rb +163 -0
- data/lib/plutonium/ui/component/base.rb +13 -0
- data/lib/plutonium/ui/component/behaviour.rb +38 -0
- data/lib/plutonium/ui/component/kit.rb +31 -0
- data/lib/plutonium/ui/component/methods.rb +54 -0
- data/lib/plutonium/ui/display/base.rb +25 -0
- data/lib/plutonium/ui/display/component/association.rb +26 -0
- data/lib/plutonium/ui/display/resource.rb +77 -0
- data/lib/plutonium/ui/display/theme.rb +27 -0
- data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
- data/lib/plutonium/ui/empty_card.rb +20 -0
- data/lib/plutonium/ui/form/base.rb +37 -0
- data/lib/plutonium/ui/form/resource.rb +75 -0
- data/lib/plutonium/ui/form/theme.rb +42 -0
- data/lib/plutonium/ui/page/base.rb +112 -0
- data/lib/plutonium/ui/page/edit.rb +23 -0
- data/lib/plutonium/ui/page/index.rb +27 -0
- data/lib/plutonium/ui/page/new.rb +23 -0
- data/lib/plutonium/ui/page/show.rb +27 -0
- data/lib/plutonium/ui/page_header.rb +49 -0
- data/lib/plutonium/ui/table/base.rb +13 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
- data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
- data/lib/plutonium/ui/table/display_theme.rb +21 -0
- data/lib/plutonium/ui/table/resource.rb +98 -0
- data/lib/plutonium/ui/table/theme.rb +35 -0
- data/lib/plutonium/ui.rb +9 -0
- data/lib/plutonium/version.rb +5 -1
- data/lib/plutonium.rb +53 -26
- data/package-lock.json +19 -22
- data/package.json +4 -4
- data/sig/.keep +0 -0
- data/src/css/plutonium.css +15 -0
- data/tailwind.options.js +11 -3
- metadata +220 -81
- data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
- data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
- data/lib/plutonium/concerns/resource_validatable.rb +0 -34
- data/lib/plutonium/config.rb +0 -9
- data/lib/plutonium/core/controllers/base.rb +0 -101
- data/lib/plutonium/core/controllers/presentable.rb +0 -65
- data/lib/plutonium/core/controllers/queryable.rb +0 -28
- data/lib/plutonium/pkg/app.rb +0 -35
- data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
- data/lib/plutonium/pkg/feature.rb +0 -18
- data/lib/plutonium/policy/initializer.rb +0 -22
- data/lib/plutonium/policy/scope.rb +0 -19
- data/lib/plutonium/pundit/context.rb +0 -18
- data/lib/plutonium/pundit/policy_finder.rb +0 -25
- data/lib/plutonium/resource/policy_context.rb +0 -5
- data/lib/plutonium/resource_register.rb +0 -83
- data/lib/plutonium/smart_cache.rb +0 -151
- data/sig/plutonium.rbs +0 -12
- /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
- /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Plutonium
|
|
2
|
-
module
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
199
|
+
authorize_current! resource_class, to: :"#{interactive_resource_action}?"
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def interactive_resource_collection
|
|
203
|
-
@interactive_resource_collection ||=
|
|
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
|
|
@@ -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
|
-
|
|
8
|
+
class Policy < ActionPolicy::Base
|
|
9
|
+
authorize :user, allow_nil: false
|
|
10
|
+
authorize :scope, allow_nil: true
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
+
Resource field auto-detection: #{self.class}##{method}
|
|
178
189
|
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
@@ -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
|
|
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.
|