plutonium 0.39.1 → 0.40.0

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-connect-resource/SKILL.md +19 -1
  3. data/.claude/skills/plutonium-controller/SKILL.md +5 -9
  4. data/.claude/skills/plutonium-definition-query/SKILL.md +10 -2
  5. data/.claude/skills/plutonium-installation/SKILL.md +9 -7
  6. data/.claude/skills/plutonium-invites/SKILL.md +363 -0
  7. data/.claude/skills/plutonium-package/SKILL.md +2 -1
  8. data/.claude/skills/plutonium-portal/SKILL.md +30 -16
  9. data/.claude/skills/plutonium-rodauth/SKILL.md +111 -18
  10. data/CHANGELOG.md +48 -0
  11. data/app/assets/plutonium.css +1 -1
  12. data/config/initializers/sqlite_alias.rb +8 -8
  13. data/docs/.vitepress/config.ts +1 -0
  14. data/docs/getting-started/tutorial/07-author-portal.md +1 -0
  15. data/docs/getting-started/tutorial/08-customizing-ui.md +5 -2
  16. data/docs/guides/adding-resources.md +10 -0
  17. data/docs/guides/authentication.md +15 -8
  18. data/docs/guides/creating-packages.md +13 -8
  19. data/docs/guides/index.md +2 -0
  20. data/docs/guides/search-filtering.md +8 -3
  21. data/docs/guides/user-invites.md +497 -0
  22. data/docs/public/templates/base.rb +5 -1
  23. data/docs/public/templates/lite.rb +42 -0
  24. data/docs/public/templates/pluton8.rb +7 -2
  25. data/docs/reference/controller/index.md +12 -7
  26. data/docs/reference/definition/query.md +12 -3
  27. data/docs/reference/generators/index.md +70 -10
  28. data/docs/reference/portal/index.md +22 -11
  29. data/gemfiles/rails_7.gemfile.lock +1 -1
  30. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  31. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  32. data/lib/generators/pu/gem/active_shrine/active_shrine_generator.rb +31 -0
  33. data/lib/generators/pu/gem/active_shrine/templates/config/initializers/shrine.rb.tt +58 -0
  34. data/lib/generators/pu/gem/annotated/templates/lib/tasks/auto_annotate_models.rake +6 -1
  35. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -0
  36. data/lib/generators/pu/invites/USAGE +27 -0
  37. data/lib/generators/pu/invites/install_generator.rb +364 -0
  38. data/lib/generators/pu/invites/invitable/USAGE +31 -0
  39. data/lib/generators/pu/invites/invitable_generator.rb +143 -0
  40. data/lib/generators/pu/invites/templates/INSTRUCTIONS +22 -0
  41. data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +24 -0
  42. data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +26 -0
  43. data/lib/generators/pu/invites/templates/db/migrate/create_user_invites.rb.tt +47 -0
  44. data/lib/generators/pu/invites/templates/invitable/invitation.html.erb.tt +45 -0
  45. data/lib/generators/pu/invites/templates/invitable/invitation.text.erb.tt +15 -0
  46. data/lib/generators/pu/invites/templates/invitable/invite_user_interaction.rb.tt +33 -0
  47. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +77 -0
  48. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/welcome_controller.rb.tt +68 -0
  49. data/lib/generators/pu/invites/templates/packages/invites/app/definitions/invites/user_invite_definition.rb.tt +23 -0
  50. data/lib/generators/pu/invites/templates/packages/invites/app/interactions/invites/cancel_invite_interaction.rb.tt +7 -0
  51. data/lib/generators/pu/invites/templates/packages/invites/app/interactions/invites/resend_invite_interaction.rb.tt +7 -0
  52. data/lib/generators/pu/invites/templates/packages/invites/app/mailers/invites/user_invite_mailer.rb.tt +34 -0
  53. data/lib/generators/pu/invites/templates/packages/invites/app/models/invites/user_invite.rb.tt +41 -0
  54. data/lib/generators/pu/invites/templates/packages/invites/app/policies/invites/user_invite_policy.rb.tt +33 -0
  55. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/error.html.erb.tt +24 -0
  56. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/landing.html.erb.tt +40 -0
  57. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/show.html.erb.tt +39 -0
  58. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/signup.html.erb.tt +49 -0
  59. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.html.erb.tt +45 -0
  60. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.text.erb.tt +15 -0
  61. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/welcome/pending_invitation.html.erb.tt +23 -0
  62. data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +33 -0
  63. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +23 -2
  64. data/lib/generators/pu/lib/plutonium_generators/concerns/configures_sqlite.rb +130 -0
  65. data/lib/generators/pu/lib/plutonium_generators/concerns/mounts_engines.rb +72 -0
  66. data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +4 -2
  67. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +7 -1
  68. data/lib/generators/pu/lite/litestream/litestream_generator.rb +105 -0
  69. data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +88 -0
  70. data/lib/generators/pu/lite/rails_pulse/templates/config/initializers/rails_pulse.rb.tt +14 -0
  71. data/lib/generators/pu/lite/setup/setup_generator.rb +54 -0
  72. data/lib/generators/pu/lite/solid_cable/solid_cable_generator.rb +65 -0
  73. data/lib/generators/pu/lite/solid_cache/solid_cache_generator.rb +66 -0
  74. data/lib/generators/pu/lite/solid_errors/solid_errors_generator.rb +61 -0
  75. data/lib/generators/pu/lite/solid_queue/solid_queue_generator.rb +107 -0
  76. data/lib/generators/pu/pkg/portal/USAGE +8 -2
  77. data/lib/generators/pu/pkg/portal/portal_generator.rb +11 -1
  78. data/lib/generators/pu/pkg/portal/templates/app/controllers/concerns/controller.rb.tt +2 -0
  79. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +1 -0
  80. data/lib/generators/pu/pkg/portal/templates/app/controllers/resource_controller.rb.tt +7 -0
  81. data/lib/generators/pu/pkg/portal/templates/lib/engine.rb.tt +3 -0
  82. data/lib/generators/pu/res/conn/USAGE +5 -0
  83. data/lib/generators/pu/res/conn/conn_generator.rb +30 -4
  84. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +6 -3
  85. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +6 -6
  86. data/lib/generators/pu/rodauth/account_generator.rb +36 -11
  87. data/lib/generators/pu/rodauth/admin_generator.rb +55 -0
  88. data/lib/generators/pu/rodauth/install_generator.rb +1 -8
  89. data/lib/generators/pu/rodauth/templates/app/interactions/invite_admin_interaction.rb.tt +25 -0
  90. data/lib/generators/pu/rodauth/templates/app/models/account.rb.tt +6 -2
  91. data/lib/generators/pu/saas/USAGE +22 -0
  92. data/lib/generators/pu/saas/entity/USAGE +19 -0
  93. data/lib/generators/pu/saas/entity_generator.rb +55 -0
  94. data/lib/generators/pu/saas/membership/USAGE +25 -0
  95. data/lib/generators/pu/saas/membership_generator.rb +165 -0
  96. data/lib/generators/pu/saas/setup/USAGE +27 -0
  97. data/lib/generators/pu/saas/setup_generator.rb +98 -0
  98. data/lib/generators/pu/saas/user/USAGE +21 -0
  99. data/lib/generators/pu/saas/user_generator.rb +66 -0
  100. data/lib/plutonium/core/controller.rb +9 -5
  101. data/lib/plutonium/definition/base.rb +3 -1
  102. data/lib/plutonium/definition/scoping.rb +20 -0
  103. data/lib/plutonium/invites/concerns/cancel_invite.rb +44 -0
  104. data/lib/plutonium/invites/concerns/invitable.rb +98 -0
  105. data/lib/plutonium/invites/concerns/invite_token.rb +186 -0
  106. data/lib/plutonium/invites/concerns/invite_user.rb +147 -0
  107. data/lib/plutonium/invites/concerns/resend_invite.rb +66 -0
  108. data/lib/plutonium/invites/controller.rb +226 -0
  109. data/lib/plutonium/invites/pending_invite_check.rb +76 -0
  110. data/lib/plutonium/invites.rb +6 -0
  111. data/lib/plutonium/resource/controllers/queryable.rb +4 -0
  112. data/lib/plutonium/resource/query_object.rb +3 -5
  113. data/lib/plutonium/version.rb +1 -1
  114. data/package.json +1 -1
  115. metadata +64 -7
  116. data/lib/generators/pu/res/entity/entity_generator.rb +0 -158
  117. data/lib/generators/pu/rodauth/customer_generator.rb +0 -101
  118. data/public/plutonium-assets/plutonium-logo-original.png +0 -0
  119. data/public/plutonium-assets/plutonium-logo-white.png +0 -0
  120. data/public/plutonium-assets/plutonium-logo.png +0 -0
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators/named_base"
4
- require_relative "../../lib/plutonium_generators"
5
-
6
- module Pu
7
- module Res
8
- class EntityGenerator < ::Rails::Generators::NamedBase
9
- include PlutoniumGenerators::Generator
10
-
11
- desc "Generate an entity model for customer accounts"
12
-
13
- class_option :model, type: :boolean, default: true
14
- class_option :allow_signup, type: :boolean, default: true,
15
- desc: "Whether to allow customer to sign up to the platform"
16
- class_option :auth_account, type: :string,
17
- desc: "Specify the authentication account name", required: true
18
-
19
- def start
20
- ensure_customer_model_exists! if behavior == :invoke
21
- generate_entity_resource
22
- generate_membership_resource
23
- end
24
-
25
- private
26
-
27
- def ensure_customer_model_exists!
28
- customer_model_path = File.join("app", "models", "#{normalized_auth_account_name}.rb")
29
- unless File.exist?(customer_model_path)
30
- raise "Customer model '#{normalized_auth_account_name}' does not exist. Please create it first."
31
- end
32
- rescue => e
33
- exception "#{self.class} failed:", e
34
- end
35
-
36
- def generate_entity_resource
37
- Rails::Generators.invoke(
38
- "pu:res:scaffold",
39
- [
40
- normalized_name,
41
- "name:string",
42
- "--model",
43
- ("--force" if options[:force]),
44
- ("--skip" if options[:skip]),
45
- "--dest=#{selected_destination_feature}"
46
- ].compact,
47
- behavior: behavior,
48
- destination_root: destination_root
49
- )
50
-
51
- add_unique_index_to_migration(normalized_name, [:name])
52
- end
53
-
54
- def generate_membership_resource
55
- Rails::Generators.invoke(
56
- "pu:res:scaffold",
57
- [
58
- normalized_entity_membership_name,
59
- *membership_attributes,
60
- "--model",
61
- ("--force" if options[:force]),
62
- ("--skip" if options[:skip]),
63
- "--dest=#{selected_destination_feature}"
64
- ].compact,
65
- behavior: behavior,
66
- destination_root: destination_root
67
- )
68
-
69
- add_unique_index_to_migration(
70
- normalized_entity_membership_name,
71
- ["#{normalized_name}_id", "#{normalized_auth_account_name}_id"]
72
- )
73
-
74
- add_default_to_role_column
75
- add_role_enum_to_model
76
- add_unique_validation_to_model
77
- end
78
-
79
- private
80
-
81
- def selected_destination_feature = "main_app"
82
-
83
- def normalized_name = name.underscore
84
-
85
- def normalized_entity_membership_name
86
- "#{normalized_name}_#{normalized_auth_account_name}"
87
- end
88
-
89
- def normalized_auth_account_name = options[:auth_account].underscore
90
-
91
- def add_unique_index_to_migration(model_name, index_columns)
92
- migration_dir = File.join("db", "migrate")
93
- migration_file = Dir[File.join(migration_dir, "*_create_#{model_name.pluralize}.rb")].first
94
-
95
- modify_file_if_exists(migration_file) do |file|
96
- index_definition = build_index_definition(model_name, index_columns)
97
- insert_into_file migration_file, indent(index_definition, 4), before: /^ end\s*$/
98
- end
99
- end
100
-
101
- def build_index_definition(model_name, index_columns)
102
- table_name = model_name.pluralize
103
-
104
- case index_columns
105
- when Array
106
- if index_columns.size == 1
107
- "add_index :#{table_name}, :#{index_columns.first}, unique: true\n"
108
- else
109
- column_list = index_columns.map { |col| ":#{col}" }.join(", ")
110
- "add_index :#{table_name}, [#{column_list}], unique: true\n"
111
- end
112
- else
113
- "add_index :#{table_name}, :#{index_columns}, unique: true\n"
114
- end
115
- end
116
-
117
- def membership_attributes
118
- [
119
- "#{normalized_name}:references",
120
- "#{normalized_auth_account_name}:references",
121
- "role:integer"
122
- ]
123
- end
124
-
125
- def add_default_to_role_column
126
- migration_dir = File.join("db", "migrate")
127
- migration_file = Dir[File.join(migration_dir, "*_create_#{normalized_entity_membership_name.pluralize}.rb")].first
128
-
129
- modify_file_if_exists(migration_file) do |file|
130
- gsub_file file, /t\.integer :role, null: false/, "t.integer :role, null: false, default: 0 # Member by default"
131
- end
132
- end
133
-
134
- def add_role_enum_to_model
135
- model_file = File.join("app", "models", "#{normalized_entity_membership_name}.rb")
136
-
137
- modify_file_if_exists(model_file) do |file|
138
- enum_definition = "\nenum :role, member: 0, owner: 1"
139
- insert_into_file file, indent(enum_definition, 2), before: /^\s*# add model configurations above\./
140
- end
141
- end
142
-
143
- def add_unique_validation_to_model
144
- model_file = File.join("app", "models", "#{normalized_entity_membership_name}.rb")
145
-
146
- modify_file_if_exists(model_file) do |file|
147
- validation_definition = "validates :#{normalized_auth_account_name}, uniqueness: {scope: :#{normalized_name}_id, message: \"is already a member of this entity\"}\n"
148
- insert_into_file file, indent(validation_definition, 2), before: /^\s*# add validations above\./
149
- end
150
- end
151
-
152
- def modify_file_if_exists(file_path)
153
- return unless file_path && File.exist?(file_path)
154
- yield(file_path)
155
- end
156
- end
157
- end
158
- end
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- return unless defined?(Rodauth::Rails)
4
-
5
- require "rails/generators/named_base"
6
- require_relative "../lib/plutonium_generators"
7
-
8
- module Pu
9
- module Rodauth
10
- class CustomerGenerator < ::Rails::Generators::NamedBase
11
- include PlutoniumGenerators::Concerns::Logger
12
-
13
- source_root File.expand_path("templates", __dir__)
14
-
15
- desc "Generate a rodauth-rails account optimized for customer-based tasks"
16
-
17
- class_option :allow_signup, type: :boolean, default: true,
18
- desc: "Whether to allow customer to sign up to the platform"
19
-
20
- class_option :entity, default: "Entity",
21
- desc: "Generate an entity model for customer accounts. Defaults to 'Entity'",
22
- aliases: ["--entity", "-e"]
23
-
24
- def start
25
- create_customer_account
26
- create_entity_model_and_membership
27
- configure_model_relationships
28
- rescue => e
29
- exception "#{self.class} failed:", e
30
- end
31
-
32
- private
33
-
34
- def create_customer_account
35
- invoke "pu:rodauth:account", [name],
36
- defaults: false,
37
- **customer_features,
38
- force: options[:force],
39
- skip: options[:skip],
40
- lint: true
41
- end
42
-
43
- def create_entity_model_and_membership
44
- invoke "pu:res:entity", [normalized_entity_name],
45
- auth_account: normalized_name,
46
- force: options[:force],
47
- skip: options[:skip]
48
- end
49
-
50
- def configure_model_relationships
51
- entity_model_path = File.join("app", "models", "#{normalized_entity_name}.rb")
52
- if File.exist?(entity_model_path)
53
- insert_into_file entity_model_path, <<~RUBY + " ", before: /# add has_many associations above\.\n/
54
- has_many :#{normalized_entity_membership_name.pluralize}
55
- has_many :#{normalized_name.pluralize}, through: :#{normalized_entity_membership_name.pluralize}
56
- RUBY
57
- end
58
-
59
- customer_model_path = File.join("app", "models", "#{normalized_name}.rb")
60
- if File.exist?(customer_model_path)
61
- insert_into_file customer_model_path, <<~RUBY + " ", before: /# add has_many associations above\.\n/
62
- has_many :#{normalized_entity_membership_name.pluralize}
63
- has_many :#{normalized_entity_name.pluralize}, through: :#{normalized_entity_membership_name.pluralize}
64
- RUBY
65
- end
66
- end
67
-
68
- private
69
-
70
- def customer_features
71
- features = %i[
72
- login
73
- remember
74
- logout
75
- create_account
76
- verify_account
77
- verify_account_grace_period
78
- reset_password
79
- reset_password_notify
80
- change_login
81
- verify_login_change
82
- change_password
83
- change_password_notify
84
- case_insensitive_login
85
- internal_request
86
- ]
87
-
88
- features.delete(:create_account) unless options[:allow_signup]
89
- features.map { |feature| [feature, true] }.to_h
90
- end
91
-
92
- def normalized_name = name.underscore
93
-
94
- def normalized_entity_name = options[:entity].underscore
95
-
96
- def normalized_entity_membership_name
97
- "#{normalized_entity_name.underscore}_#{normalized_name}"
98
- end
99
- end
100
- end
101
- end