plutonium 0.18.5 → 0.18.7
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/app/assets/plutonium.js +1 -1
 - data/app/assets/plutonium.js.map +2 -2
 - data/app/assets/plutonium.min.js +1 -1
 - data/app/assets/plutonium.min.js.map +2 -2
 - data/app/views/resource/interactive_bulk_action.html.erb +1 -1
 - data/app/views/resource/interactive_resource_action.html.erb +1 -1
 - data/lib/generators/pu/eject/layout/layout_generator.rb +1 -2
 - data/lib/generators/pu/eject/shell/shell_generator.rb +1 -3
 - data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +4 -0
 - data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +80 -0
 - data/lib/generators/pu/lib/plutonium_generators/concerns/resource_selector.rb +48 -0
 - data/lib/generators/pu/lib/plutonium_generators/generator.rb +2 -42
 - data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +1 -4
 - data/lib/generators/pu/res/conn/conn_generator.rb +9 -21
 - data/lib/generators/pu/res/scaffold/scaffold_generator.rb +10 -5
 - data/lib/plutonium/models/has_cents.rb +4 -2
 - data/lib/plutonium/resource/controller.rb +3 -3
 - data/lib/plutonium/resource/controllers/authorizable.rb +1 -1
 - data/lib/plutonium/resource/controllers/crud_actions.rb +17 -17
 - data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
 - data/lib/plutonium/resource/controllers/presentable.rb +2 -2
 - data/lib/plutonium/resource/record/associated_with.rb +83 -0
 - data/lib/plutonium/resource/record/associations.rb +92 -0
 - data/lib/plutonium/resource/record/field_names.rb +83 -0
 - data/lib/plutonium/resource/record/labeling.rb +19 -0
 - data/lib/plutonium/resource/record/routes.rb +66 -0
 - data/lib/plutonium/resource/record.rb +6 -258
 - data/lib/plutonium/ui/breadcrumbs.rb +4 -4
 - data/lib/plutonium/ui/component/methods.rb +4 -12
 - data/lib/plutonium/ui/form/base.rb +22 -10
 - data/lib/plutonium/ui/form/components/secure_association.rb +112 -0
 - data/lib/plutonium/ui/form/components/secure_polymorphic_association.rb +54 -0
 - data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +0 -1
 - data/lib/plutonium/ui/form/theme.rb +12 -1
 - data/lib/plutonium/ui/page/show.rb +1 -1
 - data/lib/plutonium/ui/page_header.rb +1 -1
 - data/lib/plutonium/version.rb +1 -1
 - data/package-lock.json +2 -2
 - data/package.json +1 -1
 - data/src/js/controllers/slim_select_controller.js +3 -1
 - metadata +11 -4
 - data/lib/plutonium/ui/form/components/belongs_to.rb +0 -66
 - data/lib/plutonium/ui/form/components/has_many.rb +0 -66
 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            <%= render_component :breadcrumbs, resource_class:, parent: current_parent, resource: resource_record %>
         
     | 
| 
      
 1 
     | 
    
         
            +
            <%= render_component :breadcrumbs, resource_class:, parent: current_parent, resource: resource_record! %>
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            <%= render_component :dyna_frame_content do %>
         
     | 
| 
       4 
4 
     | 
    
         
             
              <%= render "interactive_action_form", interactive_action: current_interactive_action %>
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            <%= render_component :breadcrumbs, resource_class:, parent: current_parent, resource: resource_record %>
         
     | 
| 
      
 1 
     | 
    
         
            +
            <%= render_component :breadcrumbs, resource_class:, parent: current_parent, resource: resource_record! %>
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            <%= render_component :dyna_frame_content do %>
         
     | 
| 
       4 
4 
     | 
    
         
             
              <%= render "interactive_action_form", interactive_action: current_interactive_action %>
         
     | 
| 
         @@ -11,7 +11,6 @@ module Pu 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  desc "Eject layout views into your own project"
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                  class_option :dest, type: :string
         
     | 
| 
       15 
14 
     | 
    
         
             
                  class_option :rodauth, type: :boolean
         
     | 
| 
       16 
15 
     | 
    
         | 
| 
       17 
16 
     | 
    
         
             
                  def start
         
     | 
| 
         @@ -28,7 +27,7 @@ module Pu 
     | 
|
| 
       28 
27 
     | 
    
         
             
                  private
         
     | 
| 
       29 
28 
     | 
    
         | 
| 
       30 
29 
     | 
    
         
             
                  def destination_portal
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
      
 30 
     | 
    
         
            +
                    portal_option(:dest, prompt: "Select destination portal")
         
     | 
| 
       32 
31 
     | 
    
         
             
                  end
         
     | 
| 
       33 
32 
     | 
    
         | 
| 
       34 
33 
     | 
    
         
             
                  def copy_file(source_path, destination_path)
         
     | 
| 
         @@ -11,8 +11,6 @@ module Pu 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  desc "Eject layout shell (i.e header, sidebar) into your own project"
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                  class_option :dest, type: :string
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
14 
     | 
    
         
             
                  def start
         
     | 
| 
       17 
15 
     | 
    
         
             
                    destination_dir = (destination_portal == "main_app") ? "app/views/" : "packages/#{destination_portal}/app/views"
         
     | 
| 
       18 
16 
     | 
    
         
             
                    [
         
     | 
| 
         @@ -28,7 +26,7 @@ module Pu 
     | 
|
| 
       28 
26 
     | 
    
         
             
                  private
         
     | 
| 
       29 
27 
     | 
    
         | 
| 
       30 
28 
     | 
    
         
             
                  def destination_portal
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
      
 29 
     | 
    
         
            +
                    portal_option(:dest, prompt: "Select destination portal")
         
     | 
| 
       32 
30 
     | 
    
         
             
                  end
         
     | 
| 
       33 
31 
     | 
    
         | 
| 
       34 
32 
     | 
    
         
             
                  def copy_file(source_path, destination_path)
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module PlutoniumGenerators
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Concerns
         
     | 
| 
      
 5 
     | 
    
         
            +
                module PackageSelector
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.included(base)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    base.send :class_option, :src, type: :string, desc: "The source package if applicable"
         
     | 
| 
      
 8 
     | 
    
         
            +
                    base.send :class_option, :dest, type: :string, desc: "The destination package if applicable"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  private
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def reserved_packages
         
     | 
| 
      
 14 
     | 
    
         
            +
                    %w[core reactor app main plutonium pluton8 plutonate]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def validate_package_name(package_name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    package_name = package_name.underscore
         
     | 
| 
      
 19 
     | 
    
         
            +
                    error("Package name is reserved\n\n#{reserved_packages.join "\n"}") if reserved_packages.include?(package_name)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    error("Package name cannot end in `_app` or `_portal`") if /(_app|_portal)$/i.match?(package_name)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def available_packages
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @available_packages ||= begin
         
     | 
| 
      
 25 
     | 
    
         
            +
                      packages = Dir["packages/*"].map { |dir| dir.gsub "packages/", "" }
         
     | 
| 
      
 26 
     | 
    
         
            +
                      packages - reserved_packages
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def available_portals
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @available_portals ||= ["main_app"] + available_packages.select { |pkg| pkg.ends_with?("_app") || pkg.ends_with?("_portal") }.sort
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def available_features
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @available_features ||= ["main_app"] + available_packages.select { |pkg| !(pkg.ends_with?("_app") || pkg.ends_with?("_portal")) }.sort
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def select_package(selected_package = nil, msg: "Select package", pkgs: nil)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    pkgs ||= available_packages
         
     | 
| 
      
 40 
     | 
    
         
            +
                    if pkgs.include?(selected_package)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      selected_package
         
     | 
| 
      
 42 
     | 
    
         
            +
                    else
         
     | 
| 
      
 43 
     | 
    
         
            +
                      prompt.select(msg, pkgs)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def select_feature(selected_package = nil, msg: "Select feature")
         
     | 
| 
      
 48 
     | 
    
         
            +
                    select_package(selected_package, msg: msg, pkgs: available_features)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def feature_option(name, prompt: nil, option_key: nil)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    # Get stored value or command line option
         
     | 
| 
      
 53 
     | 
    
         
            +
                    ivar = :"@#{name}_feature_option"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return instance_variable_get(ivar) if instance_variable_defined?(ivar)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    # Validate option or prompt user
         
     | 
| 
      
 57 
     | 
    
         
            +
                    option_key ||= name
         
     | 
| 
      
 58 
     | 
    
         
            +
                    value = select_feature(options[option_key], msg: prompt || "Select #{name} feature")
         
     | 
| 
      
 59 
     | 
    
         
            +
                    instance_variable_set(ivar, value)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    value
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def select_portal(selected_package = nil, msg: "Select portal")
         
     | 
| 
      
 64 
     | 
    
         
            +
                    select_package(selected_package, msg: msg, pkgs: available_portals)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def portal_option(name, prompt: nil, option_key: nil)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    # Get stored value or command line option
         
     | 
| 
      
 69 
     | 
    
         
            +
                    ivar = :"@#{name}_portal_option"
         
     | 
| 
      
 70 
     | 
    
         
            +
                    return instance_variable_get(ivar) if instance_variable_defined?(ivar)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    # Validate option or prompt user
         
     | 
| 
      
 73 
     | 
    
         
            +
                    option_key ||= name
         
     | 
| 
      
 74 
     | 
    
         
            +
                    value = select_portal(options[option_key], msg: prompt || "Select #{name} portal")
         
     | 
| 
      
 75 
     | 
    
         
            +
                    instance_variable_set(ivar, value)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    value
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module PlutoniumGenerators
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Concerns
         
     | 
| 
      
 5 
     | 
    
         
            +
                module ResourceSelector
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.included(base)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    # base.send :class_option, :resources, type: :array, desc: "List of resource model names if applicable"
         
     | 
| 
      
 8 
     | 
    
         
            +
                    base.send :argument, :resources, type: :array, optional: true, default: [],
         
     | 
| 
      
 9 
     | 
    
         
            +
                      desc: "List of model names if applicable"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  private
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def available_resources(source_module)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    Plutonium.eager_load_rails!
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    source_module.constantize.descendants.reject do |resource_klass|
         
     | 
| 
      
 18 
     | 
    
         
            +
                      next true if resource_klass.abstract_class?
         
     | 
| 
      
 19 
     | 
    
         
            +
                      next true if source_module == "ApplicationRecord" &&
         
     | 
| 
      
 20 
     | 
    
         
            +
                        resource_klass.ancestors.any? { |ancestor| ancestor.to_s.end_with?("::ResourceRecord") }
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end.map(&:to_s).sort
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def select_resources(source_module, prompt: "Select resources")
         
     | 
| 
      
 25 
     | 
    
         
            +
                    resources = available_resources(source_module)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    error "No resources found" if resources.blank?
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    self.prompt.multi_select(prompt, resources)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def resources_selection(prompt: nil)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ivar = :@resources_selection
         
     | 
| 
      
 33 
     | 
    
         
            +
                    return instance_variable_get(ivar) if instance_variable_defined?(ivar)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    # Convert comma-separated string to array if from command line
         
     | 
| 
      
 36 
     | 
    
         
            +
                    value = resources.map(&:classify)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    if value.empty?
         
     | 
| 
      
 38 
     | 
    
         
            +
                      source_feature = feature_option :src, prompt: "Select source feature"
         
     | 
| 
      
 39 
     | 
    
         
            +
                      source_module = (source_feature == "main_app") ? "ApplicationRecord" : "#{source_feature.camelize}::ResourceRecord"
         
     | 
| 
      
 40 
     | 
    
         
            +
                      value = select_resources(source_module, prompt: prompt || "Select #{source_module} resources")
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    instance_variable_set(ivar, value)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    value
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -14,51 +14,11 @@ module PlutoniumGenerators 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  base.send :class_option, :interactive, type: :boolean, desc: "Show prompts. Default: true"
         
     | 
| 
       15 
15 
     | 
    
         
             
                  base.send :class_option, :bundle, type: :boolean, desc: "Run bundle after setup. Default: true"
         
     | 
| 
       16 
16 
     | 
    
         
             
                  base.send :class_option, :lint, type: :boolean, desc: "Run linter after generation. Default: false"
         
     | 
| 
       17 
     | 
    
         
            -
                end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                protected
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                def reserved_packages
         
     | 
| 
       22 
     | 
    
         
            -
                  %w[core reactor app main plutonium pluton8 plutonate]
         
     | 
| 
       23 
     | 
    
         
            -
                end
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                def validate_package_name(package_name)
         
     | 
| 
       26 
     | 
    
         
            -
                  package_name = package_name.underscore
         
     | 
| 
       27 
     | 
    
         
            -
                  error("Package name is reserved\n\n#{reserved_packages.join "\n"}") if reserved_packages.include?(package_name)
         
     | 
| 
       28 
     | 
    
         
            -
                  error("Package name cannot end in `_app` or `_portal`") if /(_app|_portal)$/i.match?(package_name)
         
     | 
| 
       29 
     | 
    
         
            -
                end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                def available_packages
         
     | 
| 
       32 
     | 
    
         
            -
                  @available_packages ||= begin
         
     | 
| 
       33 
     | 
    
         
            -
                    packages = Dir["packages/*"].map { |dir| dir.gsub "packages/", "" }
         
     | 
| 
       34 
     | 
    
         
            -
                    packages - reserved_packages
         
     | 
| 
       35 
     | 
    
         
            -
                  end
         
     | 
| 
       36 
     | 
    
         
            -
                end
         
     | 
| 
       37 
17 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                  @available_apps ||= ["main_app"] + available_packages.select { |pkg| pkg.ends_with?("_app") || pkg.ends_with?("_portal") }.sort
         
     | 
| 
      
 18 
     | 
    
         
            +
                  base.include Concerns::PackageSelector
         
     | 
| 
       40 
19 
     | 
    
         
             
                end
         
     | 
| 
       41 
20 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                 
     | 
| 
       43 
     | 
    
         
            -
                  @available_features ||= ["main_app"] + available_packages.select { |pkg| !(pkg.ends_with?("_app") || pkg.ends_with?("_portal")) }.sort
         
     | 
| 
       44 
     | 
    
         
            -
                end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                def select_package(selected_package = nil, msg: "Select package", pkgs: nil)
         
     | 
| 
       47 
     | 
    
         
            -
                  pkgs ||= available_packages
         
     | 
| 
       48 
     | 
    
         
            -
                  if pkgs.include?(selected_package)
         
     | 
| 
       49 
     | 
    
         
            -
                    selected_package
         
     | 
| 
       50 
     | 
    
         
            -
                  else
         
     | 
| 
       51 
     | 
    
         
            -
                    prompt.select(msg, pkgs)
         
     | 
| 
       52 
     | 
    
         
            -
                  end
         
     | 
| 
       53 
     | 
    
         
            -
                end
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                def select_portal(selected_package = nil, msg: "Select portal")
         
     | 
| 
       56 
     | 
    
         
            -
                  select_package(selected_package, msg: msg, pkgs: available_apps)
         
     | 
| 
       57 
     | 
    
         
            -
                end
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                def select_feature(selected_package = nil, msg: "Select feature")
         
     | 
| 
       60 
     | 
    
         
            -
                  select_package(selected_package, msg: msg, pkgs: available_features)
         
     | 
| 
       61 
     | 
    
         
            -
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                protected
         
     | 
| 
       62 
22 
     | 
    
         | 
| 
       63 
23 
     | 
    
         
             
                # ####################
         
     | 
| 
       64 
24 
     | 
    
         | 
| 
         @@ -13,8 +13,6 @@ module PlutoniumGenerators 
     | 
|
| 
       13 
13 
     | 
    
         
             
                remove_task :create_module_file
         
     | 
| 
       14 
14 
     | 
    
         
             
                # remove_task :check_class_collision
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                class_option :dest, type: :string
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
16 
     | 
    
         
             
                # def check_class_collision # :doc:
         
     | 
| 
       19 
17 
     | 
    
         
             
                #   class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
         
     | 
| 
       20 
18 
     | 
    
         
             
                # end
         
     | 
| 
         @@ -41,7 +39,6 @@ module PlutoniumGenerators 
     | 
|
| 
       41 
39 
     | 
    
         
             
                def name
         
     | 
| 
       42 
40 
     | 
    
         
             
                  @pu_name ||= begin
         
     | 
| 
       43 
41 
     | 
    
         
             
                    @original_name = @name
         
     | 
| 
       44 
     | 
    
         
            -
                    @selected_destination_feature = select_feature selected_destination_feature, msg: "Select destination feature"
         
     | 
| 
       45 
42 
     | 
    
         
             
                    @name = [main_app? ? nil : selected_destination_feature.underscore, super.singularize.underscore].compact.join "/"
         
     | 
| 
       46 
43 
     | 
    
         
             
                    set_destination_root!
         
     | 
| 
       47 
44 
     | 
    
         
             
                    @name
         
     | 
| 
         @@ -57,7 +54,7 @@ module PlutoniumGenerators 
     | 
|
| 
       57 
54 
     | 
    
         
             
                end
         
     | 
| 
       58 
55 
     | 
    
         | 
| 
       59 
56 
     | 
    
         
             
                def selected_destination_feature
         
     | 
| 
       60 
     | 
    
         
            -
                   
     | 
| 
      
 57 
     | 
    
         
            +
                  feature_option :dest, prompt: "Select destination feature"
         
     | 
| 
       61 
58 
     | 
    
         
             
                end
         
     | 
| 
       62 
59 
     | 
    
         | 
| 
       63 
60 
     | 
    
         
             
                def set_destination_root!
         
     | 
| 
         @@ -6,26 +6,20 @@ module Pu 
     | 
|
| 
       6 
6 
     | 
    
         
             
              module Res
         
     | 
| 
       7 
7 
     | 
    
         
             
                class ConnGenerator < Rails::Generators::Base
         
     | 
| 
       8 
8 
     | 
    
         
             
                  include PlutoniumGenerators::Generator
         
     | 
| 
      
 9 
     | 
    
         
            +
                  include PlutoniumGenerators::Concerns::ResourceSelector
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
11 
     | 
    
         
             
                  source_root File.expand_path("templates", __dir__)
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                  desc 
     | 
| 
      
 13 
     | 
    
         
            +
                  desc(
         
     | 
| 
      
 14 
     | 
    
         
            +
                    "Create a connection between a resource and a portal\n\n" \
         
     | 
| 
      
 15 
     | 
    
         
            +
                    "e.g. rails g pu:res:conn todo --dest=dashboard_portal"
         
     | 
| 
      
 16 
     | 
    
         
            +
                  )
         
     | 
| 
       13 
17 
     | 
    
         | 
| 
       14 
18 
     | 
    
         
             
                  # argument :name
         
     | 
| 
       15 
19 
     | 
    
         | 
| 
       16 
20 
     | 
    
         
             
                  def start
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                    Plutonium.eager_load_rails!
         
     | 
| 
       21 
     | 
    
         
            -
                    available_resources = source_module.constantize.descendants.reject do |model|
         
     | 
| 
       22 
     | 
    
         
            -
                      next true if model.abstract_class?
         
     | 
| 
       23 
     | 
    
         
            -
                      next true if source_module == "ApplicationRecord" && model.ancestors.any? { |ancestor| ancestor.to_s.end_with?("::ResourceRecord") }
         
     | 
| 
       24 
     | 
    
         
            -
                    end.map(&:to_s).sort
         
     | 
| 
       25 
     | 
    
         
            -
                    error "No resources found" if available_resources.blank?
         
     | 
| 
       26 
     | 
    
         
            -
                    selected_resources = prompt.multi_select("Select resources", available_resources)
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                    @app_namespace = select_portal.camelize
         
     | 
| 
      
 21 
     | 
    
         
            +
                    selected_resources = resources_selection
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @app_namespace = portal_option(:dest, prompt: "Select destination portal").camelize
         
     | 
| 
       29 
23 
     | 
    
         | 
| 
       30 
24 
     | 
    
         
             
                    selected_resources.each do |resource|
         
     | 
| 
       31 
25 
     | 
    
         
             
                      @resource_class = resource
         
     | 
| 
         @@ -97,13 +91,7 @@ module Pu 
     | 
|
| 
       97 
91 
     | 
    
         | 
| 
       98 
92 
     | 
    
         
             
                  def attributes
         
     | 
| 
       99 
93 
     | 
    
         
             
                    resource_klass = resource_class.constantize
         
     | 
| 
       100 
     | 
    
         
            -
                    unwanted_attrs = [
         
     | 
| 
       101 
     | 
    
         
            -
                      resource_klass.primary_key.to_sym, # primary_key
         
     | 
| 
       102 
     | 
    
         
            -
                      :created_at, :updated_at # timestamps
         
     | 
| 
       103 
     | 
    
         
            -
                    ]
         
     | 
| 
       104 
94 
     | 
    
         
             
                    resource_klass.content_columns.filter_map { |col|
         
     | 
| 
       105 
     | 
    
         
            -
                      next if unwanted_attrs.include? col.name.to_sym
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
95 
     | 
    
         
             
                      PlutoniumGenerators::ModelGeneratorBase::GeneratedAttribute.parse resource_class, "#{col.name}:#{col.type}"
         
     | 
| 
       108 
96 
     | 
    
         
             
                    }
         
     | 
| 
       109 
97 
     | 
    
         
             
                  rescue ActiveRecord::StatementInvalid
         
     | 
| 
         @@ -116,11 +104,11 @@ module Pu 
     | 
|
| 
       116 
104 
     | 
    
         
             
                  end
         
     | 
| 
       117 
105 
     | 
    
         | 
| 
       118 
106 
     | 
    
         
             
                  def policy_attributes_for_create
         
     | 
| 
       119 
     | 
    
         
            -
                    default_policy_attributes
         
     | 
| 
      
 107 
     | 
    
         
            +
                    default_policy_attributes - [:created_at, :updated_at]
         
     | 
| 
       120 
108 
     | 
    
         
             
                  end
         
     | 
| 
       121 
109 
     | 
    
         | 
| 
       122 
110 
     | 
    
         
             
                  def policy_attributes_for_read
         
     | 
| 
       123 
     | 
    
         
            -
                    default_policy_attributes 
     | 
| 
      
 111 
     | 
    
         
            +
                    default_policy_attributes
         
     | 
| 
       124 
112 
     | 
    
         
             
                  end
         
     | 
| 
       125 
113 
     | 
    
         
             
                end
         
     | 
| 
       126 
114 
     | 
    
         
             
              end
         
     | 
| 
         @@ -4,6 +4,7 @@ require_relative "../../lib/plutonium_generators" 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module Pu
         
     | 
| 
       6 
6 
     | 
    
         
             
              module Res
         
     | 
| 
      
 7 
     | 
    
         
            +
                # run `rails g pu:res:scaffold existing_resource` without any arguments to import an existing resource
         
     | 
| 
       7 
8 
     | 
    
         
             
                class ScaffoldGenerator < PlutoniumGenerators::ModelGeneratorBase
         
     | 
| 
       8 
9 
     | 
    
         
             
                  include PlutoniumGenerators::Generator
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
         @@ -17,9 +18,13 @@ module Pu 
     | 
|
| 
       17 
18 
     | 
    
         
             
                    return unless options[:model]
         
     | 
| 
       18 
19 
     | 
    
         | 
| 
       19 
20 
     | 
    
         
             
                    model_class = class_name.safe_constantize
         
     | 
| 
       20 
     | 
    
         
            -
                    if model_class.present? 
     | 
| 
       21 
     | 
    
         
            -
                       
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                    if model_class.present?
         
     | 
| 
      
 22 
     | 
    
         
            +
                      if attributes.empty?
         
     | 
| 
      
 23 
     | 
    
         
            +
                        attributes_str = model_class.content_columns.map { |col| "#{col.name}:#{col.type}" }
         
     | 
| 
      
 24 
     | 
    
         
            +
                        self.attributes = parse_attributes_internal!(attributes_str)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      else
         
     | 
| 
      
 26 
     | 
    
         
            +
                        warn("Overwriting existing resource. You can leave out the attributes to import an existing resource.")
         
     | 
| 
      
 27 
     | 
    
         
            +
                      end
         
     | 
| 
       23 
28 
     | 
    
         
             
                    end
         
     | 
| 
       24 
29 
     | 
    
         
             
                  end
         
     | 
| 
       25 
30 
     | 
    
         | 
| 
         @@ -56,11 +61,11 @@ module Pu 
     | 
|
| 
       56 
61 
     | 
    
         
             
                  end
         
     | 
| 
       57 
62 
     | 
    
         | 
| 
       58 
63 
     | 
    
         
             
                  def policy_attributes_for_create
         
     | 
| 
       59 
     | 
    
         
            -
                    default_policy_attributes
         
     | 
| 
      
 64 
     | 
    
         
            +
                    default_policy_attributes - [:created_at, :updated_at]
         
     | 
| 
       60 
65 
     | 
    
         
             
                  end
         
     | 
| 
       61 
66 
     | 
    
         | 
| 
       62 
67 
     | 
    
         
             
                  def policy_attributes_for_read
         
     | 
| 
       63 
     | 
    
         
            -
                    default_policy_attributes 
     | 
| 
      
 68 
     | 
    
         
            +
                    default_policy_attributes
         
     | 
| 
       64 
69 
     | 
    
         
             
                  end
         
     | 
| 
       65 
70 
     | 
    
         
             
                end
         
     | 
| 
       66 
71 
     | 
    
         
             
              end
         
     | 
| 
         @@ -143,8 +143,10 @@ module Plutonium 
     | 
|
| 
       143 
143 
     | 
    
         
             
                        #
         
     | 
| 
       144 
144 
     | 
    
         
             
                        # @param value [Numeric, nil] The decimal value to be set.
         
     | 
| 
       145 
145 
     | 
    
         
             
                        def #{name}=(value)
         
     | 
| 
       146 
     | 
    
         
            -
                          self.#{cents_name} =  
     | 
| 
       147 
     | 
    
         
            -
                            (BigDecimal(value.to_s) * #{rate}).to_i
         
     | 
| 
      
 146 
     | 
    
         
            +
                          self.#{cents_name} = begin
         
     | 
| 
      
 147 
     | 
    
         
            +
                            (BigDecimal(value.to_s) * #{rate}).to_i if value.present?
         
     | 
| 
      
 148 
     | 
    
         
            +
                          rescue ArgumentError
         
     | 
| 
      
 149 
     | 
    
         
            +
                            nil
         
     | 
| 
       148 
150 
     | 
    
         
             
                          end
         
     | 
| 
       149 
151 
     | 
    
         
             
                        end
         
     | 
| 
       150 
152 
     | 
    
         | 
| 
         @@ -19,7 +19,7 @@ module Plutonium 
     | 
|
| 
       19 
19 
     | 
    
         
             
                    # https://github.com/ddnexus/pagy/blob/master/docs/extras/headers.md#headers
         
     | 
| 
       20 
20 
     | 
    
         
             
                    after_action { pagy_headers_merge(@pagy) if @pagy }
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                    helper_method :current_parent, :resource_record 
     | 
| 
      
 22 
     | 
    
         
            +
                    helper_method :current_parent, :resource_record!, :resource_record?, :resource_param_key, :resource_class
         
     | 
| 
       23 
23 
     | 
    
         
             
                  end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
                  class_methods do
         
     | 
| 
         @@ -62,7 +62,7 @@ module Plutonium 
     | 
|
| 
       62 
62 
     | 
    
         
             
                    end
         
     | 
| 
       63 
63 
     | 
    
         
             
                  end
         
     | 
| 
       64 
64 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                  def resource_record
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def resource_record!
         
     | 
| 
       66 
66 
     | 
    
         
             
                    @resource_record ||= resource_record_relation.first!
         
     | 
| 
       67 
67 
     | 
    
         
             
                  end
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
         @@ -117,7 +117,7 @@ module Plutonium 
     | 
|
| 
       117 
117 
     | 
    
         
             
                  # Applies submitted resource params if they have been passed
         
     | 
| 
       118 
118 
     | 
    
         
             
                  def maybe_apply_submitted_resource_params!
         
     | 
| 
       119 
119 
     | 
    
         
             
                    ensure_get_request
         
     | 
| 
       120 
     | 
    
         
            -
                    resource_record 
     | 
| 
      
 120 
     | 
    
         
            +
                    resource_record!.attributes = submitted_resource_params if params[resource_param_key]
         
     | 
| 
       121 
121 
     | 
    
         
             
                  end
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
123 
     | 
    
         
             
                  # Returns the current parent based on path parameters
         
     | 
| 
         @@ -12,7 +12,7 @@ module Plutonium 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  #     include Plutonium::Resource::Controllers::Authorizable
         
     | 
| 
       13 
13 
     | 
    
         
             
                  #   end
         
     | 
| 
       14 
14 
     | 
    
         
             
                  #
         
     | 
| 
       15 
     | 
    
         
            -
                  # @note This module assumes the existence of methods like `resource_record 
     | 
| 
      
 15 
     | 
    
         
            +
                  # @note This module assumes the existence of methods like `resource_record!`,
         
     | 
| 
       16 
16 
     | 
    
         
             
                  #   `resource_class`, `current_parent`, and `entity_scope_for_authorize`.
         
     | 
| 
       17 
17 
     | 
    
         
             
                  #
         
     | 
| 
       18 
18 
     | 
    
         
             
                  # @see ActionPolicy
         
     | 
| 
         @@ -21,8 +21,8 @@ module Plutonium 
     | 
|
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                    # GET /resources/1(.{format})
         
     | 
| 
       23 
23 
     | 
    
         
             
                    def show
         
     | 
| 
       24 
     | 
    
         
            -
                      authorize_current! resource_record
         
     | 
| 
       25 
     | 
    
         
            -
                      set_page_title resource_record 
     | 
| 
      
 24 
     | 
    
         
            +
                      authorize_current! resource_record!
         
     | 
| 
      
 25 
     | 
    
         
            +
                      set_page_title resource_record!.to_label.titleize
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                      render :show
         
     | 
| 
       28 
28 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -46,7 +46,7 @@ module Plutonium 
     | 
|
| 
       46 
46 
     | 
    
         
             
                      @resource_record = resource_class.new resource_params
         
     | 
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         
             
                      respond_to do |format|
         
     | 
| 
       49 
     | 
    
         
            -
                        if resource_record 
     | 
| 
      
 49 
     | 
    
         
            +
                        if resource_record!.save
         
     | 
| 
       50 
50 
     | 
    
         
             
                          format.html do
         
     | 
| 
       51 
51 
     | 
    
         
             
                            redirect_to redirect_url_after_submit,
         
     | 
| 
       52 
52 
     | 
    
         
             
                              notice: "#{resource_class.model_name.human} was successfully created."
         
     | 
| 
         @@ -57,7 +57,7 @@ module Plutonium 
     | 
|
| 
       57 
57 
     | 
    
         
             
                            render :new, status: :unprocessable_entity
         
     | 
| 
       58 
58 
     | 
    
         
             
                          end
         
     | 
| 
       59 
59 
     | 
    
         
             
                          format.any do
         
     | 
| 
       60 
     | 
    
         
            -
                            @errors = resource_record 
     | 
| 
      
 60 
     | 
    
         
            +
                            @errors = resource_record!.errors
         
     | 
| 
       61 
61 
     | 
    
         
             
                            render "errors", status: :unprocessable_entity
         
     | 
| 
       62 
62 
     | 
    
         
             
                          end
         
     | 
| 
       63 
63 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -66,8 +66,8 @@ module Plutonium 
     | 
|
| 
       66 
66 
     | 
    
         | 
| 
       67 
67 
     | 
    
         
             
                    # GET /resources/1/edit
         
     | 
| 
       68 
68 
     | 
    
         
             
                    def edit
         
     | 
| 
       69 
     | 
    
         
            -
                      authorize_current! resource_record
         
     | 
| 
       70 
     | 
    
         
            -
                      set_page_title "Update #{resource_record 
     | 
| 
      
 69 
     | 
    
         
            +
                      authorize_current! resource_record!
         
     | 
| 
      
 70 
     | 
    
         
            +
                      set_page_title "Update #{resource_record!.to_label.titleize}"
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
       72 
72 
     | 
    
         
             
                      maybe_apply_submitted_resource_params!
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
         @@ -76,11 +76,11 @@ module Plutonium 
     | 
|
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
                    # PATCH/PUT /resources/1(.{format})
         
     | 
| 
       78 
78 
     | 
    
         
             
                    def update
         
     | 
| 
       79 
     | 
    
         
            -
                      authorize_current! resource_record
         
     | 
| 
       80 
     | 
    
         
            -
                      set_page_title "Update #{resource_record 
     | 
| 
      
 79 
     | 
    
         
            +
                      authorize_current! resource_record!
         
     | 
| 
      
 80 
     | 
    
         
            +
                      set_page_title "Update #{resource_record!.to_label.titleize}"
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
82 
     | 
    
         
             
                      respond_to do |format|
         
     | 
| 
       83 
     | 
    
         
            -
                        if resource_record 
     | 
| 
      
 83 
     | 
    
         
            +
                        if resource_record!.update(resource_params)
         
     | 
| 
       84 
84 
     | 
    
         
             
                          format.html do
         
     | 
| 
       85 
85 
     | 
    
         
             
                            redirect_to redirect_url_after_submit, notice: "#{resource_class.model_name.human} was successfully updated.",
         
     | 
| 
       86 
86 
     | 
    
         
             
                              status: :see_other
         
     | 
| 
         @@ -91,7 +91,7 @@ module Plutonium 
     | 
|
| 
       91 
91 
     | 
    
         
             
                            render :edit, status: :unprocessable_entity
         
     | 
| 
       92 
92 
     | 
    
         
             
                          end
         
     | 
| 
       93 
93 
     | 
    
         
             
                          format.any do
         
     | 
| 
       94 
     | 
    
         
            -
                            @errors = resource_record 
     | 
| 
      
 94 
     | 
    
         
            +
                            @errors = resource_record!.errors
         
     | 
| 
       95 
95 
     | 
    
         
             
                            render "errors", status: :unprocessable_entity
         
     | 
| 
       96 
96 
     | 
    
         
             
                          end
         
     | 
| 
       97 
97 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -100,10 +100,10 @@ module Plutonium 
     | 
|
| 
       100 
100 
     | 
    
         | 
| 
       101 
101 
     | 
    
         
             
                    # DELETE /resources/1(.{format})
         
     | 
| 
       102 
102 
     | 
    
         
             
                    def destroy
         
     | 
| 
       103 
     | 
    
         
            -
                      authorize_current! resource_record
         
     | 
| 
      
 103 
     | 
    
         
            +
                      authorize_current! resource_record!
         
     | 
| 
       104 
104 
     | 
    
         | 
| 
       105 
105 
     | 
    
         
             
                      respond_to do |format|
         
     | 
| 
       106 
     | 
    
         
            -
                        resource_record 
     | 
| 
      
 106 
     | 
    
         
            +
                        resource_record!.destroy
         
     | 
| 
       107 
107 
     | 
    
         | 
| 
       108 
108 
     | 
    
         
             
                        format.html do
         
     | 
| 
       109 
109 
     | 
    
         
             
                          redirect_to redirect_url_after_destroy,
         
     | 
| 
         @@ -112,11 +112,11 @@ module Plutonium 
     | 
|
| 
       112 
112 
     | 
    
         
             
                        format.json { head :no_content }
         
     | 
| 
       113 
113 
     | 
    
         
             
                      rescue ActiveRecord::InvalidForeignKey
         
     | 
| 
       114 
114 
     | 
    
         
             
                        format.html do
         
     | 
| 
       115 
     | 
    
         
            -
                          redirect_to resource_url_for(resource_record),
         
     | 
| 
      
 115 
     | 
    
         
            +
                          redirect_to resource_url_for(resource_record!),
         
     | 
| 
       116 
116 
     | 
    
         
             
                            alert: "#{resource_class.model_name.human} is referenced by other records."
         
     | 
| 
       117 
117 
     | 
    
         
             
                        end
         
     | 
| 
       118 
118 
     | 
    
         
             
                        format.any do
         
     | 
| 
       119 
     | 
    
         
            -
                          @errors = ActiveModel::Errors.new resource_record
         
     | 
| 
      
 119 
     | 
    
         
            +
                          @errors = ActiveModel::Errors.new resource_record!
         
     | 
| 
       120 
120 
     | 
    
         
             
                          @errors.add :base, :existing_references, message: "is referenced by other records"
         
     | 
| 
       121 
121 
     | 
    
         | 
| 
       122 
122 
     | 
    
         
             
                          render "errors", status: :unprocessable_entity
         
     | 
| 
         @@ -133,9 +133,9 @@ module Plutonium 
     | 
|
| 
       133 
133 
     | 
    
         | 
| 
       134 
134 
     | 
    
         
             
                      url = case preferred_action_after_submit
         
     | 
| 
       135 
135 
     | 
    
         
             
                      when "show"
         
     | 
| 
       136 
     | 
    
         
            -
                        resource_url_for(resource_record) if current_policy.allowed_to? :show?
         
     | 
| 
      
 136 
     | 
    
         
            +
                        resource_url_for(resource_record!) if current_policy.allowed_to? :show?
         
     | 
| 
       137 
137 
     | 
    
         
             
                      when "edit"
         
     | 
| 
       138 
     | 
    
         
            -
                        resource_url_for(resource_record 
     | 
| 
      
 138 
     | 
    
         
            +
                        resource_url_for(resource_record!, action: :edit) if current_policy.allowed_to? :edit?
         
     | 
| 
       139 
139 
     | 
    
         
             
                      when "new"
         
     | 
| 
       140 
140 
     | 
    
         
             
                        resource_url_for(resource_class, action: :new) if current_policy.allowed_to? :new?
         
     | 
| 
       141 
141 
     | 
    
         
             
                      when "index"
         
     | 
| 
         @@ -144,7 +144,7 @@ module Plutonium 
     | 
|
| 
       144 
144 
     | 
    
         
             
                        # ensure we have a valid value
         
     | 
| 
       145 
145 
     | 
    
         
             
                        session[:action_after_submit_preference] = "show"
         
     | 
| 
       146 
146 
     | 
    
         
             
                      end
         
     | 
| 
       147 
     | 
    
         
            -
                      url || resource_url_for(resource_record)
         
     | 
| 
      
 147 
     | 
    
         
            +
                      url || resource_url_for(resource_record!)
         
     | 
| 
       148 
148 
     | 
    
         
             
                    end
         
     | 
| 
       149 
149 
     | 
    
         | 
| 
       150 
150 
     | 
    
         
             
                    def redirect_url_after_destroy
         
     | 
| 
         @@ -42,7 +42,7 @@ module Plutonium 
     | 
|
| 
       42 
42 
     | 
    
         
             
                      if outcome.success?
         
     | 
| 
       43 
43 
     | 
    
         
             
                        outcome.to_response.process(self) do |value|
         
     | 
| 
       44 
44 
     | 
    
         
             
                          respond_to do |format|
         
     | 
| 
       45 
     | 
    
         
            -
                            return_url = redirect_url_after_action_on(resource_record)
         
     | 
| 
      
 45 
     | 
    
         
            +
                            return_url = redirect_url_after_action_on(resource_record!)
         
     | 
| 
       46 
46 
     | 
    
         
             
                            format.any { redirect_to return_url, status: :see_other }
         
     | 
| 
       47 
47 
     | 
    
         
             
                            if helpers.current_turbo_frame == "modal"
         
     | 
| 
       48 
48 
     | 
    
         
             
                              format.turbo_stream do
         
     | 
| 
         @@ -202,7 +202,7 @@ module Plutonium 
     | 
|
| 
       202 
202 
     | 
    
         | 
| 
       203 
203 
     | 
    
         
             
                    def authorize_interactive_record_action!
         
     | 
| 
       204 
204 
     | 
    
         
             
                      interactive_resource_action = params[:interactive_action]&.to_sym
         
     | 
| 
       205 
     | 
    
         
            -
                      authorize_current! resource_record 
     | 
| 
      
 205 
     | 
    
         
            +
                      authorize_current! resource_record!, to: :"#{interactive_resource_action}?"
         
     | 
| 
       206 
206 
     | 
    
         
             
                    end
         
     | 
| 
       207 
207 
     | 
    
         | 
| 
       208 
208 
     | 
    
         
             
                    def authorize_interactive_resource_action!
         
     | 
| 
         @@ -216,7 +216,7 @@ module Plutonium 
     | 
|
| 
       216 
216 
     | 
    
         | 
| 
       217 
217 
     | 
    
         
             
                    def build_interactive_record_action_interaction
         
     | 
| 
       218 
218 
     | 
    
         
             
                      @interaction = current_interactive_action.interaction.new(view_context:)
         
     | 
| 
       219 
     | 
    
         
            -
                      @interaction.attributes = interaction_params.merge(resource: resource_record)
         
     | 
| 
      
 219 
     | 
    
         
            +
                      @interaction.attributes = interaction_params.merge(resource: resource_record!)
         
     | 
| 
       220 
220 
     | 
    
         
             
                      @interaction
         
     | 
| 
       221 
221 
     | 
    
         
             
                    end
         
     | 
| 
       222 
222 
     | 
    
         | 
| 
         @@ -36,10 +36,10 @@ module Plutonium 
     | 
|
| 
       36 
36 
     | 
    
         
             
                    end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                    def build_detail
         
     | 
| 
       39 
     | 
    
         
            -
                      current_definition.detail_class.new(resource_record 
     | 
| 
      
 39 
     | 
    
         
            +
                      current_definition.detail_class.new(resource_record!, resource_fields: presentable_attributes, resource_associations: permitted_associations, resource_definition: current_definition)
         
     | 
| 
       40 
40 
     | 
    
         
             
                    end
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                    def build_form(record = resource_record)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    def build_form(record = resource_record!)
         
     | 
| 
       43 
43 
     | 
    
         
             
                      current_definition.form_class.new(record, resource_fields: submittable_attributes, resource_definition: current_definition)
         
     | 
| 
       44 
44 
     | 
    
         
             
                    end
         
     | 
| 
       45 
45 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # lib/plutonium/resource/associations.rb
         
     | 
| 
      
 4 
     | 
    
         
            +
            module Plutonium
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Resource
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Record
         
     | 
| 
      
 7 
     | 
    
         
            +
                  module AssociatedWith
         
     | 
| 
      
 8 
     | 
    
         
            +
                    extend ActiveSupport::Concern
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    included do
         
     | 
| 
      
 11 
     | 
    
         
            +
                      scope :associated_with, ->(record) do
         
     | 
| 
      
 12 
     | 
    
         
            +
                        named_scope = :"associated_with_#{record.model_name.singular}"
         
     | 
| 
      
 13 
     | 
    
         
            +
                        return send(named_scope, record) if respond_to?(named_scope)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                        own_association = klass.find_association_from_self_to_record(record)
         
     | 
| 
      
 16 
     | 
    
         
            +
                        if own_association
         
     | 
| 
      
 17 
     | 
    
         
            +
                          return klass.query_based_on_association(own_association, record)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                        record_association = klass.find_association_to_self_from_record(record)
         
     | 
| 
      
 21 
     | 
    
         
            +
                        if record_association
         
     | 
| 
      
 22 
     | 
    
         
            +
                          Plutonium.logger.warn do
         
     | 
| 
      
 23 
     | 
    
         
            +
                            [
         
     | 
| 
      
 24 
     | 
    
         
            +
                              "Using indirect association from #{record.class} to #{klass.name}",
         
     | 
| 
      
 25 
     | 
    
         
            +
                              "via '#{record_association.name}'.",
         
     | 
| 
      
 26 
     | 
    
         
            +
                              "This may result in poor query performance for large datasets",
         
     | 
| 
      
 27 
     | 
    
         
            +
                              "as it requires loading records to perform the association.",
         
     | 
| 
      
 28 
     | 
    
         
            +
                              "",
         
     | 
| 
      
 29 
     | 
    
         
            +
                              "Consider defining a direct association or implementing",
         
     | 
| 
      
 30 
     | 
    
         
            +
                              "a custom scope '#{named_scope}' for better performance."
         
     | 
| 
      
 31 
     | 
    
         
            +
                            ].join("\n")
         
     | 
| 
      
 32 
     | 
    
         
            +
                          end
         
     | 
| 
      
 33 
     | 
    
         
            +
                          return where(id: record.public_send(record_association.name))
         
     | 
| 
      
 34 
     | 
    
         
            +
                        end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                        klass.raise_unresolvable_association_error(record, named_scope)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    class_methods do
         
     | 
| 
      
 41 
     | 
    
         
            +
                      def find_association_from_self_to_record(record)
         
     | 
| 
      
 42 
     | 
    
         
            +
                        reflect_on_all_associations.find do |assoc|
         
     | 
| 
      
 43 
     | 
    
         
            +
                          assoc.klass.name == record.class.name unless assoc.polymorphic?
         
     | 
| 
      
 44 
     | 
    
         
            +
                        rescue
         
     | 
| 
      
 45 
     | 
    
         
            +
                          assoc.check_validity!
         
     | 
| 
      
 46 
     | 
    
         
            +
                          raise
         
     | 
| 
      
 47 
     | 
    
         
            +
                        end
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      def find_association_to_self_from_record(record)
         
     | 
| 
      
 51 
     | 
    
         
            +
                        record.class.reflect_on_all_associations.find do |assoc|
         
     | 
| 
      
 52 
     | 
    
         
            +
                          assoc.klass.name == name
         
     | 
| 
      
 53 
     | 
    
         
            +
                        rescue
         
     | 
| 
      
 54 
     | 
    
         
            +
                          assoc.check_validity!
         
     | 
| 
      
 55 
     | 
    
         
            +
                          raise
         
     | 
| 
      
 56 
     | 
    
         
            +
                        end
         
     | 
| 
      
 57 
     | 
    
         
            +
                      end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                      def query_based_on_association(assoc, record)
         
     | 
| 
      
 60 
     | 
    
         
            +
                        case assoc.macro
         
     | 
| 
      
 61 
     | 
    
         
            +
                        when :has_one
         
     | 
| 
      
 62 
     | 
    
         
            +
                          joins(assoc.name).where(assoc.name => {record.class.primary_key => record.id})
         
     | 
| 
      
 63 
     | 
    
         
            +
                        when :belongs_to
         
     | 
| 
      
 64 
     | 
    
         
            +
                          where(assoc.name => record)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        when :has_many
         
     | 
| 
      
 66 
     | 
    
         
            +
                          joins(assoc.name).where(assoc.klass.table_name => record)
         
     | 
| 
      
 67 
     | 
    
         
            +
                        else
         
     | 
| 
      
 68 
     | 
    
         
            +
                          raise NotImplementedError, "associated_with->##{assoc.macro}"
         
     | 
| 
      
 69 
     | 
    
         
            +
                        end
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      def raise_unresolvable_association_error(record, named_scope)
         
     | 
| 
      
 73 
     | 
    
         
            +
                        raise "Could not resolve the association between '#{name}' and '#{record.class.name}'\n\n" \
         
     | 
| 
      
 74 
     | 
    
         
            +
                              "Define\n" \
         
     | 
| 
      
 75 
     | 
    
         
            +
                              " 1. the associations between the models\n" \
         
     | 
| 
      
 76 
     | 
    
         
            +
                              " 2. a named scope on #{name} e.g.\n\n" \
         
     | 
| 
      
 77 
     | 
    
         
            +
                              "scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
            end
         
     |