plutonium 0.42.0 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-controller/SKILL.md +38 -1
  3. data/.claude/skills/plutonium-definition/SKILL.md +14 -0
  4. data/.claude/skills/plutonium-forms/SKILL.md +16 -1
  5. data/.claude/skills/plutonium-profile/SKILL.md +276 -0
  6. data/.claude/skills/plutonium-views/SKILL.md +23 -1
  7. data/CHANGELOG.md +42 -0
  8. data/app/assets/plutonium.css +2 -2
  9. data/app/views/plutonium/_resource_header.html.erb +6 -27
  10. data/app/views/plutonium/_resource_sidebar.html.erb +1 -2
  11. data/app/views/resource/_resource_details.rabl +3 -2
  12. data/app/views/resource/index.rabl +3 -2
  13. data/app/views/resource/show.rabl +3 -2
  14. data/docs/guides/user-profile.md +322 -0
  15. data/docs/reference/controller/index.md +38 -1
  16. data/docs/reference/definition/index.md +16 -0
  17. data/docs/reference/views/forms.md +15 -0
  18. data/docs/reference/views/index.md +23 -1
  19. data/gemfiles/rails_7.gemfile.lock +1 -1
  20. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  21. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  22. data/lib/generators/pu/core/assets/assets_generator.rb +12 -0
  23. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +11 -0
  24. data/lib/generators/pu/core/typespec/templates/common.tsp.tt +95 -0
  25. data/lib/generators/pu/core/typespec/templates/main.tsp.tt +27 -0
  26. data/lib/generators/pu/core/typespec/templates/main_multi.tsp.tt +25 -0
  27. data/lib/generators/pu/core/typespec/templates/model.tsp.tt +226 -0
  28. data/lib/generators/pu/core/typespec/typespec_generator.rb +342 -0
  29. data/lib/generators/pu/invites/USAGE +0 -1
  30. data/lib/generators/pu/invites/install_generator.rb +62 -15
  31. data/lib/generators/pu/invites/templates/db/migrate/create_user_invites.rb.tt +2 -2
  32. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +2 -0
  33. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/welcome_controller.rb.tt +1 -0
  34. data/lib/generators/pu/invites/templates/packages/invites/app/models/invites/user_invite.rb.tt +5 -5
  35. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/signup.html.erb.tt +4 -4
  36. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +1 -1
  37. data/lib/generators/pu/lib/plutonium_generators/generator.rb +29 -0
  38. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +6 -23
  39. data/lib/generators/pu/pkg/portal/portal_generator.rb +5 -1
  40. data/lib/generators/pu/profile/USAGE +59 -0
  41. data/lib/generators/pu/profile/concerns/profile_arguments.rb +27 -0
  42. data/lib/generators/pu/profile/conn/USAGE +33 -0
  43. data/lib/generators/pu/profile/conn_generator.rb +167 -0
  44. data/lib/generators/pu/profile/install_generator.rb +119 -0
  45. data/lib/generators/pu/profile/setup/USAGE +42 -0
  46. data/lib/generators/pu/profile/setup_generator.rb +73 -0
  47. data/lib/generators/pu/rodauth/account_generator.rb +2 -4
  48. data/lib/generators/pu/rodauth/install_generator.rb +2 -2
  49. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +3 -0
  50. data/lib/generators/pu/saas/api_client_generator.rb +0 -2
  51. data/lib/generators/pu/saas/membership_generator.rb +68 -19
  52. data/lib/generators/pu/saas/setup_generator.rb +7 -2
  53. data/lib/generators/pu/saas/user_generator.rb +0 -2
  54. data/lib/plutonium/auth/rodauth.rb +8 -0
  55. data/lib/plutonium/core/controller.rb +7 -4
  56. data/lib/plutonium/core/controllers/authorizable.rb +5 -1
  57. data/lib/plutonium/definition/base.rb +7 -0
  58. data/lib/plutonium/helpers/display_helper.rb +6 -0
  59. data/lib/plutonium/profile/security_section.rb +118 -0
  60. data/lib/plutonium/resource/controller.rb +17 -7
  61. data/lib/plutonium/resource/controllers/interactive_actions.rb +11 -25
  62. data/lib/plutonium/resource/controllers/presentable.rb +46 -3
  63. data/lib/plutonium/resource/record/associated_with.rb +7 -1
  64. data/lib/plutonium/routing/mapper_extensions.rb +18 -18
  65. data/lib/plutonium/routing/route_set_extensions.rb +23 -2
  66. data/lib/plutonium/ui/breadcrumbs.rb +111 -131
  67. data/lib/plutonium/ui/dyna_frame/content.rb +12 -2
  68. data/lib/plutonium/ui/form/resource.rb +26 -19
  69. data/lib/plutonium/ui/page/base.rb +14 -14
  70. data/lib/plutonium/ui/table/components/scopes_bar.rb +2 -74
  71. data/lib/plutonium/ui/table/components/selection_column.rb +6 -2
  72. data/lib/plutonium/ui/table/resource.rb +3 -2
  73. data/lib/plutonium/version.rb +1 -1
  74. data/lib/tasks/release.rake +6 -6
  75. data/package.json +1 -1
  76. metadata +17 -3
  77. data/lib/generators/pu/rodauth/concerns/gem_helpers.rb +0 -19
@@ -117,11 +117,7 @@ module PlutoniumGenerators
117
117
  if name.include? "/"
118
118
  attr_options[:to_table] = name.underscore.tr("/", "_").pluralize.to_sym
119
119
  attr_options[:class_name] = name.classify
120
- name = name.underscore
121
- if (shared_namespace = find_shared_namespace(model_name, name, separator: "/"))
122
- name = name.sub("#{shared_namespace}/", "")
123
- end
124
- name = name.tr("/", "_")
120
+ name = PlutoniumGenerators::Generator.derive_association_name(model_name, name)
125
121
  end
126
122
  end
127
123
 
@@ -259,27 +255,14 @@ module PlutoniumGenerators
259
255
  end
260
256
  end
261
257
 
262
- def find_shared_namespace(model1, model2, separator: "::")
263
- parts1 = model1.underscore.split(separator)
264
- parts2 = model2.underscore.split(separator)
265
-
266
- shared_namespace = []
267
- [parts1.length, parts2.length].min.times do |i|
268
- if parts1[i] == parts2[i]
269
- shared_namespace << parts1[i]
270
- else
271
- break
272
- end
273
- end
274
-
275
- shared_namespace.empty? ? nil : shared_namespace.join(separator)
276
- end
277
258
  end
278
259
 
279
260
  def required?
280
- return false if attr_options[:null] == true
281
-
282
- super
261
+ if attr_options.key?(:null)
262
+ !attr_options[:null]
263
+ else
264
+ super
265
+ end
283
266
  end
284
267
 
285
268
  def cents?
@@ -88,7 +88,11 @@ module Pu
88
88
  def bring_your_own_auth? = @bring_your_own_auth
89
89
 
90
90
  def configure_entity_scoping
91
- @scoped_entity_class = options[:scope].camelize if options[:scope].present?
91
+ return unless options[:scope].present?
92
+
93
+ scope = options[:scope].camelize
94
+ # Prepend :: to ensure absolute constant reference (avoids NameError in engine.rb)
95
+ @scoped_entity_class = scope.start_with?("::") ? scope : "::#{scope}"
92
96
  end
93
97
 
94
98
  def scoped_to_entity? = scoped_entity_class.present?
@@ -0,0 +1,59 @@
1
+ Description:
2
+ Generate a Profile resource for managing Rodauth account settings.
3
+ Creates a resource linked to the User model with a customized ShowPage
4
+ that displays security settings links (change password, 2FA, etc.).
5
+
6
+ Usage:
7
+ rails g pu:profile:install [NAME] [field:type field:type] --dest=DESTINATION
8
+
9
+ Arguments:
10
+ NAME Profile resource name (default: Profile)
11
+ field:type Additional fields to add to the profile
12
+
13
+ Options:
14
+ --dest=DESTINATION Target destination (required to avoid interactive prompts)
15
+ Use 'main_app' for main application
16
+ Use 'package_name' for feature package
17
+ --user-model=NAME Rodauth user model (default: User)
18
+
19
+ Examples:
20
+ # Basic profile in main_app
21
+ rails g pu:profile:install --dest=main_app
22
+
23
+ # Profile with custom fields
24
+ rails g pu:profile:install bio:text avatar:attachment --dest=main_app
25
+
26
+ # Custom name with fields in a package
27
+ rails g pu:profile:install AccountSettings \
28
+ bio:text \
29
+ 'timezone:string?' \
30
+ 'notifications_enabled:boolean' \
31
+ --dest=customer
32
+
33
+ # With custom user model
34
+ rails g pu:profile:install --dest=main_app --user-model=Account
35
+
36
+ Generated Files:
37
+ Model: app/models/[package/]profile.rb
38
+ Migration: db/migrate/xxx_create_[package_]profiles.rb
39
+ Controller: [packages/package/]app/controllers/[package/]profiles_controller.rb
40
+ Policy: [packages/package/]app/policies/[package/]profile_policy.rb
41
+ Definition: [packages/package/]app/definitions/[package/]profile_definition.rb
42
+
43
+ What Gets Modified:
44
+ User Model: Adds `has_one :profile, dependent: :destroy`
45
+ Definition: Injects ShowPage with SecuritySection for Rodauth links
46
+
47
+ The SecuritySection automatically displays links for enabled Rodauth features:
48
+ - Change Password (change_password)
49
+ - Change Email (change_login)
50
+ - Two-Factor Authentication (otp)
51
+ - Recovery Codes (recovery_codes)
52
+ - Security Keys (webauthn)
53
+ - Active Sessions (active_sessions)
54
+ - Close Account (close_account)
55
+
56
+ After Generation:
57
+ 1. Run migrations: rails db:migrate
58
+ 2. Connect to portal: rails g pu:profile:conn --dest=my_portal
59
+ 3. Customize the profile definition as needed
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pu
4
+ module Profile
5
+ module Concerns
6
+ module ProfileArguments
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ argument :name, type: :string, default: "Profile", required: false, banner: "NAME"
11
+ argument :attributes, type: :array, default: [], banner: "field[:type] field[:type]"
12
+ end
13
+
14
+ # Normalize arguments: if name contains ":", treat it as an attribute
15
+ def normalize_arguments
16
+ if name.include?(":")
17
+ @profile_attributes = [name, *attributes]
18
+ @profile_name = "Profile"
19
+ else
20
+ @profile_name = name
21
+ @profile_attributes = attributes
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ Description:
2
+ Connect a Profile resource to a portal with the profile_url helper configured.
3
+ This enables the "Profile" link in the user menu.
4
+
5
+ Usage:
6
+ rails g pu:profile:conn [RESOURCE] --dest=PORTAL
7
+
8
+ Arguments:
9
+ RESOURCE Profile resource class (default: Profile)
10
+ Use namespaced class for package resources (e.g., Competition::Profile)
11
+
12
+ Options:
13
+ --dest=PORTAL Target portal (required to avoid interactive prompts)
14
+
15
+ Examples:
16
+ # Connect namespaced Profile to portal
17
+ rails g pu:profile:conn Competition::Profile --dest=competition_portal
18
+
19
+ # Connect main_app Profile to portal
20
+ rails g pu:profile:conn Profile --dest=customer_portal
21
+
22
+ # Connect custom-named profile
23
+ rails g pu:profile:conn Competition::AccountSettings --dest=competition_portal
24
+
25
+ What Gets Created:
26
+ Controller: packages/[portal]/app/controllers/[portal]/profiles_controller.rb
27
+
28
+ What Gets Modified:
29
+ Routes: packages/[portal]/config/routes.rb (registers as singular resource)
30
+ Controller: packages/[portal]/app/controllers/[portal]/resource_controller.rb
31
+ (adds profile_url helper method)
32
+
33
+ The profile_url helper enables the "Profile" link in the user navigation menu.
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require_relative "../lib/plutonium_generators"
5
+
6
+ module Pu
7
+ module Profile
8
+ class ConnGenerator < ::Rails::Generators::Base
9
+ include PlutoniumGenerators::Generator
10
+
11
+ desc "Connect a Profile resource to a portal and configure the profile_url helper"
12
+
13
+ argument :name, type: :string, default: "Profile", required: false, banner: "RESOURCE"
14
+
15
+ class_option :dest, type: :string,
16
+ desc: "Destination portal"
17
+
18
+ class_option :user_model, type: :string, default: "User",
19
+ desc: "The Rodauth user model"
20
+
21
+ def start
22
+ validate_portal_destination!
23
+ connect_to_portal
24
+ customize_policy
25
+ customize_definition
26
+ customize_controller
27
+ add_profile_url_helper
28
+ rescue => e
29
+ exception "#{self.class} failed:", e
30
+ end
31
+
32
+ private
33
+
34
+ def validate_portal_destination!
35
+ if selected_destination_portal == "main_app"
36
+ raise ArgumentError, <<~MSG.squish
37
+ pu:profile:conn is for portal packages only. For main_app, configure
38
+ profile_url directly in your ResourceController.
39
+ MSG
40
+ end
41
+ end
42
+
43
+ def connect_to_portal
44
+ invoke "pu:res:conn", [resource_class_name],
45
+ dest: selected_destination_portal,
46
+ singular: true,
47
+ policy: true,
48
+ definition: true,
49
+ force: options[:force],
50
+ skip: options[:skip]
51
+ end
52
+
53
+ def customize_policy
54
+ content = <<-RUBY.chomp
55
+
56
+ # Profile is scoped to current user, not entity.
57
+ # Note: `user` here is the policy's user method (current authenticated user),
58
+ # while `#{user_table}` is the model's association name.
59
+ relation_scope do |relation|
60
+ skip_default_relation_scope!
61
+ relation.where(#{user_table}: user)
62
+ end
63
+
64
+ def create?
65
+ user.#{profile_association}.nil?
66
+ end
67
+
68
+ def destroy?
69
+ false
70
+ end
71
+
72
+ # User is set automatically from current_user, not via mass assignment
73
+ def permitted_attributes_for_create
74
+ super - [:#{user_table}]
75
+ end
76
+ RUBY
77
+ inject_into_file policy_path, content, after: /include #{dest_name.camelize}::ResourcePolicy\n/
78
+ end
79
+
80
+ def customize_definition
81
+ # Add ShowPage with SecuritySection
82
+ content = indent(<<~RUBY, 2)
83
+
84
+ class ShowPage < ShowPage
85
+ private
86
+
87
+ def render_after_content
88
+ render Plutonium::Profile::SecuritySection.new
89
+ end
90
+ end
91
+ RUBY
92
+ inject_into_file definition_path, content, after: /include #{dest_name.camelize}::ResourceDefinition\n/
93
+ end
94
+
95
+ def customize_controller
96
+ # Set user automatically when creating profile
97
+ content = <<-RUBY.chomp
98
+
99
+ private
100
+
101
+ def resource_params
102
+ super.merge(#{user_table}: current_user)
103
+ end
104
+ RUBY
105
+ inject_into_file controller_path, content, after: /include #{dest_name.camelize}::Concerns::Controller\n/
106
+ end
107
+
108
+ def add_profile_url_helper
109
+ content = <<-RUBY.chomp
110
+
111
+ included do
112
+ helper_method :profile_url
113
+ end
114
+
115
+ private
116
+
117
+ # Returns the URL to the user's profile page.
118
+ def profile_url
119
+ profile = current_user.#{profile_association}
120
+ if profile
121
+ resource_url_for(profile)
122
+ else
123
+ resource_url_for(#{resource_class_name}, action: :new)
124
+ end
125
+ end
126
+ RUBY
127
+ inject_into_file concerns_controller_path, content, after: /# add concerns above\.\n/
128
+ end
129
+
130
+ def profile_association
131
+ resource_class_name.demodulize.underscore
132
+ end
133
+
134
+ def resource_class_name
135
+ name.camelize
136
+ end
137
+
138
+ def user_table
139
+ options[:user_model].underscore
140
+ end
141
+
142
+ def dest_name
143
+ selected_destination_portal
144
+ end
145
+
146
+ def concerns_controller_path
147
+ "packages/#{dest_name}/app/controllers/#{dest_name}/concerns/controller.rb"
148
+ end
149
+
150
+ def controller_path
151
+ "packages/#{dest_name}/app/controllers/#{dest_name}/#{resource_class_name.underscore.pluralize}_controller.rb"
152
+ end
153
+
154
+ def policy_path
155
+ "packages/#{dest_name}/app/policies/#{dest_name}/#{resource_class_name.underscore}_policy.rb"
156
+ end
157
+
158
+ def definition_path
159
+ "packages/#{dest_name}/app/definitions/#{dest_name}/#{resource_class_name.underscore}_definition.rb"
160
+ end
161
+
162
+ def selected_destination_portal
163
+ @selected_destination_portal ||= portal_option :dest, prompt: "Select destination portal"
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require_relative "../lib/plutonium_generators"
5
+ require_relative "concerns/profile_arguments"
6
+
7
+ module Pu
8
+ module Profile
9
+ class InstallGenerator < ::Rails::Generators::Base
10
+ include PlutoniumGenerators::Generator
11
+ include Concerns::ProfileArguments
12
+
13
+ desc "Generate a Profile resource for managing Rodauth account settings"
14
+
15
+ class_option :user_model, type: :string, default: "User",
16
+ desc: "The Rodauth user model"
17
+
18
+ class_option :dest, type: :string,
19
+ desc: "Package where the Profile resource should be created"
20
+
21
+ def start
22
+ normalize_arguments
23
+ generate_profile_scaffold
24
+ add_user_association
25
+ add_unique_index_to_migration
26
+ rescue => e
27
+ exception "#{self.class} failed:", e
28
+ end
29
+
30
+ private
31
+
32
+ def generate_profile_scaffold
33
+ invoke "pu:res:scaffold", [@profile_name, *scaffold_attributes],
34
+ dest: selected_destination_feature,
35
+ force: options[:force],
36
+ skip: options[:skip]
37
+ end
38
+
39
+ def add_user_association
40
+ association = if dest_package?
41
+ " has_one :#{file_name}, class_name: \"#{namespaced_class_name}\", dependent: :destroy\n"
42
+ else
43
+ " has_one :#{file_name}, dependent: :destroy\n"
44
+ end
45
+ inject_into_file user_model_path, association,
46
+ before: /^\s*# add has_one associations above\.\n/
47
+ end
48
+
49
+ def add_unique_index_to_migration
50
+ migration_file = Dir[File.join(migration_dir, "*_create_#{table_name}.rb")].first
51
+ unless migration_file
52
+ say_status :warning, "Migration file not found in #{migration_dir}, skipping unique index", :yellow
53
+ return
54
+ end
55
+
56
+ # Add unique: true to the user reference for has_one relationship
57
+ gsub_file migration_file,
58
+ /t\.belongs_to :#{user_table}, null: false, foreign_key: true/,
59
+ "t.belongs_to :#{user_table}, null: false, foreign_key: true, index: {unique: true}"
60
+ end
61
+
62
+ def class_name
63
+ @profile_name.camelize
64
+ end
65
+
66
+ def namespaced_class_name
67
+ if dest_package?
68
+ "#{dest_name.camelize}::#{class_name}"
69
+ else
70
+ class_name
71
+ end
72
+ end
73
+
74
+ def file_name
75
+ @profile_name.underscore
76
+ end
77
+
78
+ def user_table
79
+ options[:user_model].underscore
80
+ end
81
+
82
+ def user_model_path
83
+ "app/models/#{user_table}.rb"
84
+ end
85
+
86
+ def dest_package?
87
+ selected_destination_feature != "main_app"
88
+ end
89
+
90
+ def dest_name
91
+ selected_destination_feature
92
+ end
93
+
94
+ def migration_dir
95
+ if dest_package?
96
+ "packages/#{dest_name}/db/migrate"
97
+ else
98
+ "db/migrate"
99
+ end
100
+ end
101
+
102
+ def table_name
103
+ if dest_package?
104
+ "#{dest_name}_#{file_name.pluralize}"
105
+ else
106
+ file_name.pluralize
107
+ end
108
+ end
109
+
110
+ def scaffold_attributes
111
+ ["#{user_table}:belongs_to", *@profile_attributes.map(&:to_s)]
112
+ end
113
+
114
+ def selected_destination_feature
115
+ feature_option :dest, prompt: "Select destination feature"
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,42 @@
1
+ Description:
2
+ Generate a complete Profile setup: creates the resource and connects it to a portal.
3
+ This combines pu:profile:install and pu:profile:conn into a single command.
4
+
5
+ Usage:
6
+ rails g pu:profile:setup [NAME] [field:type ...] --dest=PACKAGE --portal=PORTAL
7
+
8
+ Arguments:
9
+ NAME Profile resource name (default: Profile)
10
+ field:type Additional fields for the profile
11
+
12
+ Options:
13
+ --dest=PACKAGE Package where Profile resource is created
14
+ --portal=PORTAL Portal to connect the Profile to
15
+ --user-model=NAME Rodauth user model (default: User)
16
+
17
+ Examples:
18
+ # Complete setup in one command
19
+ rails g pu:profile:setup date_of_birth:date bio:text \
20
+ --dest=competition \
21
+ --portal=competition_portal
22
+
23
+ # With custom name
24
+ rails g pu:profile:setup AccountSettings bio:text avatar:attachment \
25
+ --dest=main_app \
26
+ --portal=customer_portal
27
+
28
+ # Main app profile connected to admin portal
29
+ rails g pu:profile:setup --dest=main_app --portal=admin_portal
30
+
31
+ What This Does:
32
+ 1. Creates Profile resource (model, migration, controller, policy, definition)
33
+ 2. Adds has_one :profile to User model
34
+ 3. Adds unique index on user_id
35
+ 4. Customizes policy for owner-only access
36
+ 5. Adds SecuritySection to show page
37
+ 6. Connects to portal as singular resource
38
+ 7. Adds profile_url helper to portal
39
+
40
+ Equivalent to running:
41
+ rails g pu:profile:install [NAME] [fields] --dest=PACKAGE
42
+ rails g pu:profile:conn [RESOURCE] --dest=PORTAL
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require_relative "../lib/plutonium_generators"
5
+ require_relative "concerns/profile_arguments"
6
+
7
+ module Pu
8
+ module Profile
9
+ class SetupGenerator < ::Rails::Generators::Base
10
+ include PlutoniumGenerators::Generator
11
+ include Concerns::ProfileArguments
12
+
13
+ desc "Generate a complete Profile setup with resource and portal connection"
14
+
15
+ class_option :user_model, type: :string, default: "User",
16
+ desc: "The Rodauth user model"
17
+
18
+ class_option :dest, type: :string,
19
+ desc: "Package where the Profile resource should be created"
20
+
21
+ class_option :portal, type: :string,
22
+ desc: "Portal to connect the Profile to"
23
+
24
+ def start
25
+ normalize_arguments
26
+ generate_profile
27
+ connect_to_portal if options[:portal].present?
28
+ rescue => e
29
+ exception "#{self.class} failed:", e
30
+ end
31
+
32
+ private
33
+
34
+ def generate_profile
35
+ klass = Rails::Generators.find_by_namespace("pu:profile:install")
36
+ klass.new(
37
+ [@profile_name, *@profile_attributes],
38
+ {
39
+ user_model: options[:user_model],
40
+ dest: selected_destination_feature,
41
+ force: options[:force],
42
+ skip: options[:skip]
43
+ }
44
+ ).invoke_all
45
+ end
46
+
47
+ def connect_to_portal
48
+ # Shell out to a new process so the newly created model file gets loaded
49
+ generate "pu:profile:conn", "#{resource_class_name} --dest=#{options[:portal]} --user-model=#{options[:user_model]}"
50
+ end
51
+
52
+ def resource_class_name
53
+ if dest_package?
54
+ "#{dest_name.camelize}::#{@profile_name.camelize}"
55
+ else
56
+ @profile_name.camelize
57
+ end
58
+ end
59
+
60
+ def dest_package?
61
+ selected_destination_feature != "main_app"
62
+ end
63
+
64
+ def dest_name
65
+ selected_destination_feature
66
+ end
67
+
68
+ def selected_destination_feature
69
+ @selected_destination_feature ||= feature_option :dest, prompt: "Select destination feature"
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,19 +1,17 @@
1
- return unless defined?(Rodauth::Rails)
2
-
3
1
  require "rails/generators/base"
4
2
  require "securerandom"
5
3
 
6
4
  require "#{__dir__}/concerns/configuration"
7
5
  require "#{__dir__}/concerns/account_selector"
8
6
  require "#{__dir__}/concerns/feature_selector"
9
- require "#{__dir__}/concerns/gem_helpers"
7
+ require "#{__dir__}/../lib/plutonium_generators/concerns/actions"
10
8
 
11
9
  module Pu
12
10
  module Rodauth
13
11
  class AccountGenerator < ::Rails::Generators::Base
14
12
  include Concerns::AccountSelector
15
13
  include Concerns::FeatureSelector
16
- include Concerns::GemHelpers
14
+ include PlutoniumGenerators::Concerns::Actions
17
15
 
18
16
  source_root "#{__dir__}/templates"
19
17
 
@@ -2,13 +2,13 @@ require "rails/generators/base"
2
2
  require "rails/generators/active_record/migration"
3
3
  require "securerandom"
4
4
 
5
- require "#{__dir__}/concerns/gem_helpers"
5
+ require "#{__dir__}/../lib/plutonium_generators/concerns/actions"
6
6
 
7
7
  module Pu
8
8
  module Rodauth
9
9
  class InstallGenerator < ::Rails::Generators::Base
10
10
  include ::ActiveRecord::Generators::Migration
11
- include Concerns::GemHelpers
11
+ include PlutoniumGenerators::Concerns::Actions
12
12
 
13
13
  source_root "#{__dir__}/templates"
14
14
 
@@ -290,6 +290,9 @@ class <%= account_path.classify %>RodauthPlugin < RodauthPlugin
290
290
  <% end -%>
291
291
  <% if reset_password? -%>
292
292
 
293
+ # Redirect to login page after requesting password reset.
294
+ reset_password_email_sent_redirect { login_path }
295
+
293
296
  # Redirect to login page after password reset.
294
297
  reset_password_redirect { login_path }
295
298
  <% end -%>
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- return unless defined?(Rodauth::Rails)
4
-
5
3
  require "rails/generators/named_base"
6
4
  require_relative "../lib/plutonium_generators"
7
5