plutonium 0.14.0 → 0.15.0.pre.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/README copy.md +1 -1
  3. data/README.md +1 -1
  4. data/app/assets/plutonium.css +1 -1
  5. data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
  6. data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
  7. data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
  8. data/app/views/resource/_resource_details.html.erb +1 -36
  9. data/app/views/resource/_resource_form.html.erb +1 -5
  10. data/app/views/resource/_resource_table.html.erb +315 -85
  11. data/app/views/resource/edit.html.erb +1 -5
  12. data/app/views/resource/index.html.erb +1 -5
  13. data/app/views/resource/new.html.erb +1 -5
  14. data/app/views/resource/show.html.erb +1 -5
  15. data/config/initializers/pagy.rb +1 -0
  16. data/config/initializers/rabl.rb +27 -20
  17. data/gemfiles/rails_7.gemfile.lock +5 -1
  18. data/lib/generators/pu/core/assets/assets_generator.rb +2 -2
  19. data/lib/generators/pu/core/install/install_generator.rb +0 -3
  20. data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
  21. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
  22. data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
  23. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
  24. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +5 -2
  25. data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
  26. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +19 -0
  27. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
  28. data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
  29. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
  30. data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
  31. data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
  32. data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
  33. data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
  34. data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
  35. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
  36. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  37. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
  38. data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
  39. data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
  40. data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
  41. data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
  42. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  43. data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
  44. data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
  45. data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
  46. data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
  47. data/lib/generators/pu/res/model/model_generator.rb +0 -7
  48. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
  49. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
  50. data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
  51. data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
  52. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
  53. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
  54. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
  55. data/lib/plutonium/action/README.md +0 -0
  56. data/lib/plutonium/action/base.rb +103 -0
  57. data/lib/plutonium/action/interactive.rb +117 -0
  58. data/lib/plutonium/action/route_options.rb +65 -0
  59. data/lib/plutonium/action/simple.rb +8 -0
  60. data/lib/plutonium/auth.rb +1 -1
  61. data/lib/plutonium/configuration.rb +130 -0
  62. data/lib/plutonium/core/actions/collection.rb +1 -1
  63. data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
  64. data/lib/plutonium/core/autodiscovery/association_renderer_discoverer.rb +1 -1
  65. data/lib/plutonium/core/autodiscovery/input_discoverer.rb +1 -1
  66. data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +1 -1
  67. data/lib/plutonium/core/controller.rb +110 -0
  68. data/lib/plutonium/core/controllers/authorizable.rb +12 -35
  69. data/lib/plutonium/core/controllers/bootable.rb +38 -7
  70. data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
  71. data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
  72. data/lib/plutonium/core/ui/collection.rb +1 -1
  73. data/lib/plutonium/core/ui/detail.rb +1 -1
  74. data/lib/plutonium/core/ui/form.rb +1 -1
  75. data/lib/plutonium/definition/actions.rb +50 -0
  76. data/lib/plutonium/definition/base.rb +92 -0
  77. data/lib/plutonium/definition/config_attr.rb +30 -0
  78. data/lib/plutonium/definition/defineable_props.rb +96 -0
  79. data/lib/plutonium/definition/search.rb +21 -0
  80. data/lib/plutonium/engine/validator.rb +30 -0
  81. data/lib/plutonium/engine.rb +25 -0
  82. data/lib/plutonium/helpers/assets_helper.rb +73 -20
  83. data/lib/plutonium/helpers/form_helper.rb +1 -3
  84. data/lib/plutonium/interaction/README.md +369 -0
  85. data/lib/plutonium/interaction/base.rb +75 -0
  86. data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
  87. data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
  88. data/lib/plutonium/interaction/outcome.rb +129 -0
  89. data/lib/plutonium/interaction/response/base.rb +63 -0
  90. data/lib/plutonium/interaction/response/null.rb +33 -0
  91. data/lib/plutonium/interaction/response/redirect.rb +30 -0
  92. data/lib/plutonium/interaction/response/render.rb +28 -0
  93. data/lib/plutonium/lib/bit_flags.rb +70 -9
  94. data/lib/plutonium/lib/overlayed_hash.rb +86 -0
  95. data/lib/plutonium/lib/smart_cache.rb +171 -0
  96. data/lib/plutonium/models/has_cents.rb +170 -0
  97. data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
  98. data/lib/plutonium/{application → portal}/controller.rb +3 -11
  99. data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
  100. data/lib/plutonium/portal/engine.rb +15 -0
  101. data/lib/plutonium/railtie.rb +35 -15
  102. data/lib/plutonium/reloader.rb +71 -29
  103. data/lib/plutonium/resource/controller.rb +51 -34
  104. data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
  105. data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
  106. data/lib/plutonium/resource/controllers/defineable.rb +26 -0
  107. data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
  108. data/lib/plutonium/resource/controllers/presentable.rb +41 -0
  109. data/lib/plutonium/resource/controllers/queryable.rb +44 -0
  110. data/lib/plutonium/resource/definition.rb +6 -0
  111. data/lib/plutonium/resource/policy.rb +25 -13
  112. data/lib/plutonium/resource/query_object.rb +50 -51
  113. data/lib/plutonium/resource/record.rb +6 -89
  114. data/lib/plutonium/resource/register.rb +82 -0
  115. data/lib/plutonium/routing/mapper_extensions.rb +1 -1
  116. data/lib/plutonium/routing/resource_registration.rb +1 -1
  117. data/lib/plutonium/routing/route_set_extensions.rb +6 -18
  118. data/lib/plutonium/ui/action_button.rb +125 -0
  119. data/lib/plutonium/ui/breadcrumbs.rb +163 -0
  120. data/lib/plutonium/ui/component/base.rb +13 -0
  121. data/lib/plutonium/ui/component/behaviour.rb +38 -0
  122. data/lib/plutonium/ui/component/kit.rb +31 -0
  123. data/lib/plutonium/ui/component/methods.rb +54 -0
  124. data/lib/plutonium/ui/display/base.rb +25 -0
  125. data/lib/plutonium/ui/display/component/association.rb +26 -0
  126. data/lib/plutonium/ui/display/resource.rb +77 -0
  127. data/lib/plutonium/ui/display/theme.rb +27 -0
  128. data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
  129. data/lib/plutonium/ui/empty_card.rb +20 -0
  130. data/lib/plutonium/ui/form/base.rb +37 -0
  131. data/lib/plutonium/ui/form/resource.rb +75 -0
  132. data/lib/plutonium/ui/form/theme.rb +42 -0
  133. data/lib/plutonium/ui/page/base.rb +112 -0
  134. data/lib/plutonium/ui/page/edit.rb +23 -0
  135. data/lib/plutonium/ui/page/index.rb +27 -0
  136. data/lib/plutonium/ui/page/new.rb +23 -0
  137. data/lib/plutonium/ui/page/show.rb +27 -0
  138. data/lib/plutonium/ui/page_header.rb +49 -0
  139. data/lib/plutonium/ui/table/base.rb +13 -0
  140. data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
  141. data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
  142. data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
  143. data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
  144. data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
  145. data/lib/plutonium/ui/table/display_theme.rb +21 -0
  146. data/lib/plutonium/ui/table/resource.rb +98 -0
  147. data/lib/plutonium/ui/table/theme.rb +35 -0
  148. data/lib/plutonium/ui.rb +9 -0
  149. data/lib/plutonium/version.rb +5 -1
  150. data/lib/plutonium.rb +53 -26
  151. data/package-lock.json +19 -22
  152. data/package.json +4 -4
  153. data/sig/.keep +0 -0
  154. data/src/css/plutonium.css +15 -0
  155. data/tailwind.options.js +11 -3
  156. metadata +220 -81
  157. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
  158. data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
  159. data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
  160. data/lib/plutonium/concerns/resource_validatable.rb +0 -34
  161. data/lib/plutonium/config.rb +0 -9
  162. data/lib/plutonium/core/controllers/base.rb +0 -101
  163. data/lib/plutonium/core/controllers/presentable.rb +0 -65
  164. data/lib/plutonium/core/controllers/queryable.rb +0 -28
  165. data/lib/plutonium/pkg/app.rb +0 -35
  166. data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
  167. data/lib/plutonium/pkg/feature.rb +0 -18
  168. data/lib/plutonium/policy/initializer.rb +0 -22
  169. data/lib/plutonium/policy/scope.rb +0 -19
  170. data/lib/plutonium/pundit/context.rb +0 -18
  171. data/lib/plutonium/pundit/policy_finder.rb +0 -25
  172. data/lib/plutonium/resource/policy_context.rb +0 -5
  173. data/lib/plutonium/resource_register.rb +0 -83
  174. data/lib/plutonium/smart_cache.rb +0 -151
  175. data/sig/plutonium.rbs +0 -12
  176. /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
  177. /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
  178. /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
  179. /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
  180. /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
  181. /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
  182. /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
  183. /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  184. /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
  185. /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
  186. /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  187. /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
  188. /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
  189. /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
  190. /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -0,0 +1,270 @@
1
+ require "sequel/core"
2
+
3
+ class <%= account_path.classify %>RodauthPlugin < RodauthPlugin
4
+ configure do
5
+ # This block is running inside of
6
+ # plugin :rodauth do
7
+ # ...
8
+ # end
9
+
10
+ # ==> Features
11
+ # See the Rodauth documentation for the list of available config options:
12
+ # http://rodauth.jeremyevans.net/documentation.html
13
+
14
+ # List of authentication features that are loaded.
15
+ enable <%= selected_features.map(&:inspect).join ', ' %>
16
+
17
+ # ==> General
18
+
19
+ # Change prefix of table and foreign key column names from default "account"
20
+ # accounts_table: 'players'
21
+
22
+ # The secret key used for hashing public-facing tokens for various features.
23
+ # Defaults to Rails `secret_key_base`, but you can use your own secret key.
24
+ # hmac_secret "<SECRET_KEY>"
25
+
26
+ # Use path prefix for all routes.
27
+ <%= '# ' if primary? %>prefix "/<%= account_path.pluralize %>"
28
+ <% unless separate_passwords? -%>
29
+
30
+ # Store password hash in a column instead of a separate table.
31
+ account_password_hash_column :password_hash
32
+ <% end -%>
33
+
34
+ # Specify the controller used for view rendering, CSRF, and callbacks.
35
+ rails_controller { Rodauth::<%= account_path.classify %>Controller }
36
+
37
+ # Specify the model to be used.
38
+ rails_account_model { <%= account_path.classify %> }
39
+ <% if verify_account? -%>
40
+
41
+ # Set password password during create account.
42
+ # verify_account_set_password? false
43
+ <% end -%>
44
+
45
+ # Change some default param keys.
46
+ # login_param "email"
47
+ # password_confirm_param "confirm_password"
48
+ <% if login? -%>
49
+
50
+ # Redirect back to originally requested location after authentication.
51
+ login_return_to_requested_location? true
52
+ <% end -%>
53
+ # two_factor_auth_return_to_requested_location? true # if using MFA
54
+
55
+ # Autologin the user after they have reset their password.
56
+ # reset_password_autologin? true
57
+
58
+ # Delete the account record when the user has closed their account.
59
+ # delete_account_on_close? true
60
+
61
+ # Redirect to the app from login and registration pages if already logged in.
62
+ # already_logged_in { redirect login_redirect }
63
+ <% if jwt? -%>
64
+
65
+ # ==> JWT
66
+
67
+ # Set JWT secret, which is used to cryptographically protect the token.
68
+ jwt_secret Rails.application.credentials.secret_key_base
69
+ <% end -%>
70
+ <% if only_json? -%>
71
+
72
+ # ==> Api only
73
+
74
+ # Accept only JSON requests.
75
+ only_json? true
76
+
77
+ # Handle login and password confirmation fields on the client side.
78
+ require_password_confirmation? false
79
+ require_login_confirmation? false
80
+ <% else -%>
81
+
82
+ # Accept both api and form requests
83
+ # Requires the JSON feature
84
+ <%= '# ' unless json? %>only_json? false
85
+ <% end -%>
86
+ <% if mails? -%>
87
+
88
+ # ==> Emails
89
+ # Use a custom mailer for delivering authentication emails.
90
+ <% if reset_password? -%>
91
+
92
+ create_reset_password_email do
93
+ Rodauth::<%= account_path.classify %>Mailer.reset_password(self.class.configuration_name, account_id, reset_password_key_value)
94
+ end
95
+ <% end -%>
96
+ <% if verify_account? -%>
97
+
98
+ create_verify_account_email do
99
+ Rodauth::<%= account_path.classify %>Mailer.verify_account(self.class.configuration_name, account_id, verify_account_key_value)
100
+ end
101
+ <% end -%>
102
+ <% if verify_login_change? -%>
103
+
104
+ create_verify_login_change_email do |_login|
105
+ Rodauth::<%= account_path.classify %>Mailer.verify_login_change(self.class.configuration_name, account_id, verify_login_change_key_value)
106
+ end
107
+ <% end -%>
108
+ <% if change_password_notify? -%>
109
+
110
+ create_password_changed_email do
111
+ Rodauth::<%= account_path.classify %>Mailer.change_password_notify(self.class.configuration_name, account_id)
112
+ end
113
+ <% end -%>
114
+ <% if reset_password_notify? -%>
115
+
116
+ create_reset_password_notify_email do
117
+ Rodauth::<%= account_path.classify %>Mailer.reset_password_notify(self.class.configuration_name, account_id)
118
+ end
119
+ <% end -%>
120
+ <% if email_auth? -%>
121
+
122
+ create_email_auth_email do
123
+ Rodauth::<%= account_path.classify %>Mailer.email_auth(self.class.configuration_name, account_id, email_auth_key_value)
124
+ end
125
+ <% end -%>
126
+ <% if lockout? -%>
127
+
128
+ create_unlock_account_email do
129
+ Rodauth::<%= account_path.classify %>Mailer.unlock_account(self.class.configuration_name, account_id, unlock_account_key_value)
130
+ end
131
+ <% end -%>
132
+
133
+ send_email do |email|
134
+ # queue email delivery on the mailer after the transaction commits
135
+ db.after_commit { email.deliver_later }
136
+ end
137
+ <% end -%>
138
+ <% unless only_json? -%>
139
+
140
+ # ==> Flash
141
+ # Does not work with only_json?
142
+
143
+ # Match flash keys with ones already used in the Rails app.
144
+ # flash_notice_key :success # default is :notice
145
+ # flash_error_key :error # default is :alert
146
+
147
+ # Override default flash messages.
148
+ # create_account_notice_flash "Your account has been created. Please verify your account by visiting the confirmation link sent to your email address."
149
+ # require_login_error_flash "Login is required for accessing this page"
150
+ # login_notice_flash nil
151
+ <% end -%>
152
+
153
+ # ==> Validation
154
+ # Override default validation error messages.
155
+ # no_matching_login_message "user with this email address doesn't exist"
156
+ # already_an_account_with_this_login_message "user with this email address already exists"
157
+ # password_too_short_message { "needs to have at least #{password_minimum_length} characters" }
158
+ # login_does_not_meet_requirements_message { "invalid email#{", #{login_requirement_message}" if login_requirement_message}" }
159
+
160
+ # ==> Passwords
161
+
162
+ # Passwords shorter than 8 characters are considered weak according to OWASP.
163
+ <%= '# ' unless login? %>password_minimum_length 8
164
+
165
+ # Custom password complexity requirements (alternative to password_complexity feature).
166
+ # password_meets_requirements? do |password|
167
+ # super(password) && password_complex_enough?(password)
168
+ # end
169
+ # auth_class_eval do
170
+ # def password_complex_enough?(password)
171
+ # return true if password.match?(/\d/) && password.match?(/[^a-zA-Z\d]/)
172
+ # set_password_requirement_error_message(:password_simple, "requires one number and one special character")
173
+ # false
174
+ # end
175
+ # end
176
+ <% unless argon2? -%>
177
+
178
+ # = bcrypt
179
+
180
+ # bcrypt has a maximum input length of 72 bytes, truncating any extra bytes.
181
+ password_maximum_bytes 72 if respond_to?(:password_maximum_bytes)
182
+ <% else -%>
183
+
184
+ # = argon2
185
+
186
+ # Use a rotatable password pepper when hashing passwords with Argon2.
187
+ argon2_secret "TODO: <SECRET_KEY>"
188
+
189
+ # Since we're using argon2, prevent loading the bcrypt gem to save memory.
190
+ require_bcrypt? false
191
+
192
+ # Having a maximum password length set prevents long password DoS attacks.
193
+ password_maximum_length 64 if respond_to?(:password_maximum_length)
194
+ <% end -%>
195
+ <% if remember? -%>
196
+
197
+ # ==> Remember Feature
198
+
199
+ # Remember all logged in users.
200
+ after_login { remember_login }
201
+
202
+ # Or only remember users that have ticked a "Remember Me" checkbox on login.
203
+ # after_login { remember_login if param_or_nil("remember") }
204
+
205
+ # Extend user's remember period when remembered via a cookie
206
+ extend_remember_deadline? true
207
+
208
+ # Store the user's remember cookie under a namespace
209
+ remember_cookie_key "_<%= table_prefix %>_remember"
210
+ <% end -%>
211
+
212
+ # ==> Hooks
213
+
214
+ # Validate custom fields in the create account form.
215
+ # before_create_account do
216
+ # throw_error_status(422, "name", "must be present") if param("name").empty?
217
+ # end
218
+
219
+ # Perform additional actions after the account is created.
220
+ # after_create_account do
221
+ # Profile.create!(account_id: account_id, name: param("name"))
222
+ # end
223
+
224
+ # Do additional cleanup after the account is closed.
225
+ # after_close_account do
226
+ # Profile.find_by!(account_id: account_id).destroy
227
+ # end
228
+ <% unless only_json? -%>
229
+
230
+ # ==> Redirects
231
+ <% if create_account? -%>
232
+
233
+ # Redirect to home after login.
234
+ create_account_redirect "/"
235
+ <% end -%>
236
+ <% if login? -%>
237
+
238
+ # Redirect to home after login.
239
+ login_redirect "/"
240
+ <% end -%>
241
+ <% if logout? -%>
242
+
243
+ # Redirect to home page after logout.
244
+ logout_redirect "/"
245
+ <% end -%>
246
+ <% if verify_account? -%>
247
+
248
+ # Redirect to wherever login redirects to after account verification.
249
+ verify_account_redirect { login_redirect }
250
+ <% end -%>
251
+ <% if reset_password? -%>
252
+
253
+ # Redirect to login page after password reset.
254
+ reset_password_redirect { login_path }
255
+ <% end -%>
256
+
257
+ # Ensure requiring login follows login route changes.
258
+ require_login_redirect { login_path }
259
+ <% end -%>
260
+
261
+ # ==> Deadlines
262
+ # Change default deadlines for some actions.
263
+ # verify_account_grace_period 3.days.to_i
264
+ # reset_password_deadline_interval Hash[hours: 6]
265
+ # verify_login_change_deadline_interval Hash[days: 2]
266
+ <% unless only_json? -%>
267
+ # remember_deadline_interval Hash[days: 30]
268
+ <% end -%>
269
+ end
270
+ end
File without changes
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/string_inquirer"
4
+
5
+ module Plutonium
6
+ module Action
7
+ # Base class for all actions in the Plutonium framework.
8
+ #
9
+ # @attr_reader [Symbol] name The name of the action.
10
+ # @attr_reader [String] label The human-readable label for the action.
11
+ # @attr_reader [String, nil] icon The icon associated with the action.
12
+ # @attr_reader [RouteOptions] route_options The routing options for the action.
13
+ # @attr_reader [String, nil] confirmation The confirmation message for the action.
14
+ # @attr_reader [String, nil] turbo_frame The Turbo Frame ID for the action.
15
+ # @attr_reader [Symbol, nil] color The color associated with the action.
16
+ # @attr_reader [Symbol, nil] category The category of the action.
17
+ # @attr_reader [Integer] position The position of the action within its category.
18
+ class Base
19
+ attr_reader :name, :label, :icon, :route_options, :confirmation, :turbo_frame, :color, :category, :position
20
+
21
+ # Initialize a new action.
22
+ #
23
+ # @param [Symbol] name The name of the action.
24
+ # @param [Hash] options The options for the action.
25
+ # @option options [String] :label The human-readable label for the action.
26
+ # @option options [String] :icon The icon associated with the action (e.g., 'fa-edit' for Font Awesome).
27
+ # @option options [Symbol] :color The color associated with the action (e.g., :primary, :secondary, :success, :warning, :danger).
28
+ # @option options [String] :confirmation The confirmation message to display before executing the action.
29
+ # @option options [RouteOptions, Hash] :route_options The routing options for the action.
30
+ # @option options [String] :turbo_frame The Turbo Frame ID for the action (used in Hotwire/Turbo Drive applications).
31
+ # @option options [Boolean] :bulk_action (false) If true, applies to a bulk selection of records (e.g., "Mark Selected as Read").
32
+ # @option options [Boolean] :collection_record_action (false) If true, applies to records in a collection (e.g., "Edit Record" button in a table).
33
+ # @option options [Boolean] :record_action (false) If true, applies to an individual record (e.g., "Delete" button on a Show page).
34
+ # @option options [Boolean] :resource_action (false) If true, applies to the entire resource and can be used in any context (e.g., "Import from CSV").
35
+ # @option options [Symbol] :category The category of the action. Determines visibility and grouping.
36
+ # Valid values include:
37
+ # @option options [Symbol] :primary Always shown and given prominence in the UI.
38
+ # @option options [Symbol] :secondary Shown in secondary menus or less prominent areas.
39
+ # @option options [Symbol] :danger Actions that require caution, often destructive operations.
40
+ # @option options [Integer] :position (50) The position of the action in its group. Lower numbers appear first.
41
+ def initialize(name, **options)
42
+ @name = name.to_sym
43
+ @label = options[:label] || name.to_s.humanize
44
+ @icon = options[:icon]
45
+ @color = options[:color]
46
+ @confirmation = options[:confirmation]
47
+ @route_options = build_route_options(options[:route_options])
48
+ @turbo_frame = options[:turbo_frame]
49
+ @bulk_action = options[:bulk_action] || false
50
+ @collection_record_action = options[:collection_record_action] || false
51
+ @record_action = options[:record_action] || false
52
+ @resource_action = options[:resource_action] || false
53
+ @category = ActiveSupport::StringInquirer.new((options[:category] || :secondary).to_s)
54
+ @position = options[:position] || 50
55
+
56
+ freeze
57
+ end
58
+
59
+ # @return [Boolean] Whether this is a bulk action.
60
+ def bulk_action?
61
+ @bulk_action
62
+ end
63
+
64
+ # @return [Boolean] Whether this is a collection record action.
65
+ def collection_record_action?
66
+ @collection_record_action
67
+ end
68
+
69
+ # @return [Boolean] Whether this is a record action.
70
+ def record_action?
71
+ @record_action
72
+ end
73
+
74
+ # @return [Boolean] Whether this is a resource action.
75
+ def resource_action?
76
+ @resource_action
77
+ end
78
+
79
+ def permitted_by?(policy)
80
+ policy.allowed_to?(:"#{name}?")
81
+ end
82
+
83
+ private
84
+
85
+ # Build RouteOptions from the provided options
86
+ #
87
+ # @param [RouteOptions, Hash, nil] options The routing options
88
+ # @return [RouteOptions] The built RouteOptions object
89
+ def build_route_options(options)
90
+ case options
91
+ when RouteOptions
92
+ options
93
+ when Hash
94
+ RouteOptions.new(**options)
95
+ when nil
96
+ RouteOptions.new
97
+ else
98
+ raise ArgumentError, "Invalid route_options. Expected RouteOptions, Hash, or nil."
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Action
5
+ # InteractiveAction class for handling interactive actions in the Plutonium framework
6
+ #
7
+ # @attr_reader [Class] interaction The interaction class associated with this action
8
+ # @attr_reader [Boolean] immediate Whether the action is executed immediately
9
+ class Interactive < Base
10
+ attr_reader :interaction, :immediate
11
+
12
+ # Initialize a new InteractiveAction
13
+ #
14
+ # @param [Symbol] name The name of the action
15
+ # @param [Class] interaction The interaction class for this action
16
+ # @param [Boolean] immediate Whether the action is executed immediately
17
+ # @param [Hash] options Additional options for the action
18
+ def initialize(name, interaction:, immediate:, **)
19
+ @interaction = interaction
20
+ @immediate = immediate
21
+
22
+ super(name, **)
23
+ end
24
+
25
+ # Get the confirmation message for the action
26
+ #
27
+ # @return [String, nil] The confirmation message or nil if not applicable
28
+ def confirmation
29
+ super || (@immediate ? "#{label}?" : nil)
30
+ end
31
+
32
+ # Factory for creating Interactive actions
33
+ class Factory
34
+ # Create a new Interactive action based on the interaction type
35
+ #
36
+ # @param [Symbol] name The name of the action
37
+ # @param [Class] interaction The interaction class
38
+ # @param [Hash] options Additional options for the action
39
+ # @return [Interactive] A new Interactive action instance
40
+ def self.create(name, interaction:, **options)
41
+ attribute_names = symbolized_attribute_names(interaction)
42
+ action_type = determine_action_type(attribute_names)
43
+ input_fields = determine_input_fields(attribute_names)
44
+ action_options = determine_action_options(action_type)
45
+ immediate = options.fetch(:immediate) { input_fields.blank? }
46
+ route_options = build_route_options(name, action_type, immediate)
47
+
48
+ Interactive.new(
49
+ name,
50
+ interaction: interaction,
51
+ immediate: immediate,
52
+ route_options: route_options,
53
+ **action_options,
54
+ **options
55
+ )
56
+ end
57
+
58
+ # Get symbolized attribute names for the interaction
59
+ #
60
+ # @param [Class] interaction The interaction class
61
+ # @return [Array<Symbol>] Symbolized attribute names
62
+ def self.symbolized_attribute_names(interaction)
63
+ interaction.attribute_names.map(&:to_sym)
64
+ end
65
+
66
+ # Determine the action type based on the interaction's attributes
67
+ #
68
+ # @param [Array<Symbol>] attribute_names Symbolized attribute names
69
+ # @return [Symbol] The determined action type
70
+ def self.determine_action_type(attribute_names)
71
+ if attribute_names.include?(:resource)
72
+ :interactive_resource_record_action
73
+ elsif attribute_names.include?(:resources)
74
+ :interactive_resource_collection_action
75
+ else
76
+ :interactive_resource_recordless_action
77
+ end
78
+ end
79
+
80
+ # Determine the input fields for the action
81
+ #
82
+ # @param [Array<Symbol>] attribute_names Symbolized attribute names
83
+ # @return [Array<Symbol>] The input fields
84
+ def self.determine_input_fields(attribute_names)
85
+ attribute_names - [:resource, :resources]
86
+ end
87
+
88
+ # Determine the action options based on the action type
89
+ #
90
+ # @param [Symbol] action_type The type of the action
91
+ # @return [Hash] The action options
92
+ def self.determine_action_options(action_type)
93
+ {
94
+ bulk_action: action_type == :interactive_resource_collection_action,
95
+ record_action: action_type == :interactive_resource_record_action,
96
+ collection_record_action: action_type == :interactive_resource_record_action,
97
+ resource_action: action_type == :interactive_resource_recordless_action
98
+ }
99
+ end
100
+
101
+ # Build the route options for the action
102
+ #
103
+ # @param [Symbol] name The name of the action
104
+ # @param [Symbol] action_type The type of the action
105
+ # @param [Boolean] immediate Whether the action is executed immediately
106
+ # @return [RouteOptions] The route options for the action
107
+ def self.build_route_options(name, action_type, immediate)
108
+ RouteOptions.new(
109
+ method: immediate ? :post : :get,
110
+ action: action_type,
111
+ interactive_action: name
112
+ )
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Action
5
+ # RouteOptions class for handling routing options in the Plutonium framework.
6
+ #
7
+ # @attr_reader [Symbol] method The HTTP method for the route.
8
+ # @attr_reader [Array] url_args The positional arguments for URL generation.
9
+ # @attr_reader [Hash] url_options URL options for the route.
10
+ # @attr_reader [Symbol] url_resolver The method to use for resolving URLs.
11
+ class RouteOptions
12
+ attr_reader :method, :url_args, :url_options, :url_resolver
13
+
14
+ # Initialize a new RouteOptions instance.
15
+ #
16
+ # @param [Array] url_args The positional arguments for URL generation.
17
+ # @param [Symbol] method The HTTP method for the route (default: :get).
18
+ # @param [Symbol] url_resolver The method to use for resolving URLs (default: :resource_url_for).
19
+ # @param [Hash] url_options URL options for the route.
20
+ def initialize(*url_args, method: :get, url_resolver: :resource_url_for, **url_options)
21
+ @method = method
22
+ @url_resolver = url_resolver
23
+ @url_args = url_args
24
+ @url_options = url_options.freeze
25
+ freeze
26
+ end
27
+
28
+ # Convert the RouteOptions to arguments suitable for URL helpers.
29
+ #
30
+ # @return [Array] The arguments for URL generation.
31
+ def to_url_args
32
+ @url_args + [@url_options]
33
+ end
34
+
35
+ # Merge this RouteOptions with another RouteOptions instance.
36
+ #
37
+ # @param [RouteOptions] other The other RouteOptions instance to merge with.
38
+ # @return [RouteOptions] A new RouteOptions instance with merged values.
39
+ def merge(other)
40
+ self.class.new(
41
+ *(@url_args | other.url_args),
42
+ method: other.method || @method,
43
+ url_resolver: other.url_resolver || @url_resolver,
44
+ **@url_options.merge(other.url_options)
45
+ )
46
+ end
47
+
48
+ def ==(other)
49
+ self.class == other.class &&
50
+ method == other.method &&
51
+ url_resolver == other.url_resolver &&
52
+ url_args == other.url_args &&
53
+ url_options == other.url_options
54
+ end
55
+
56
+ def eql?(other)
57
+ self == other
58
+ end
59
+
60
+ def hash
61
+ [self.class, method, url_resolver, url_args, url_options].hash
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Action
5
+ class Simple < Base
6
+ end
7
+ end
8
+ end
@@ -1,6 +1,6 @@
1
1
  module Plutonium
2
2
  module Auth
3
- def self.rodauth(name)
3
+ def self.Rodauth(name)
4
4
  Rodauth.for(name)
5
5
  end
6
6
  end