plutonium 0.10.1 → 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/app/assets/javascripts/turbo/index.js +1 -1
- data/app/views/application/_flash_alerts.html.erb +3 -0
- data/app/views/application/_flash_toasts.html.erb +2 -0
- data/app/views/application/_resource_header.html.erb +12 -12
- data/app/views/components/base.rb +1 -1
- data/app/views/layouts/resource.html copy.erb +2 -2
- data/app/views/layouts/resource.html.erb +3 -2
- data/app/views/layouts/rodauth.html.erb +13 -8
- data/app/views/resource/_nav_user.html.erb +1 -1
- data/app/views/resource/index.rabl +1 -1
- data/app/views/rodauth/add_recovery_codes.html.erb +8 -7
- data/app/views/rodauth/otp_auth.html.erb +1 -1
- data/app/views/rodauth/otp_setup.html.erb +10 -8
- data/brakeman.ignore +1 -1
- data/config/initializers/rabl.rb +2 -0
- data/css.manifest +1 -1
- data/js.manifest +2 -2
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +0 -3
- data/lib/generators/pu/core/ruby/ruby_generator.rb +30 -0
- data/lib/generators/pu/core/ruby/templates/.keep +0 -0
- data/lib/generators/pu/docker/install/install_generator.rb +35 -0
- data/lib/generators/pu/docker/install/templates/.keep +0 -0
- data/lib/generators/pu/docker/install/templates/Dockerfile.dev.tt +30 -0
- data/lib/generators/pu/docker/install/templates/Dockerfile.tt +75 -0
- data/lib/generators/pu/docker/install/templates/bin/console +3 -0
- data/lib/generators/pu/docker/install/templates/bin/restart +3 -0
- data/lib/generators/pu/docker/install/templates/bin/shell +3 -0
- data/lib/generators/pu/docker/install/templates/docker-compose.yml +29 -0
- data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +32 -0
- data/lib/generators/pu/gem/dotenv/templates/.env +6 -0
- data/lib/generators/pu/gem/dotenv/templates/.env.local +5 -0
- data/lib/generators/pu/gem/dotenv/templates/.env.local.template +5 -0
- data/lib/generators/pu/gem/dotenv/templates/.env.template +6 -0
- data/lib/generators/pu/gem/dotenv/templates/.keep +0 -0
- data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +15 -0
- data/lib/generators/pu/gem/redis/redis_generator.rb +22 -0
- data/lib/generators/pu/gem/redis/templates/.keep +0 -0
- data/lib/generators/pu/gen/pug/pug_generator.rb +6 -0
- data/lib/generators/pu/gen/pug/templates/pug.rb.tt +1 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +154 -32
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +6 -6
- data/lib/generators/pu/lib/plutonium_generators/installer.rb +1 -1
- data/lib/generators/pu/pkg/app/templates/config/routes.rb.tt +3 -3
- data/lib/generators/pu/rodauth/account_generator.rb +10 -10
- data/lib/generators/pu/rodauth/install_generator.rb +9 -2
- data/lib/generators/pu/rodauth/migration/sequel/audit_logging.erb +2 -2
- data/lib/generators/pu/rodauth/migration_generator.rb +1 -1
- data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/account_rodauth_plugin.rb.tt +2 -2
- data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/rodauth_plugin.rb.tt +0 -3
- data/lib/generators/pu/rodauth/templates/db/migrate/install_rodauth.rb.tt +5 -0
- data/lib/generators/pu/service/postgres/postgres_generator.rb +61 -0
- data/lib/generators/pu/service/postgres/templates/.keep +0 -0
- data/lib/generators/pu/service/postgres/templates/bin/initdb.d/create-multiple-postgresql-databases.sh +22 -0
- data/lib/generators/pu/service/postgres/templates/database.yml.tt +93 -0
- data/lib/generators/pu/service/sidekiq/sidekiq_generator.rb +62 -0
- data/lib/generators/pu/service/sidekiq/templates/.keep +0 -0
- data/lib/generators/pu/service/sidekiq/templates/app/sidekiq/sidekiq_job.rb +5 -0
- data/lib/generators/pu/service/sidekiq/templates/config/initializers/sidekiq.rb +53 -0
- data/lib/generators/pu/service/sidekiq/templates/config/sidekiq.yml +6 -0
- data/lib/plutonium/config.rb +7 -13
- data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
- data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
- data/lib/plutonium/core/controllers/base.rb +13 -3
- data/lib/plutonium/core/controllers/entity_scoping.rb +3 -3
- data/lib/plutonium/core/controllers/queryable.rb +3 -1
- data/lib/plutonium/icons.rb +1 -1
- data/lib/plutonium/pkg/app.rb +6 -0
- data/lib/plutonium/railtie.rb +19 -0
- data/lib/plutonium/reloader.rb +99 -0
- data/lib/plutonium/resource/controller.rb +62 -22
- data/lib/plutonium/resource/policy.rb +56 -9
- data/lib/plutonium/resource/presenter.rb +44 -12
- data/lib/plutonium/resource/query_object.rb +186 -73
- data/lib/plutonium/resource/record.rb +213 -119
- data/lib/plutonium/rodauth/controller_methods.rb +7 -0
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +54 -12
- data/lib/tasks/create_rodauth_admin.rake +16 -0
- data/package-lock.json +174 -0
- data/package.json +2 -0
- data/public/plutonium-assets/plutonium-app-6WILQCTT.js +39 -0
- data/public/plutonium-assets/plutonium-app-6WILQCTT.js.map +7 -0
- data/public/plutonium-assets/plutonium-logo-original.png +0 -0
- data/public/plutonium-assets/plutonium-logo-white.png +0 -0
- data/public/plutonium-assets/plutonium-logo.png +0 -0
- data/public/plutonium-assets/plutonium.2d4f0c333cd000051d3b.css +3424 -0
- data/public/plutonium-assets/plutonium.8bee7a8482988b0360e3.css +3420 -0
- data/public/plutonium-assets/plutonium.ico +0 -0
- metadata +44 -7
- data/lib/plutonium/reactor/core.rb +0 -78
- data/public/plutonium-assets/logo.png +0 -0
- /data/lib/generators/pu/rodauth/templates/app/{misc → rodauth}/rodauth_app.rb.tt +0 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
redis_config = {
|
4
|
+
url: ENV.fetch("REDIS_QUEUE_URL", ""),
|
5
|
+
connect_timeout: 1, # Defaults to 1 second
|
6
|
+
read_timeout: 0.2, # Defaults to 1 second
|
7
|
+
write_timeout: 0.2, # Defaults to 1 second
|
8
|
+
# https://github.com/redis/redis-rb#reconnections
|
9
|
+
reconnect_attempts: [ # Defaults to 1
|
10
|
+
0, # retry immediately
|
11
|
+
0.25, # retry a second time after 250ms
|
12
|
+
1, # retry a third time after another 1s
|
13
|
+
5, # retry a fourth time after another 5s
|
14
|
+
10 # retry a fifth and final time after another 15s
|
15
|
+
]
|
16
|
+
}
|
17
|
+
|
18
|
+
Sidekiq.configure_client do |config|
|
19
|
+
config.redis = redis_config
|
20
|
+
|
21
|
+
# Configure sidekiq client here
|
22
|
+
end
|
23
|
+
|
24
|
+
Sidekiq.configure_server do |config|
|
25
|
+
config.redis = redis_config
|
26
|
+
config.logger = Rails.logger = Sidekiq::Logger.new($stdout)
|
27
|
+
config.death_handlers << lambda { |job, exception|
|
28
|
+
worker = job["wrapped"].safe_constantize
|
29
|
+
worker&.sidekiq_retries_exhausted_block&.call(job, exception)
|
30
|
+
}
|
31
|
+
|
32
|
+
if defined?(Prosopite)
|
33
|
+
# configure prosopite
|
34
|
+
config.server_middleware do |chain|
|
35
|
+
require "prosopite/middleware/sidekiq"
|
36
|
+
chain.add(Prosopite::Middleware::Sidekiq)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Configure sidekiq server here
|
41
|
+
end
|
42
|
+
|
43
|
+
# Use sidekiq
|
44
|
+
Rails.application.config.active_job.queue_adapter = :sidekiq
|
45
|
+
|
46
|
+
# nil will use the "default" queue
|
47
|
+
# some of these options will not work with your Rails version. add/remove as necessary
|
48
|
+
Rails.application.config.action_mailer.deliver_later_queue_name = nil if Rails.application.config.respond_to?(:action_mailbox) # defaults to "mailers"
|
49
|
+
Rails.application.config.action_mailbox.queues.routing = nil if Rails.application.config.respond_to?(:action_mailbox) # defaults to "action_mailbox_routing"
|
50
|
+
Rails.application.config.active_storage.queues.analysis = nil if Rails.application.config.respond_to?(:active_storage) # defaults to "active_storage_analysis"
|
51
|
+
Rails.application.config.active_storage.queues.purge = nil if Rails.application.config.respond_to?(:active_storage) # defaults to "active_storage_purge"
|
52
|
+
Rails.application.config.active_storage.queues.mirror = nil if Rails.application.config.respond_to?(:active_storage) # defaults to "active_storage_mirror"
|
53
|
+
Rails.application.config.active_storage.queues.purge = :low if Rails.application.config.respond_to?(:active_storage) # put purge jobs in the `low` queue
|
data/lib/plutonium/config.rb
CHANGED
@@ -3,25 +3,19 @@ require "active_model"
|
|
3
3
|
|
4
4
|
module Plutonium
|
5
5
|
module Config
|
6
|
-
# @return [Boolean] Are we developing plutonium? This is separate from Rails development.
|
7
|
-
mattr_accessor :development
|
8
|
-
@@development = ActiveModel::Type::Boolean.new.cast(ENV["PLUTONIUM_DEV"]).present?
|
9
|
-
|
10
|
-
# @return [Boolean] Should hotreload be enabled? Enabled by default in Rails development.
|
11
|
-
mattr_accessor :enable_hotreload
|
12
|
-
@@enable_hotreload = defined?(Rails.env) && Rails.env.development?
|
13
|
-
|
14
|
-
mattr_accessor :cache_discovery
|
15
|
-
@@cache_discovery = defined?(Rails.env) && !Rails.env.development?
|
16
|
-
|
17
6
|
mattr_accessor :stylesheet_tag
|
18
7
|
@@stylesheet_tag = ->(view_context) {
|
19
|
-
"<link rel=\"stylesheet\" href=\"#{Plutonium.stylesheet_link}\" />"
|
8
|
+
"<link rel=\"stylesheet\" href=\"#{Plutonium.stylesheet_link}\" data-turbo-track=\"reload\" />".html_safe
|
20
9
|
}
|
21
10
|
|
22
11
|
mattr_accessor :script_tag
|
23
12
|
@@script_tag = ->(view_context) {
|
24
|
-
"<script src=\"#{Plutonium.script_link}\"></script>"
|
13
|
+
"<script src=\"#{Plutonium.script_link}\" data-turbo-track=\"reload\"></script>".html_safe
|
14
|
+
}
|
15
|
+
|
16
|
+
mattr_accessor :favicon_tag
|
17
|
+
@@favicon_tag = ->(view_context) {
|
18
|
+
"<link rel=\"icon\" type=\"image/x-icon\" href=\"#{Plutonium.favicon_link}\">".html_safe
|
25
19
|
}
|
26
20
|
end
|
27
21
|
end
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
14
14
|
# If cache_discovery is enabled, use the class level cache that persists
|
15
15
|
# between requests, otherwise use the instance one.
|
16
16
|
def autodiscovery_input_cache
|
17
|
-
if
|
17
|
+
if Rails.application.config.plutonium.cache_discovery
|
18
18
|
self.class.autodiscovery_input_cache
|
19
19
|
else
|
20
20
|
@autodiscovery_input_cache ||= {}
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
14
14
|
# If cache_discovery is enabled, use the class level cache that persists
|
15
15
|
# between requests, otherwise use the instance one.
|
16
16
|
def autodiscovery_renderer_cache
|
17
|
-
if
|
17
|
+
if Rails.application.config.plutonium.cache_discovery
|
18
18
|
self.class.autodiscovery_renderer_cache
|
19
19
|
else
|
20
20
|
@autodiscovery_renderer_cache ||= {}
|
@@ -18,7 +18,7 @@ module Plutonium
|
|
18
18
|
end
|
19
19
|
|
20
20
|
helper Plutonium::Helpers
|
21
|
-
helper_method :page_title, :resource_url_for, :resource_url_args_for
|
21
|
+
helper_method :page_title, :resource_url_for, :resource_url_args_for, :root_path, :application_name
|
22
22
|
|
23
23
|
append_view_path File.expand_path("app/views", Plutonium.root)
|
24
24
|
layout -> { turbo_frame_request? ? false : "resource" }
|
@@ -27,11 +27,15 @@ module Plutonium
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def set_page_title
|
30
|
-
@page_title =
|
30
|
+
@page_title = nil
|
31
31
|
end
|
32
32
|
|
33
33
|
def page_title(title)
|
34
|
-
[title.presence,
|
34
|
+
[title.presence, application_name].compact.join(" | ")
|
35
|
+
end
|
36
|
+
|
37
|
+
def application_name
|
38
|
+
Plutonium.application_name
|
35
39
|
end
|
36
40
|
|
37
41
|
#
|
@@ -80,6 +84,12 @@ module Plutonium
|
|
80
84
|
def resource_url_for(...)
|
81
85
|
send(current_package.name.underscore.to_sym).url_for(resource_url_args_for(...))
|
82
86
|
end
|
87
|
+
|
88
|
+
def root_path(*)
|
89
|
+
return send(:"#{scoped_entity_param_key}_root_path", *) if scoped_to_entity? && scoped_entity_strategy == :path
|
90
|
+
|
91
|
+
super(*)
|
92
|
+
end
|
83
93
|
end
|
84
94
|
end
|
85
95
|
end
|
@@ -43,15 +43,15 @@ module Plutonium
|
|
43
43
|
return unless current_user.present?
|
44
44
|
|
45
45
|
@current_scoped_entity ||= case scoped_entity_strategy
|
46
|
-
when :current_user
|
47
|
-
current_user
|
48
46
|
when :path
|
49
47
|
scoped_entity_class
|
50
48
|
.associated_with(current_user)
|
51
49
|
.from_path_param(request.path_parameters[scoped_entity_param_key])
|
52
50
|
.first! # Raise NotFound if user does not have access to the entity or it does not exist
|
51
|
+
when Symbol
|
52
|
+
send scoped_entity_strategy
|
53
53
|
else
|
54
|
-
raise NotImplementedError, "unknown scoped entity strategy: #{scoped_entity_strategy}"
|
54
|
+
raise NotImplementedError, "unknown scoped entity strategy: #{scoped_entity_strategy.inspect}"
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
using Plutonium::Refinements::ParameterRefinements
|
2
|
+
|
1
3
|
module Plutonium
|
2
4
|
module Core
|
3
5
|
module Controllers
|
@@ -18,7 +20,7 @@ module Plutonium
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def resource_query_params
|
21
|
-
(params[:q]&.to_unsafe_h || {}).with_indifferent_access
|
23
|
+
(params[:q]&.nilify&.to_unsafe_h || {}).with_indifferent_access
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
data/lib/plutonium/icons.rb
CHANGED
@@ -17,7 +17,7 @@ module Plutonium
|
|
17
17
|
path = Plutonium.root.join "app/assets/icons/#{name}.svg"
|
18
18
|
raise "Invalid icon: #{name}" unless File.exist?(path)
|
19
19
|
|
20
|
-
File.read(path).sub("<svg ", "<svg class=\"#{ICON_SIZES[size]}\" ")
|
20
|
+
File.read(path).sub("<svg ", "<svg class=\"#{ICON_SIZES[size]}\" ").html_safe
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/plutonium/pkg/app.rb
CHANGED
@@ -44,7 +44,12 @@ module Plutonium
|
|
44
44
|
}.to_h
|
45
45
|
end
|
46
46
|
|
47
|
+
def draw_custom_routes(&block)
|
48
|
+
@custom_routes_block = block
|
49
|
+
end
|
50
|
+
|
47
51
|
def draw_resource_routes
|
52
|
+
custom_routes_block = @custom_routes_block
|
48
53
|
registered_resources = resource_register
|
49
54
|
scoped_entity_param_key = self.scoped_entity_param_key if scoped_entity_strategy == :path
|
50
55
|
routes.draw do
|
@@ -111,6 +116,7 @@ module Plutonium
|
|
111
116
|
scope_options = scoped_entity_param_key.present? ? {as: scoped_entity_param_key} : {}
|
112
117
|
|
113
118
|
scope scope_name, scope_options do
|
119
|
+
instance_exec(&custom_routes_block) if custom_routes_block.present?
|
114
120
|
# we have to reverse sort our resource routes in order to prevent routing conflicts
|
115
121
|
# e.g. /blogs/1 and blogs/comments cause an issue if Blog is registered before Blogs::Comment
|
116
122
|
# attempting to load blogs/comments routes to blogs/:id which fails with a 404 since BlogsController
|
data/lib/plutonium/railtie.rb
CHANGED
@@ -3,6 +3,8 @@ require "view_component"
|
|
3
3
|
module Plutonium
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
config.plutonium = ActiveSupport::OrderedOptions.new
|
6
|
+
config.plutonium.cache_discovery = defined?(Rails.env) && !Rails.env.development?
|
7
|
+
config.plutonium.enable_hotreload = defined?(Rails.env) && Rails.env.development?
|
6
8
|
|
7
9
|
initializer "plutonium.append_assets_path" do |app|
|
8
10
|
config.to_prepare do
|
@@ -10,6 +12,14 @@ module Plutonium
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
15
|
+
initializer "plutonium.load_view_components" do
|
16
|
+
load Plutonium.root.join("app", "views", "components", "base.rb")
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "plutonium.load_initializers" do
|
20
|
+
Dir.glob(Plutonium.root.join("config", "initializers", "**", "*.rb")) { |file| load file }
|
21
|
+
end
|
22
|
+
|
13
23
|
initializer "plutonium.asset_server" do
|
14
24
|
# setup a middleware to serve our assets
|
15
25
|
config.app_middleware.insert_before(
|
@@ -28,5 +38,14 @@ module Plutonium
|
|
28
38
|
initializer "plutonium.view_components_capture_compat" do
|
29
39
|
config.view_component.capture_compatibility_patch_enabled = true
|
30
40
|
end
|
41
|
+
|
42
|
+
rake_tasks do
|
43
|
+
load "tasks/create_rodauth_admin.rake"
|
44
|
+
end
|
45
|
+
|
46
|
+
config.after_initialize do
|
47
|
+
Plutonium::Reloader.start! if Rails.application.config.plutonium.enable_hotreload
|
48
|
+
Plutonium::ZEITWERK_LOADER.eager_load if Rails.env.production?
|
49
|
+
end
|
31
50
|
end
|
32
51
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
# This is a glorified initializer
|
3
|
+
|
4
|
+
module Plutonium
|
5
|
+
class Reloader
|
6
|
+
class << self
|
7
|
+
def start!
|
8
|
+
puts "=> [plutonium] starting reloader"
|
9
|
+
|
10
|
+
@listener&.stop
|
11
|
+
@listener = initialize_listener
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def initialize_listener
|
17
|
+
require "listen"
|
18
|
+
|
19
|
+
reload_paths = gather_reload_paths
|
20
|
+
return unless reload_paths.any?
|
21
|
+
|
22
|
+
listener = Listen.to(*reload_paths, only: /\.rb$/) do |modified, added, removed|
|
23
|
+
handle_file_changes(modified, added, removed)
|
24
|
+
end
|
25
|
+
listener.start
|
26
|
+
listener
|
27
|
+
end
|
28
|
+
|
29
|
+
def gather_reload_paths
|
30
|
+
reload_paths = []
|
31
|
+
|
32
|
+
if Plutonium.development?
|
33
|
+
reload_paths << Plutonium.lib_root.to_s
|
34
|
+
reload_paths << Plutonium.root.join("app", "views", "components").to_s
|
35
|
+
reload_paths << Plutonium.root.join("config", "initializers").to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
packages_dir = Rails.root.join("packages/").to_s
|
39
|
+
reload_paths << packages_dir if File.directory?(packages_dir)
|
40
|
+
|
41
|
+
reload_paths
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_file_changes(modified, added, removed)
|
45
|
+
(modified + added).each do |file|
|
46
|
+
Plutonium.logger.debug "[plutonium] change detected: #{file}"
|
47
|
+
|
48
|
+
if file == __FILE__
|
49
|
+
reload_file(file)
|
50
|
+
start!
|
51
|
+
elsif file_starts_with_packages_dir?(file)
|
52
|
+
handle_package_file_changes(file, added)
|
53
|
+
else
|
54
|
+
reload_framework_and_file(file)
|
55
|
+
end
|
56
|
+
rescue => e
|
57
|
+
log_reload_failure(file, e)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def file_starts_with_packages_dir?(file)
|
62
|
+
packages_dir = Rails.root.join("packages/").to_s
|
63
|
+
file.starts_with?(packages_dir)
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_package_file_changes(file, added)
|
67
|
+
return if added.include?(file)
|
68
|
+
|
69
|
+
case File.basename(file)
|
70
|
+
when "engine.rb"
|
71
|
+
reload_engine_and_routes(file)
|
72
|
+
else
|
73
|
+
# Non-engine package files are reloaded by Rails automatically
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def reload_engine_and_routes(file)
|
78
|
+
Plutonium.logger.debug "[plutonium] reloading #{file}"
|
79
|
+
load file
|
80
|
+
Rails.application.reload_routes!
|
81
|
+
end
|
82
|
+
|
83
|
+
def reload_framework_and_file(file)
|
84
|
+
Plutonium.logger.debug "[plutonium] reloading framework"
|
85
|
+
Plutonium::ZEITWERK_LOADER.reload
|
86
|
+
load Plutonium.root.join("app", "views", "components", "base.rb")
|
87
|
+
load file # Ensure files that do not contain constants are loaded
|
88
|
+
end
|
89
|
+
|
90
|
+
def reload_file(file)
|
91
|
+
load file
|
92
|
+
end
|
93
|
+
|
94
|
+
def log_reload_failure(file, error)
|
95
|
+
Plutonium.logger.error "\n[plutonium] reload failed #{file}\n\n#{error}\n"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -6,6 +6,7 @@ using Plutonium::Refinements::ParameterRefinements
|
|
6
6
|
|
7
7
|
module Plutonium
|
8
8
|
module Resource
|
9
|
+
# Controller module to handle resource actions and concerns
|
9
10
|
module Controller
|
10
11
|
extend ActiveSupport::Concern
|
11
12
|
include Pagy::Backend
|
@@ -17,7 +18,6 @@ module Plutonium
|
|
17
18
|
include Plutonium::Core::Controllers::InteractiveActions
|
18
19
|
|
19
20
|
included do
|
20
|
-
# we use class attribute since we want this value inherited
|
21
21
|
class_attribute :resource_class, instance_writer: false, instance_predicate: false
|
22
22
|
|
23
23
|
# https://github.com/ddnexus/pagy/blob/master/docs/extras/headers.md#headers
|
@@ -27,6 +27,8 @@ module Plutonium
|
|
27
27
|
end
|
28
28
|
|
29
29
|
class_methods do
|
30
|
+
# Sets the resource class for the controller
|
31
|
+
# @param [Class] resource_class The resource class
|
30
32
|
def controller_for(resource_class)
|
31
33
|
self.resource_class = resource_class
|
32
34
|
end
|
@@ -34,6 +36,8 @@ module Plutonium
|
|
34
36
|
|
35
37
|
private
|
36
38
|
|
39
|
+
# Creates a policy context
|
40
|
+
# @return [Plutonium::Resource::PolicyContext] The policy context
|
37
41
|
def policy_context
|
38
42
|
Plutonium::Resource::PolicyContext.new(
|
39
43
|
user: current_user,
|
@@ -41,10 +45,15 @@ module Plutonium
|
|
41
45
|
)
|
42
46
|
end
|
43
47
|
|
48
|
+
# Returns the resource record based on path parameters
|
49
|
+
# @return [ActiveRecord::Base, nil] The resource record
|
44
50
|
def resource_record
|
45
|
-
@resource_record ||=
|
51
|
+
@resource_record ||= policy_scope(resource_class).from_path_param(params[:id]).first! if params[:id].present?
|
52
|
+
@resource_record
|
46
53
|
end
|
47
54
|
|
55
|
+
# Returns the submitted resource parameters
|
56
|
+
# @return [Hash] The submitted resource parameters
|
48
57
|
def submitted_resource_params
|
49
58
|
@submitted_resource_params ||= begin
|
50
59
|
strong_parameters = resource_class.strong_parameters_for(*permitted_attributes)
|
@@ -52,24 +61,25 @@ module Plutonium
|
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
64
|
+
# Returns the resource parameters, including scoped and parent parameters
|
65
|
+
# @return [Hash] The resource parameters
|
55
66
|
def resource_params
|
56
67
|
input_params = submitted_resource_params.dup
|
57
68
|
|
58
|
-
|
59
|
-
input_params
|
60
|
-
input_params[:"#{scoped_entity_param_key}_id"] = current_scoped_entity.id if scoped_to_entity?
|
61
|
-
# Override any parent params
|
62
|
-
input_params[parent_input_param] = current_parent if current_parent.present?
|
63
|
-
input_params[:"#{parent_input_param}_id"] = current_parent.id if current_parent.present?
|
69
|
+
override_entity_scoping_params(input_params)
|
70
|
+
override_parent_params(input_params)
|
64
71
|
|
65
|
-
# additionally filter our input_params through our inputs
|
66
72
|
current_presenter.defined_inputs_for(*permitted_attributes).collect_all(input_params)
|
67
73
|
end
|
68
74
|
|
75
|
+
# Returns the resource parameter key
|
76
|
+
# @return [Symbol] The resource parameter key
|
69
77
|
def resource_param_key
|
70
78
|
resource_class.model_name.singular_route_key
|
71
79
|
end
|
72
80
|
|
81
|
+
# Creates a resource context
|
82
|
+
# @return [Plutonium::Resource::Context] The resource context
|
73
83
|
def resource_context
|
74
84
|
Plutonium::Resource::Context.new(
|
75
85
|
resource_class:,
|
@@ -78,29 +88,32 @@ module Plutonium
|
|
78
88
|
)
|
79
89
|
end
|
80
90
|
|
91
|
+
# Creates a resource presenter
|
92
|
+
# @param [Class] resource_class The resource class
|
93
|
+
# @param [ActiveRecord::Base] resource_record The resource record
|
94
|
+
# @return [Object] The resource presenter
|
81
95
|
def resource_presenter(resource_class, resource_record)
|
82
96
|
presenter_class = "#{current_package}::#{resource_class}Presenter".constantize
|
83
97
|
presenter_class.new resource_context, resource_record
|
84
98
|
end
|
85
99
|
|
100
|
+
# Creates a resource query object
|
101
|
+
# @param [Class] resource_class The resource class
|
102
|
+
# @param [ActionController::Parameters] params The request parameters
|
103
|
+
# @return [Object] The resource query object
|
86
104
|
def resource_query_object(resource_class, params)
|
87
105
|
query_object_class = "#{current_package}::#{resource_class}QueryObject".constantize
|
88
106
|
query_object_class.new resource_context, params
|
89
107
|
end
|
90
108
|
|
91
|
-
#
|
109
|
+
# Applies submitted resource params if they have been passed
|
92
110
|
def maybe_apply_submitted_resource_params!
|
93
|
-
|
94
|
-
# we need to ensure that this is being called from get because
|
95
|
-
# it is potentially unsafe since we don't apply the input filter. see #resource_params
|
96
|
-
# would have been nice to be able to, but we can't until we have the presenter, and the presenter
|
97
|
-
# requires the resource_record for our dynamic forms
|
98
|
-
# is this perfect? no. but it works.
|
99
|
-
raise "🚨🚨🚨 this should be called from actions that are not persisting this data" unless request.method == "GET"
|
100
|
-
|
111
|
+
ensure_get_request
|
101
112
|
resource_record.attributes = submitted_resource_params if params[resource_param_key].present?
|
102
113
|
end
|
103
114
|
|
115
|
+
# Returns the current parent based on path parameters
|
116
|
+
# @return [ActiveRecord::Base, nil] The current parent
|
104
117
|
def current_parent
|
105
118
|
return unless parent_route_param.present?
|
106
119
|
|
@@ -113,22 +126,49 @@ module Plutonium
|
|
113
126
|
end
|
114
127
|
end
|
115
128
|
|
129
|
+
# Returns the parent route parameter
|
130
|
+
# @return [Symbol, nil] The parent route parameter
|
116
131
|
def parent_route_param
|
117
132
|
@parent_route_param ||= request.path_parameters.keys.reverse.find { |key| /_id$/.match? key }
|
118
133
|
end
|
119
134
|
|
135
|
+
# Returns the parent input parameter
|
136
|
+
# @return [Symbol, nil] The parent input parameter
|
120
137
|
def parent_input_param
|
121
138
|
return unless current_parent.present?
|
122
139
|
|
123
140
|
resource_class.reflect_on_all_associations(:belongs_to).find { |assoc| assoc.klass.name == current_parent.class.name }&.name&.to_sym
|
124
141
|
end
|
125
142
|
|
126
|
-
|
143
|
+
# Ensures the method is a GET request
|
144
|
+
def ensure_get_request
|
145
|
+
unless request.method == "GET"
|
146
|
+
raise "🚨🚨🚨 This should be called from actions that are not persisting this data"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Overrides entity scoping parameters
|
151
|
+
# @param [Hash] input_params The input parameters
|
152
|
+
def override_entity_scoping_params(input_params)
|
153
|
+
if scoped_to_entity?
|
154
|
+
input_params[scoped_entity_param_key] = current_scoped_entity
|
155
|
+
input_params[:"#{scoped_entity_param_key}_id"] = current_scoped_entity.id
|
156
|
+
end
|
157
|
+
end
|
127
158
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
|
159
|
+
# Overrides parent parameters
|
160
|
+
# @param [Hash] input_params The input parameters
|
161
|
+
def override_parent_params(input_params)
|
162
|
+
if current_parent.present?
|
163
|
+
input_params[parent_input_param] = current_parent
|
164
|
+
input_params[:"#{parent_input_param}_id"] = current_parent.id
|
165
|
+
end
|
166
|
+
end
|
131
167
|
|
168
|
+
# Constructs resource URL arguments
|
169
|
+
# @param [Array] args The URL arguments
|
170
|
+
# @param [Hash] kwargs The keyword arguments
|
171
|
+
# @return [Array] The URL arguments
|
132
172
|
def resource_url_args_for(*args, **kwargs)
|
133
173
|
kwargs[:parent] = current_parent unless kwargs.key?(:parent)
|
134
174
|
super(*args, **kwargs)
|