plutonium 0.14.1 → 0.15.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) 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/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
  19. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
  20. data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
  21. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
  22. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -8
  23. data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
  24. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +1 -1
  25. data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
  26. data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
  27. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
  28. data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
  29. data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
  30. data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
  31. data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
  32. data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
  33. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
  34. data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
  35. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
  36. data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
  37. data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
  38. data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
  39. data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
  40. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
  41. data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
  42. data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
  43. data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
  44. data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
  45. data/lib/generators/pu/res/model/model_generator.rb +0 -7
  46. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
  47. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
  48. data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
  49. data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
  50. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
  51. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
  52. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
  53. data/lib/plutonium/action/README.md +0 -0
  54. data/lib/plutonium/action/base.rb +103 -0
  55. data/lib/plutonium/action/interactive.rb +117 -0
  56. data/lib/plutonium/action/route_options.rb +65 -0
  57. data/lib/plutonium/action/simple.rb +8 -0
  58. data/lib/plutonium/auth.rb +1 -1
  59. data/lib/plutonium/configuration.rb +0 -8
  60. data/lib/plutonium/core/actions/collection.rb +1 -1
  61. data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
  62. data/lib/plutonium/core/controller.rb +110 -0
  63. data/lib/plutonium/core/controllers/authorizable.rb +12 -35
  64. data/lib/plutonium/core/controllers/bootable.rb +38 -7
  65. data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
  66. data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
  67. data/lib/plutonium/core/ui/collection.rb +1 -1
  68. data/lib/plutonium/core/ui/detail.rb +1 -1
  69. data/lib/plutonium/core/ui/form.rb +1 -1
  70. data/lib/plutonium/definition/actions.rb +50 -0
  71. data/lib/plutonium/definition/base.rb +92 -0
  72. data/lib/plutonium/definition/config_attr.rb +30 -0
  73. data/lib/plutonium/definition/defineable_props.rb +96 -0
  74. data/lib/plutonium/definition/search.rb +21 -0
  75. data/lib/plutonium/engine/validator.rb +30 -0
  76. data/lib/plutonium/engine.rb +25 -0
  77. data/lib/plutonium/helpers/form_helper.rb +1 -3
  78. data/lib/plutonium/interaction/README.md +369 -0
  79. data/lib/plutonium/interaction/base.rb +75 -0
  80. data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
  81. data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
  82. data/lib/plutonium/interaction/outcome.rb +129 -0
  83. data/lib/plutonium/interaction/response/base.rb +63 -0
  84. data/lib/plutonium/interaction/response/null.rb +33 -0
  85. data/lib/plutonium/interaction/response/redirect.rb +30 -0
  86. data/lib/plutonium/interaction/response/render.rb +28 -0
  87. data/lib/plutonium/lib/bit_flags.rb +70 -9
  88. data/lib/plutonium/{config → lib}/overlayed_hash.rb +1 -1
  89. data/lib/plutonium/lib/smart_cache.rb +171 -0
  90. data/lib/plutonium/models/has_cents.rb +170 -0
  91. data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
  92. data/lib/plutonium/{application → portal}/controller.rb +3 -11
  93. data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
  94. data/lib/plutonium/portal/engine.rb +15 -0
  95. data/lib/plutonium/railtie.rb +33 -1
  96. data/lib/plutonium/reloader.rb +5 -5
  97. data/lib/plutonium/resource/controller.rb +51 -34
  98. data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
  99. data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
  100. data/lib/plutonium/resource/controllers/defineable.rb +26 -0
  101. data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
  102. data/lib/plutonium/resource/controllers/presentable.rb +41 -0
  103. data/lib/plutonium/resource/controllers/queryable.rb +44 -0
  104. data/lib/plutonium/resource/definition.rb +6 -0
  105. data/lib/plutonium/resource/policy.rb +25 -13
  106. data/lib/plutonium/resource/query_object.rb +50 -51
  107. data/lib/plutonium/resource/record.rb +6 -89
  108. data/lib/plutonium/resource/register.rb +82 -0
  109. data/lib/plutonium/routing/mapper_extensions.rb +1 -1
  110. data/lib/plutonium/routing/resource_registration.rb +1 -1
  111. data/lib/plutonium/routing/route_set_extensions.rb +6 -18
  112. data/lib/plutonium/ui/action_button.rb +125 -0
  113. data/lib/plutonium/ui/breadcrumbs.rb +163 -0
  114. data/lib/plutonium/ui/component/base.rb +13 -0
  115. data/lib/plutonium/ui/component/behaviour.rb +38 -0
  116. data/lib/plutonium/ui/component/kit.rb +31 -0
  117. data/lib/plutonium/ui/component/methods.rb +54 -0
  118. data/lib/plutonium/ui/display/base.rb +25 -0
  119. data/lib/plutonium/ui/display/component/association.rb +26 -0
  120. data/lib/plutonium/ui/display/resource.rb +77 -0
  121. data/lib/plutonium/ui/display/theme.rb +27 -0
  122. data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
  123. data/lib/plutonium/ui/empty_card.rb +20 -0
  124. data/lib/plutonium/ui/form/base.rb +37 -0
  125. data/lib/plutonium/ui/form/resource.rb +75 -0
  126. data/lib/plutonium/ui/form/theme.rb +42 -0
  127. data/lib/plutonium/ui/page/base.rb +112 -0
  128. data/lib/plutonium/ui/page/edit.rb +23 -0
  129. data/lib/plutonium/ui/page/index.rb +27 -0
  130. data/lib/plutonium/ui/page/new.rb +23 -0
  131. data/lib/plutonium/ui/page/show.rb +27 -0
  132. data/lib/plutonium/ui/page_header.rb +49 -0
  133. data/lib/plutonium/ui/table/base.rb +13 -0
  134. data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
  135. data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
  136. data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
  137. data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
  138. data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
  139. data/lib/plutonium/ui/table/display_theme.rb +21 -0
  140. data/lib/plutonium/ui/table/resource.rb +98 -0
  141. data/lib/plutonium/ui/table/theme.rb +35 -0
  142. data/lib/plutonium/ui.rb +9 -0
  143. data/lib/plutonium/version.rb +5 -1
  144. data/lib/plutonium.rb +14 -1
  145. data/package-lock.json +19 -22
  146. data/package.json +4 -4
  147. data/src/css/plutonium.css +15 -0
  148. data/tailwind.options.js +11 -3
  149. metadata +218 -81
  150. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
  151. data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
  152. data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
  153. data/lib/plutonium/concerns/resource_validatable.rb +0 -34
  154. data/lib/plutonium/config.rb +0 -9
  155. data/lib/plutonium/core/controllers/base.rb +0 -101
  156. data/lib/plutonium/core/controllers/presentable.rb +0 -65
  157. data/lib/plutonium/core/controllers/queryable.rb +0 -28
  158. data/lib/plutonium/pkg/app.rb +0 -35
  159. data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
  160. data/lib/plutonium/pkg/feature.rb +0 -18
  161. data/lib/plutonium/policy/initializer.rb +0 -22
  162. data/lib/plutonium/policy/scope.rb +0 -19
  163. data/lib/plutonium/pundit/context.rb +0 -18
  164. data/lib/plutonium/pundit/policy_finder.rb +0 -25
  165. data/lib/plutonium/resource/policy_context.rb +0 -5
  166. data/lib/plutonium/resource_register.rb +0 -83
  167. data/lib/plutonium/smart_cache.rb +0 -151
  168. /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
  169. /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
  170. /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
  171. /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
  172. /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
  173. /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
  174. /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
  175. /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  176. /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
  177. /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
  178. /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
  179. /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
  180. /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
  181. /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
  182. /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
@@ -126,13 +126,5 @@ module Plutonium
126
126
  def configure
127
127
  yield(configuration)
128
128
  end
129
-
130
- # Load default configuration for a specific version
131
- #
132
- # @param version [Float] the version to load defaults for
133
- # @return [void]
134
- def load_defaults(version)
135
- configuration.load_defaults(version)
136
- end
137
129
  end
138
130
  end
@@ -9,7 +9,7 @@ module Plutonium
9
9
  end
10
10
 
11
11
  def permitted_for(policy)
12
- Collection.new(@collection.select { |name, action| policy.send_with_report :"#{action.name}?" })
12
+ Collection.new(@collection.select { |name, action| policy.allowed_to? :"#{action.name}?" })
13
13
  end
14
14
 
15
15
  def collection_actions
@@ -19,7 +19,9 @@ module Plutonium
19
19
  def self.for_resource_association(resource_class, attr_name, **options)
20
20
  association = resource_class.try(:reflect_on_association, attr_name)
21
21
  raise ArgumentError, "#{attr_name} is not a valid association of #{resource_class}" unless association.present?
22
- raise ArgumentError, "#{association.klass} does is not a resource record" unless association.klass.include?(Plutonium::Resource::Record)
22
+ # # TODO: fix constant being out of sync after reload during development
23
+ # valid_resource_record = Plutonium.configuration.development? ? association.klass.respond_to?(:resource_field_names) : association.klass.include?(Plutonium: :Resource: :Record)
24
+ # raise ArgumentError, "#{association.klass} is not a resource record" unless valid_resource_record
23
25
 
24
26
  type = association.macro
25
27
  raise NotImplementedError, "#{macro} associations are currently not supported." unless type == :has_many