plutonium 0.5.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 (346) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.ruby-version +1 -0
  4. data/.standard.yml +4 -0
  5. data/.vscode/extensions.json +15 -0
  6. data/.vscode/launch.json +7 -0
  7. data/.vscode/settings.json +35 -0
  8. data/CHANGELOG.md +5 -0
  9. data/LICENSE.txt +21 -0
  10. data/README copy.md +84 -0
  11. data/README.md +67 -0
  12. data/Rakefile +8 -0
  13. data/app/views/layouts/resource.html.erb +43 -0
  14. data/app/views/resource/_color_modes.html.erb +55 -0
  15. data/app/views/resource/_details.html.erb +6 -0
  16. data/app/views/resource/_empty_card.html.erb +12 -0
  17. data/app/views/resource/_flash.html.erb +9 -0
  18. data/app/views/resource/_form.html.erb +32 -0
  19. data/app/views/resource/_header.html.erb +8 -0
  20. data/app/views/resource/_interactive_resource_action_form.html.erb +67 -0
  21. data/app/views/resource/_messages.html.erb +1 -0
  22. data/app/views/resource/_metric_card.html.erb +16 -0
  23. data/app/views/resource/_nav_user.html.erb +4 -0
  24. data/app/views/resource/_pagination.html.erb +6 -0
  25. data/app/views/resource/_pagy_pagination.html.erb +15 -0
  26. data/app/views/resource/_resource.html.erb +1 -0
  27. data/app/views/resource/_resource.rabl +5 -0
  28. data/app/views/resource/_sidebar.html.erb +11 -0
  29. data/app/views/resource/_sidebar_menu.html.erb +1 -0
  30. data/app/views/resource/_tab_menu.html.erb +13 -0
  31. data/app/views/resource/_table.html.erb +78 -0
  32. data/app/views/resource/_table_actions.html.erb +15 -0
  33. data/app/views/resource/_toast.html.erb +26 -0
  34. data/app/views/resource/_toolbar.html.erb +22 -0
  35. data/app/views/resource/_toolbar_actions.html.erb +16 -0
  36. data/app/views/resource/_toolbar_breadcrumbs.html.erb +32 -0
  37. data/app/views/resource/_toolbar_search_input.erb +27 -0
  38. data/app/views/resource/edit.html.erb +10 -0
  39. data/app/views/resource/errors.rabl +3 -0
  40. data/app/views/resource/index.html.erb +19 -0
  41. data/app/views/resource/index.rabl +3 -0
  42. data/app/views/resource/interactive_resource_collection_action.html.erb +11 -0
  43. data/app/views/resource/interactive_resource_record_action.html.erb +11 -0
  44. data/app/views/resource/interactive_resource_recordless_action.html.erb +11 -0
  45. data/app/views/resource/new.html.erb +11 -0
  46. data/app/views/resource/readme.md +16 -0
  47. data/app/views/resource/show.html.erb +14 -0
  48. data/app/views/resource/show.rabl +3 -0
  49. data/brakeman.ignore +51 -0
  50. data/lib/active_model/validations/array_validator.rb +74 -0
  51. data/lib/active_model/validations/attached_validator.rb +17 -0
  52. data/lib/active_model/validations/url_validator.rb +51 -0
  53. data/lib/generators/pu/core/install/install_generator.rb +43 -0
  54. data/lib/generators/pu/core/install/templates/.keep +0 -0
  55. data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +2 -0
  56. data/lib/generators/pu/core/install/templates/app/interactions/resource_interaction.rb.tt +2 -0
  57. data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +5 -0
  58. data/lib/generators/pu/core/install/templates/app/policies/resource_policy.rb.tt +2 -0
  59. data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +2 -0
  60. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +2 -0
  61. data/lib/generators/pu/core/install/templates/config/packages.rb +3 -0
  62. data/lib/generators/pu/gem/pagy/pagy_generator.rb +25 -0
  63. data/lib/generators/pu/gem/pagy/templates/.keep +0 -0
  64. data/lib/generators/pu/gem/pagy/templates/config/initializers/pagy.rb +4 -0
  65. data/lib/generators/pu/gem/rabl/rabl_generator.rb +25 -0
  66. data/lib/generators/pu/gem/rabl/templates/.keep +0 -0
  67. data/lib/generators/pu/gem/rabl/templates/config/initializers/rabl.rb +60 -0
  68. data/lib/generators/pu/gem/simple_form/simple_form_generator.rb +25 -0
  69. data/lib/generators/pu/gem/simple_form/templates/.keep +0 -0
  70. data/lib/generators/pu/gen/pug/pug_generator.rb +43 -0
  71. data/lib/generators/pu/gen/pug/templates/pug.rb.tt +23 -0
  72. data/lib/generators/pu/pkg/app/app_generator.rb +60 -0
  73. data/lib/generators/pu/pkg/app/templates/app/controllers/resource_controller.rb.tt +26 -0
  74. data/lib/generators/pu/pkg/app/templates/app/interactions/resource_interaction.rb.tt +4 -0
  75. data/lib/generators/pu/pkg/app/templates/app/policies/resource_policy.rb.tt +4 -0
  76. data/lib/generators/pu/pkg/app/templates/app/presenters/resource_presenter.rb.tt +4 -0
  77. data/lib/generators/pu/pkg/app/templates/config/routes.rb.tt +17 -0
  78. data/lib/generators/pu/pkg/app/templates/lib/engine.rb.tt +14 -0
  79. data/lib/generators/pu/pkg/feature/feature_generator.rb +44 -0
  80. data/lib/generators/pu/pkg/feature/templates/.keep +0 -0
  81. data/lib/generators/pu/pkg/feature/templates/app/controllers/resource_controller.rb.tt +7 -0
  82. data/lib/generators/pu/pkg/feature/templates/app/interactions/resource_interaction.rb.tt +4 -0
  83. data/lib/generators/pu/pkg/feature/templates/app/models/resource_record.rb.tt +5 -0
  84. data/lib/generators/pu/pkg/feature/templates/app/policies/resource_policy.rb.tt +4 -0
  85. data/lib/generators/pu/pkg/feature/templates/app/presenters/resource_presenter.rb.tt +4 -0
  86. data/lib/generators/pu/pkg/feature/templates/lib/engine.rb.tt +8 -0
  87. data/lib/generators/pu/resource/conn/conn_generator.rb +54 -0
  88. data/lib/generators/pu/resource/conn/templates/.keep +0 -0
  89. data/lib/generators/pu/resource/conn/templates/app/controllers/resource_controller.rb.tt +3 -0
  90. data/lib/generators/pu/resource/conn/templates/app/policies/resource_policy.rb.tt +3 -0
  91. data/lib/generators/pu/resource/conn/templates/app/presenters/resource_presenter.rb.tt +3 -0
  92. data/lib/generators/pu/resource/model/USAGE +113 -0
  93. data/lib/generators/pu/resource/model/model_generator.rb +163 -0
  94. data/lib/generators/pu/resource/model/templates/controller.rb.tt +5 -0
  95. data/lib/generators/pu/resource/model/templates/create_table_migration.rb.tt +26 -0
  96. data/lib/generators/pu/resource/model/templates/model.rb.tt +53 -0
  97. data/lib/generators/pu/resource/model/templates/module.rb.tt +7 -0
  98. data/lib/generators/pu/resource/model/templates/policy.rb.tt +4 -0
  99. data/lib/generators/pu/resource/model/templates/presenter.rb.tt +4 -0
  100. data/lib/generators/pu/resource/scaffold/scaffold_generator.rb +219 -0
  101. data/lib/generators/pu/resource/scaffold/templates/app/controllers/admin_resources/resource_controller.rb.tt +7 -0
  102. data/lib/generators/pu/resource/scaffold/templates/app/controllers/entity_resources/resource_controller.rb.tt +7 -0
  103. data/lib/generators/pu/resource/scaffold/templates/app/policies/resources/admin/resource_policy.rb.tt +22 -0
  104. data/lib/generators/pu/resource/scaffold/templates/app/policies/resources/entity/resource_policy.rb.tt +22 -0
  105. data/lib/generators/pu/resource/scaffold/templates/app/policies/resources/resource_policy.rb.tt +13 -0
  106. data/lib/generators/pu/resource/scaffold/templates/app/resources/resource/admin_presenter.rb.tt +7 -0
  107. data/lib/generators/pu/resource/scaffold/templates/app/resources/resource/entity_presenter.rb.tt +7 -0
  108. data/lib/generators/pu/resource/scaffold/templates/app/resources/resource/presenter.rb.tt +23 -0
  109. data/lib/generators/pu/resource/scaffold/templates/app/views/resources/resource/_resource.html.erb.tt +3 -0
  110. data/lib/generators/pu/resource/scaffold/templates/app/views/resources/resource/_resource.rabl +7 -0
  111. data/lib/generators/pu/rodauth/account_generator.rb +120 -0
  112. data/lib/generators/pu/rodauth/concerns/account_selector.rb +22 -0
  113. data/lib/generators/pu/rodauth/concerns/configuration.rb +211 -0
  114. data/lib/generators/pu/rodauth/concerns/feature_selector.rb +125 -0
  115. data/lib/generators/pu/rodauth/install_generator.rb +69 -0
  116. data/lib/generators/pu/rodauth/migration/active_record/account_expiration.erb +8 -0
  117. data/lib/generators/pu/rodauth/migration/active_record/active_sessions.erb +7 -0
  118. data/lib/generators/pu/rodauth/migration/active_record/audit_logging.erb +16 -0
  119. data/lib/generators/pu/rodauth/migration/active_record/base.erb +22 -0
  120. data/lib/generators/pu/rodauth/migration/active_record/disallow_password_reuse.erb +5 -0
  121. data/lib/generators/pu/rodauth/migration/active_record/email_auth.erb +8 -0
  122. data/lib/generators/pu/rodauth/migration/active_record/jwt_refresh.erb +7 -0
  123. data/lib/generators/pu/rodauth/migration/active_record/lockout.erb +13 -0
  124. data/lib/generators/pu/rodauth/migration/active_record/otp.erb +8 -0
  125. data/lib/generators/pu/rodauth/migration/active_record/password_expiration.erb +6 -0
  126. data/lib/generators/pu/rodauth/migration/active_record/recovery_codes.erb +6 -0
  127. data/lib/generators/pu/rodauth/migration/active_record/remember.erb +7 -0
  128. data/lib/generators/pu/rodauth/migration/active_record/reset_password.erb +8 -0
  129. data/lib/generators/pu/rodauth/migration/active_record/separate_passwords.erb +5 -0
  130. data/lib/generators/pu/rodauth/migration/active_record/single_session.erb +6 -0
  131. data/lib/generators/pu/rodauth/migration/active_record/sms_codes.erb +9 -0
  132. data/lib/generators/pu/rodauth/migration/active_record/verify_account.erb +8 -0
  133. data/lib/generators/pu/rodauth/migration/active_record/verify_login_change.erb +8 -0
  134. data/lib/generators/pu/rodauth/migration/active_record/webauthn.erb +13 -0
  135. data/lib/generators/pu/rodauth/migration/sequel/account_expiration.erb +7 -0
  136. data/lib/generators/pu/rodauth/migration/sequel/active_sessions.erb +8 -0
  137. data/lib/generators/pu/rodauth/migration/sequel/audit_logging.erb +17 -0
  138. data/lib/generators/pu/rodauth/migration/sequel/base.erb +25 -0
  139. data/lib/generators/pu/rodauth/migration/sequel/disallow_password_reuse.erb +6 -0
  140. data/lib/generators/pu/rodauth/migration/sequel/email_auth.erb +7 -0
  141. data/lib/generators/pu/rodauth/migration/sequel/jwt_refresh.erb +8 -0
  142. data/lib/generators/pu/rodauth/migration/sequel/lockout.erb +11 -0
  143. data/lib/generators/pu/rodauth/migration/sequel/otp.erb +7 -0
  144. data/lib/generators/pu/rodauth/migration/sequel/password_expiration.erb +5 -0
  145. data/lib/generators/pu/rodauth/migration/sequel/recovery_codes.erb +6 -0
  146. data/lib/generators/pu/rodauth/migration/sequel/remember.erb +6 -0
  147. data/lib/generators/pu/rodauth/migration/sequel/reset_password.erb +7 -0
  148. data/lib/generators/pu/rodauth/migration/sequel/separate_passwords.erb +6 -0
  149. data/lib/generators/pu/rodauth/migration/sequel/single_session.erb +5 -0
  150. data/lib/generators/pu/rodauth/migration/sequel/sms_codes.erb +8 -0
  151. data/lib/generators/pu/rodauth/migration/sequel/verify_account.erb +7 -0
  152. data/lib/generators/pu/rodauth/migration/sequel/verify_login_change.erb +7 -0
  153. data/lib/generators/pu/rodauth/migration/sequel/webauthn.erb +13 -0
  154. data/lib/generators/pu/rodauth/migration_generator.rb +190 -0
  155. data/lib/generators/pu/rodauth/templates/INSTRUCTIONS +52 -0
  156. data/lib/generators/pu/rodauth/templates/app/controllers/plugin_controller.rb.tt +6 -0
  157. data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +3 -0
  158. data/lib/generators/pu/rodauth/templates/app/mailers/account_mailer.rb.tt +4 -0
  159. data/lib/generators/pu/rodauth/templates/app/mailers/rodauth_mailer.rb.tt +62 -0
  160. data/lib/generators/pu/rodauth/templates/app/misc/account_rodauth_plugin.rb.tt +255 -0
  161. data/lib/generators/pu/rodauth/templates/app/misc/rodauth_app.rb.tt +24 -0
  162. data/lib/generators/pu/rodauth/templates/app/misc/rodauth_plugin.rb.tt +234 -0
  163. data/lib/generators/pu/rodauth/templates/app/models/account.rb.tt +40 -0
  164. data/lib/generators/pu/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +7 -0
  165. data/lib/generators/pu/rodauth/templates/app/views/rodauth/_login_form.html.erb +26 -0
  166. data/lib/generators/pu/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +9 -0
  167. data/lib/generators/pu/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +6 -0
  168. data/lib/generators/pu/rodauth/templates/app/views/rodauth/change_login.html.erb +27 -0
  169. data/lib/generators/pu/rodauth/templates/app/views/rodauth/change_password.html.erb +27 -0
  170. data/lib/generators/pu/rodauth/templates/app/views/rodauth/close_account.html.erb +13 -0
  171. data/lib/generators/pu/rodauth/templates/app/views/rodauth/confirm_password.html.erb +11 -0
  172. data/lib/generators/pu/rodauth/templates/app/views/rodauth/create_account.html.erb +35 -0
  173. data/lib/generators/pu/rodauth/templates/app/views/rodauth/email_auth.html.erb +5 -0
  174. data/lib/generators/pu/rodauth/templates/app/views/rodauth/login.html.erb +4 -0
  175. data/lib/generators/pu/rodauth/templates/app/views/rodauth/logout.html.erb +14 -0
  176. data/lib/generators/pu/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb +3 -0
  177. data/lib/generators/pu/rodauth/templates/app/views/rodauth/otp_auth.html.erb +15 -0
  178. data/lib/generators/pu/rodauth/templates/app/views/rodauth/otp_disable.html.erb +13 -0
  179. data/lib/generators/pu/rodauth/templates/app/views/rodauth/otp_setup.html.erb +41 -0
  180. data/lib/generators/pu/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +11 -0
  181. data/lib/generators/pu/rodauth/templates/app/views/rodauth/recovery_codes.html.erb +13 -0
  182. data/lib/generators/pu/rodauth/templates/app/views/rodauth/remember.html.erb +22 -0
  183. data/lib/generators/pu/rodauth/templates/app/views/rodauth/reset_password.html.erb +19 -0
  184. data/lib/generators/pu/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +17 -0
  185. data/lib/generators/pu/rodauth/templates/app/views/rodauth/sms_auth.html.erb +15 -0
  186. data/lib/generators/pu/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +15 -0
  187. data/lib/generators/pu/rodauth/templates/app/views/rodauth/sms_disable.html.erb +13 -0
  188. data/lib/generators/pu/rodauth/templates/app/views/rodauth/sms_request.html.erb +5 -0
  189. data/lib/generators/pu/rodauth/templates/app/views/rodauth/sms_setup.html.erb +23 -0
  190. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/_email_auth_request_form.html.erb +5 -0
  191. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/_login_form.html.erb +24 -0
  192. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/_login_form_footer.html.erb +7 -0
  193. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/add_recovery_codes.html.erb +16 -0
  194. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/change_login.html.erb +25 -0
  195. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/change_password.html.erb +25 -0
  196. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/close_account.html.erb +11 -0
  197. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/confirm_password.html.erb +19 -0
  198. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/create_account.html.erb +33 -0
  199. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/email_auth.html.erb +3 -0
  200. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/login.html.erb +5 -0
  201. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/logout.html.erb +10 -0
  202. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/multi_phase_login.html.erb +5 -0
  203. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/otp_auth.html.erb +9 -0
  204. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/otp_disable.html.erb +11 -0
  205. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/otp_setup.html.erb +32 -0
  206. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/recovery_auth.html.erb +9 -0
  207. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/recovery_codes.html.erb +11 -0
  208. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/remember.html.erb +18 -0
  209. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/reset_password.html.erb +17 -0
  210. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/reset_password_request.html.erb +17 -0
  211. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/sms_auth.html.erb +9 -0
  212. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/sms_confirm.html.erb +9 -0
  213. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/sms_disable.html.erb +11 -0
  214. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/sms_request.html.erb +3 -0
  215. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/sms_setup.html.erb +17 -0
  216. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/two_factor_auth.html.erb +5 -0
  217. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/two_factor_disable.html.erb +11 -0
  218. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/two_factor_manage.html.erb +26 -0
  219. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/unlock_account.html.erb +15 -0
  220. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/unlock_account_request.html.erb +9 -0
  221. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/verify_account.html.erb +19 -0
  222. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/verify_account_resend.html.erb +17 -0
  223. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/verify_login_change.html.erb +3 -0
  224. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/webauthn_auth.html.erb +13 -0
  225. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/webauthn_autofill.html.erb +10 -0
  226. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb +21 -0
  227. data/lib/generators/pu/rodauth/templates/app/views/rodauth/tailwind/webauthn_setup.html.erb +21 -0
  228. data/lib/generators/pu/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +5 -0
  229. data/lib/generators/pu/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +13 -0
  230. data/lib/generators/pu/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +22 -0
  231. data/lib/generators/pu/rodauth/templates/app/views/rodauth/unlock_account.html.erb +15 -0
  232. data/lib/generators/pu/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +9 -0
  233. data/lib/generators/pu/rodauth/templates/app/views/rodauth/verify_account.html.erb +21 -0
  234. data/lib/generators/pu/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +17 -0
  235. data/lib/generators/pu/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +5 -0
  236. data/lib/generators/pu/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +15 -0
  237. data/lib/generators/pu/rodauth/templates/app/views/rodauth/webauthn_autofill.html.erb +10 -0
  238. data/lib/generators/pu/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +23 -0
  239. data/lib/generators/pu/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +23 -0
  240. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb +5 -0
  241. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/password_changed.text.erb +2 -0
  242. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb +5 -0
  243. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/reset_password_notify.text.erb +2 -0
  244. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb +5 -0
  245. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb +4 -0
  246. data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +10 -0
  247. data/lib/generators/pu/rodauth/templates/config/initializers/rodauth.rb.tt +3 -0
  248. data/lib/generators/pu/rodauth/templates/db/migrate/create_rodauth.rb.tt +13 -0
  249. data/lib/generators/pu/rodauth/views_generator.rb +125 -0
  250. data/lib/plutonium/auth/public_access.rb +17 -0
  251. data/lib/plutonium/auth/rodauth.rb +35 -0
  252. data/lib/plutonium/auth.rb +12 -0
  253. data/lib/plutonium/config.rb +11 -0
  254. data/lib/plutonium/core/.DS_Store +0 -0
  255. data/lib/plutonium/core/action.rb +46 -0
  256. data/lib/plutonium/core/actions/basic_action.rb +16 -0
  257. data/lib/plutonium/core/actions/collection.rb +33 -0
  258. data/lib/plutonium/core/actions/destroy_action.rb +21 -0
  259. data/lib/plutonium/core/actions/edit_action.rb +19 -0
  260. data/lib/plutonium/core/actions/interactive_action.rb +64 -0
  261. data/lib/plutonium/core/actions/new_action.rb +18 -0
  262. data/lib/plutonium/core/actions/show_action.rb +17 -0
  263. data/lib/plutonium/core/actions.rb +17 -0
  264. data/lib/plutonium/core/app_controller.rb +121 -0
  265. data/lib/plutonium/core/autodiscovery/discoverer.rb +15 -0
  266. data/lib/plutonium/core/autodiscovery/input_discoverer.rb +33 -0
  267. data/lib/plutonium/core/autodiscovery/renderer_discoverer.rb +33 -0
  268. data/lib/plutonium/core/autodiscovery.rb +13 -0
  269. data/lib/plutonium/core/controllers/authorizable.rb +56 -0
  270. data/lib/plutonium/core/controllers/bootable.rb +44 -0
  271. data/lib/plutonium/core/controllers/crud_actions.rb +128 -0
  272. data/lib/plutonium/core/controllers/entity_scoping.rb +69 -0
  273. data/lib/plutonium/core/controllers/interactive_actions.rb +210 -0
  274. data/lib/plutonium/core/controllers/presentable.rb +59 -0
  275. data/lib/plutonium/core/controllers.rb +16 -0
  276. data/lib/plutonium/core/definers/action_definer.rb +25 -0
  277. data/lib/plutonium/core/definers/field_definer.rb +19 -0
  278. data/lib/plutonium/core/definers/input_definer.rb +37 -0
  279. data/lib/plutonium/core/definers/renderer_definer.rb +35 -0
  280. data/lib/plutonium/core/definers.rb +14 -0
  281. data/lib/plutonium/core/fields/inputs/association_input.rb +26 -0
  282. data/lib/plutonium/core/fields/inputs/basic_input.rb +42 -0
  283. data/lib/plutonium/core/fields/inputs/belongs_to_input.rb +15 -0
  284. data/lib/plutonium/core/fields/inputs/factory.rb +58 -0
  285. data/lib/plutonium/core/fields/inputs/has_many_input.rb +15 -0
  286. data/lib/plutonium/core/fields/inputs/noop_input.rb +19 -0
  287. data/lib/plutonium/core/fields/inputs.rb +18 -0
  288. data/lib/plutonium/core/fields/renderers/association_renderer.rb +25 -0
  289. data/lib/plutonium/core/fields/renderers/basic_renderer.rb +27 -0
  290. data/lib/plutonium/core/fields/renderers/factory.rb +52 -0
  291. data/lib/plutonium/core/fields/renderers.rb +15 -0
  292. data/lib/plutonium/core/fields.rb +12 -0
  293. data/lib/plutonium/core/ui/collection.rb +14 -0
  294. data/lib/plutonium/core/ui/detail.rb +11 -0
  295. data/lib/plutonium/core/ui/form.rb +11 -0
  296. data/lib/plutonium/core/ui.rb +13 -0
  297. data/lib/plutonium/core.rb +16 -0
  298. data/lib/plutonium/helpers/action_buttons_helper.rb +32 -0
  299. data/lib/plutonium/helpers/application_helper.rb +22 -0
  300. data/lib/plutonium/helpers/attachment_helper.rb +62 -0
  301. data/lib/plutonium/helpers/content_helper.rb +66 -0
  302. data/lib/plutonium/helpers/display_helper.rb +83 -0
  303. data/lib/plutonium/helpers/form_helper.rb +24 -0
  304. data/lib/plutonium/helpers/menu_helper.rb +69 -0
  305. data/lib/plutonium/helpers/pagination_helper.rb +9 -0
  306. data/lib/plutonium/helpers/turbo_helper.rb +13 -0
  307. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +9 -0
  308. data/lib/plutonium/helpers.rb +31 -0
  309. data/lib/plutonium/initializers/TODO +2 -0
  310. data/lib/plutonium/initializers/pagy.rb +5 -0
  311. data/lib/plutonium/initializers/patches.rb +2 -0
  312. data/lib/plutonium/initializers/rabl.rb +60 -0
  313. data/lib/plutonium/initializers/simple_form.rb +405 -0
  314. data/lib/plutonium/lib/after_commit.rb +34 -0
  315. data/lib/plutonium/lib/bit_flags.rb +36 -0
  316. data/lib/plutonium/packaging/app.rb +125 -0
  317. data/lib/plutonium/packaging/feature.rb +18 -0
  318. data/lib/plutonium/packaging/package.rb +22 -0
  319. data/lib/plutonium/packaging.rb +9 -0
  320. data/lib/plutonium/policy/initializer.rb +22 -0
  321. data/lib/plutonium/policy/scope.rb +19 -0
  322. data/lib/plutonium/policy.rb +8 -0
  323. data/lib/plutonium/preserved__/field.rb +65 -0
  324. data/lib/plutonium/preserved__/input.rb +93 -0
  325. data/lib/plutonium/reactor/core.rb +61 -0
  326. data/lib/plutonium/reactor/resource_context.rb +15 -0
  327. data/lib/plutonium/reactor/resource_controller.rb +108 -0
  328. data/lib/plutonium/reactor/resource_interaction.rb +8 -0
  329. data/lib/plutonium/reactor/resource_policy.rb +105 -0
  330. data/lib/plutonium/reactor/resource_presenter.rb +42 -0
  331. data/lib/plutonium/reactor/resource_record.rb +123 -0
  332. data/lib/plutonium/reactor.rb +13 -0
  333. data/lib/plutonium/refinements/parameter_refinements.rb +26 -0
  334. data/lib/plutonium/simple_form_components/attachment_component.rb +77 -0
  335. data/lib/plutonium/simple_form_components/input_group_component.rb +14 -0
  336. data/lib/plutonium/version.rb +3 -0
  337. data/lib/plutonium.rb +31 -0
  338. data/plutonium.gemspec +47 -0
  339. data/public/.keep +0 -0
  340. data/public/plutonium-assets/application.css +24280 -0
  341. data/public/plutonium-assets/application.js +31420 -0
  342. data/public/plutonium-assets/fonts/bootstrap-icons.woff +0 -0
  343. data/public/plutonium-assets/fonts/bootstrap-icons.woff2 +0 -0
  344. data/public/plutonium-assets/logo.png +0 -0
  345. data/sig/plutonium.rbs +4 -0
  346. metadata +519 -0
@@ -0,0 +1,78 @@
1
+ <%
2
+ # locals: {
3
+ # table: "instance of Plutonium::UI::Builder::Collection"
4
+ # }
5
+
6
+ resource_class = table.resource_class
7
+ resources = table.records.to_a
8
+ record_actions = table.actions.collection_record_actions
9
+ search_object = table.search_object
10
+ %>
11
+ <div class="row">
12
+ <% unless resources.any? %>
13
+ <div class="col-12">
14
+ <%=
15
+ render 'empty_card', message: "You have no #{resource_name_plural(resource_class).downcase}" do
16
+ if policy(resource_class).create?
17
+ link_to "Create #{resource_name(resource_class)}", adapt_route_args(resource_class, action: :new), class: 'btn btn-outline-primary'
18
+ end
19
+ end
20
+ %>
21
+ </div>
22
+ <% else %>
23
+ <div id="table-container" class="col-12 table-responsive max-vh-75 pb-2" data-controller="scroll-preserver" data-action="scroll->scroll-preserver#scrolled" >
24
+ <table class="table table-hover table-striped my-0">
25
+ <thead class="sticky-top">
26
+ <tr>
27
+ <th scope="col" class="text-center resource-table-col-id"><%= sort_link(search_object, :id, '#', class: "text-decoration-none") %></th>
28
+ <% table.fields.each do |name, field| %>
29
+ <th scope="col" class="resource-table-col-attr">
30
+ <% if resource_class.ransortable_attributes.include? name.to_s %>
31
+ <%= sort_link(search_object, name, field.label, class: "text-decoration-none") %>
32
+ <% else %>
33
+ <%= field.label %>
34
+ <% end %>
35
+ </th>
36
+ <% end %>
37
+ <th scope="col" class="text-center resource-table-col-actions">Actions</th>
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ <% resources.each do |resource| %>
42
+ <tr class="align-middle">
43
+ <td class="text-center text-truncate resource-table-cell-id">
44
+ <% if policy(resource).show? %>
45
+ <%= link_to resource.to_param, adapt_route_args(resource), class: "text-decoration-none" %>
46
+ <% else %>
47
+ <%= resource.to_param %>
48
+ <% end %>
49
+ </td>
50
+ <% table.fields.each do |name, field| %>
51
+ <%
52
+ options = field.options
53
+ max_width = options.delete :pu_max_width
54
+ %>
55
+ <td class='text-truncate resource-table-cell-attr' style="max-width: <%= max_width %>px;">
56
+ <%= field.render self, resource %>
57
+ </td>
58
+ <% end %>
59
+ <td class="text-center resource-table-cell-actions">
60
+ <%=
61
+ render partial: 'table_actions', locals: {
62
+ actions: record_actions.permitted_for(policy(resource)),
63
+ resource: resource
64
+ }
65
+ %>
66
+ </td>
67
+ </tr>
68
+ <% end %>
69
+ </tbody>
70
+ </table>
71
+ </div>
72
+ <% if table.pagination.present? %>
73
+ <div class="col-12">
74
+ <%= render 'pagination', pagination: table.pagination %>
75
+ </div>
76
+ <% end %>
77
+ <% end %>
78
+ </div>
@@ -0,0 +1,15 @@
1
+ <%
2
+ # locals: {
3
+ # resource: "the resource to generate actions for",
4
+ # actions: "list of actions to generate",
5
+ # }
6
+ %>
7
+ <% actions.values.each do |action| %>
8
+ <%=
9
+ table_action_button adapt_route_args(resource, action: action.route_options.action, **action.route_options.options),
10
+ icon: action.icon,
11
+ button_class: action.action_class,
12
+ method: action.route_options.method,
13
+ confirm: action.confirmation
14
+ %>
15
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <div aria-live="polite" aria-atomic="true" class="position-relative" data-controller="toasts">
2
+ <div class="toast-container top-0 end-0 p-3 ">
3
+ <% flash.each do |type, msg| %>
4
+ <%
5
+ type = { alert: :danger, error: :danger, warning: :warning, success: :success }[type.to_sym] || :primary
6
+ title = { danger: 'Error', warning: 'Warning', success: 'Success' }[type] || 'Info'
7
+ icon = {
8
+ danger: 'exclamation-triangle-fill',
9
+ warning: 'exclamation-diamond',
10
+ success: 'check-circle-fill'
11
+ }[type] || 'exclamation-circle'
12
+ %>
13
+ <div class="toast text-bg-<%= type %>" role="alert" aria-live="assertive" aria-atomic="true" data-turbo-temporary>
14
+ <div class="toast-header">
15
+ <i class="bi bi-<%= icon %>"></i>&nbsp;
16
+ <strong class="me-auto"><%= title %></strong>
17
+ <small class="text-body-secondary"><%= timeago(DateTime.now) %></small>
18
+ <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
19
+ </div>
20
+ <div class="toast-body">
21
+ <%= msg %>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+ </div>
26
+ </div>
@@ -0,0 +1,22 @@
1
+ <%
2
+ # locals: {
3
+ # parent?: "parent of the resource to render toolbar for",
4
+ # resource?: "resource to render toolbar for",
5
+ # resource_class: "Class of resource being rendered",
6
+ # }
7
+
8
+ resource = nil unless local_assigns[:resource].present?
9
+ associations = nil unless local_assigns[:associations].present?
10
+ %>
11
+ <div class="row justify-content-between py-2 mb-3 border-bottom">
12
+ <div class="col-sm-6">
13
+ <%= render partial: 'toolbar_breadcrumbs', locals: {
14
+ resource: resource,
15
+ resource_class: resource_class,
16
+ parent: parent
17
+ } %>
18
+ </div>
19
+ <div class="col-sm-6 text-end">
20
+ <%= yield %>
21
+ </div>
22
+ </div>
@@ -0,0 +1,16 @@
1
+ <%
2
+ # locals: {
3
+ # resource: "the resource to generate actions for",
4
+ # actions: "list of actions to generate",
5
+ # }
6
+ %>
7
+ <% actions.values.each do |action| %>
8
+ <%=
9
+ toolbar_action_button adapt_route_args(resource, action: action.route_options.action, **action.route_options.options),
10
+ icon: action.icon,
11
+ button_class: action.action_class,
12
+ method: action.route_options.method,
13
+ confirm: action.confirmation,
14
+ turbo_frame: action.turbo_frame
15
+ %>
16
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%#
2
+ locals: {
3
+ parent?: "parent of the resource to render toolbar for",
4
+ resource?: "resource to render toolbar for",
5
+ resource_class: "Class of resource being rendered",
6
+ }
7
+ %>
8
+ <nav aria-label="breadcrumb" style="--bs-breadcrumb-divider: url(&#34;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='%236c757d'/%3E%3C/svg%3E&#34;);">
9
+ <ol class="breadcrumb my-1">
10
+ <% if parent.present? %>
11
+ <li class="breadcrumb-item"><%= link_to resource_name_plural(parent.class), adapt_route_args(parent.class, use_parent: false), class: 'text-decoration-none' %></li>
12
+ <li class="breadcrumb-item"><%= link_to display_name_of(parent), adapt_route_args(parent, use_parent: false), class: 'text-decoration-none' %></li>
13
+ <% end %>
14
+
15
+ <% if resource.present? %>
16
+ <li class="breadcrumb-item"><%= link_to resource_name_plural(resource_class), adapt_route_args(resource_class), class: 'text-decoration-none' %></li>
17
+ <% if /edit|update|.*interactive_resource.*/.match? action_name %>
18
+ <li class="breadcrumb-item"><%= link_to display_name_of(resource), adapt_route_args(resource), class: 'text-decoration-none' %></li>
19
+ <li class="breadcrumb-item active" aria-current="page"><%= (params[:interactive_action] || 'Edit').titleize %></li>
20
+ <% else %>
21
+ <li class="breadcrumb-item active" aria-current="page"><%= resource.persisted? ? display_name_of(resource) : 'New' %></li>
22
+ <% end %>
23
+ <% else %>
24
+ <% if /.*interactive_resource.*/.match? action_name %>
25
+ <li class="breadcrumb-item"><%= link_to resource_name_plural(resource_class), adapt_route_args(resource_class), class: 'text-decoration-none' %></li>
26
+ <li class="breadcrumb-item active" aria-current="page"><%= params[:interactive_action].titleize %></li>
27
+ <% else %>
28
+ <li class="breadcrumb-item"><%= resource_name_plural(resource_class) %></li>
29
+ <% end %>
30
+ <% end %>
31
+ </ol>
32
+ </nav>
@@ -0,0 +1,27 @@
1
+ <%
2
+ # locals: {
3
+ # search_object: "the object to build the search form for",
4
+ # search_field: "search input field name",
5
+ # }
6
+ %>
7
+ <%= search_form_for adapt_route_args(search_object), class: "d-inline", html: { data: { controller: "form", turbo_frame: nil } } do |f| %>
8
+ <label for="search" class="visually-hidden">Search</label>
9
+ <% if search_field.present? %>
10
+ <%=
11
+ f.search_field search_field, id: "search",
12
+ class: "form-control form-control-sm d-inline w-auto",
13
+ # since ransack trims strings, we instead
14
+ # use the url param because it preserves trailing spaces
15
+ # that way, when the frame reloads the user gets to continue typing
16
+ value: params[:q].try(:[], search_field),
17
+ placeholder: "search...",
18
+ data: {
19
+ action: "form#submit",
20
+ form_target: "focus"
21
+ }
22
+
23
+ %>
24
+ <% end %>
25
+ <%# preserve sorting %>
26
+ <%= f.hidden_field :s, value: params[:q].try(:[], :s) %>
27
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <% content_for :toolbar do %>
2
+ <%=
3
+ render partial: 'toolbar', locals: {
4
+ resource_class: resource_class,
5
+ resource: @form.record,
6
+ parent: current_parent,
7
+ }
8
+ %>
9
+ <% end %>
10
+ <%= render "form", form: @form %>
@@ -0,0 +1,3 @@
1
+ collection @errors, root: :errors, object_root: false
2
+
3
+ attributes :attribute, :detail, :message, :full_message
@@ -0,0 +1,19 @@
1
+ <% content_for :toolbar do %>
2
+ <%= render 'toolbar', resource_class: resource_class,
3
+ parent: current_parent do %>
4
+ <%=
5
+ render partial: 'toolbar_search_input', locals: {
6
+ search_object: @collection.search_object,
7
+ search_field: nil
8
+ }
9
+ %>
10
+ <%=
11
+ render partial: 'toolbar_actions', locals: {
12
+ actions: @collection.actions.collection_actions.permitted_for(policy(resource_class)),
13
+ resource: resource_class
14
+ }
15
+ %>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <%= render 'table', table: @collection %>
@@ -0,0 +1,3 @@
1
+ collection @resource_records, root: resource_class.to_s.demodulize.underscore.pluralize.to_sym, object_root: false
2
+
3
+ extends "_resource"
@@ -0,0 +1,11 @@
1
+ <% content_for :toolbar do %>
2
+ <%=
3
+ render partial: 'toolbar', locals: {
4
+ resource_class: resource_class,
5
+ resource: resource_record,
6
+ parent: current_parent,
7
+ }
8
+ %>
9
+ <% end %>
10
+
11
+ <%= render "interactive_resource_action_form", interactive_action: current_interactive_action %>
@@ -0,0 +1,11 @@
1
+ <% content_for :toolbar do %>
2
+ <%=
3
+ render partial: 'toolbar', locals: {
4
+ resource_class: resource_class,
5
+ resource: resource_record,
6
+ parent: current_parent,
7
+ }
8
+ %>
9
+ <% end %>
10
+
11
+ <%= render "interactive_resource_action_form", interactive_action: current_interactive_action %>
@@ -0,0 +1,11 @@
1
+ <% content_for :toolbar do %>
2
+ <%=
3
+ render partial: 'toolbar', locals: {
4
+ resource_class: resource_class,
5
+ resource: resource_record,
6
+ parent: current_parent,
7
+ }
8
+ %>
9
+ <% end %>
10
+
11
+ <%= render "interactive_resource_action_form", interactive_action: current_interactive_action %>
@@ -0,0 +1,11 @@
1
+ <% content_for :toolbar do %>
2
+ <%=
3
+ render partial: 'toolbar', locals: {
4
+ resource_class: resource_class,
5
+ resource: @form.record,
6
+ parent: current_parent,
7
+ }
8
+ %>
9
+ <% end %>
10
+
11
+ <%= render "form", form: @form %>
@@ -0,0 +1,16 @@
1
+ # Example View Lookup
2
+
3
+ Missing partial demo_app/blogs/_nav_user, blogs/_nav_user, resource/_nav_user with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}.
4
+
5
+ Searched in:
6
+
7
+ - "/Users/user/plutonium/demo/packages/demo_app/app/views"
8
+ - "/Users/user/plutonium/demo/app/views"
9
+ - "/Users/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/turbo-rails-2.0.3/app/views"
10
+ - "/Users/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actiontext-7.1.3/app/views"
11
+ - "/Users/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/actionmailbox-7.1.3/app/views"
12
+ - "/Users/user/plutonium/plutonium/app/views"
13
+
14
+ # Partials vs Shared Partials
15
+
16
+ Rails does not apply namespace lookup to shared partials. So instead, we go with normal partials.
@@ -0,0 +1,14 @@
1
+ <% content_for :toolbar do %>
2
+ <%= render 'toolbar', resource_class: resource_class,
3
+ resource: @detail.record,
4
+ parent: current_parent do %>
5
+ <%=
6
+ render partial: 'toolbar_actions', locals: {
7
+ actions: @detail.actions.record_actions.permitted_for(policy(@detail.record)),
8
+ resource: @detail.record
9
+ }
10
+ %>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <%= render "resource", details: @detail %>
@@ -0,0 +1,3 @@
1
+ object @resource_record
2
+
3
+ extends "_resource"
data/brakeman.ignore ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "ignored_warnings": [
3
+ {
4
+ "warning_type": "Cross-Site Request Forgery",
5
+ "warning_code": 7,
6
+ "fingerprint": "1cb8570b8c91f38317cdf909e01e7016359846174f427e86011633c344d30fc3",
7
+ "check_name": "ForgerySetting",
8
+ "message": "`protect_from_forgery` should be called in `Plutonium::Reactor::ResourceController`",
9
+ "file": "lib/plutonium/reactor/resource_controller.rb",
10
+ "line": 10,
11
+ "link": "https://brakemanscanner.org/docs/warning_types/cross-site_request_forgery/",
12
+ "code": null,
13
+ "render_path": null,
14
+ "location": {
15
+ "type": "controller",
16
+ "controller": "Plutonium::Reactor::ResourceController"
17
+ },
18
+ "user_input": null,
19
+ "confidence": "High",
20
+ "cwe_id": [
21
+ 352
22
+ ],
23
+ "note": "this is tested and confirmed to be a false flag"
24
+ },
25
+ {
26
+ "warning_type": "Mass Assignment",
27
+ "warning_code": 70,
28
+ "fingerprint": "873ee0d868e06a32e8ff387a38ddb8c6183a419813d5c20122fa9c3a887f4e54",
29
+ "check_name": "MassAssignment",
30
+ "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
31
+ "file": "lib/plutonium/reactor/resource_controller.rb",
32
+ "line": 58,
33
+ "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
34
+ "code": "params.require(resource_param_key).permit!",
35
+ "render_path": null,
36
+ "location": {
37
+ "type": "method",
38
+ "class": "Plutonium::Reactor::ResourceController",
39
+ "method": "resource_params"
40
+ },
41
+ "user_input": null,
42
+ "confidence": "Medium",
43
+ "cwe_id": [
44
+ 915
45
+ ],
46
+ "note": "we manually filter params"
47
+ }
48
+ ],
49
+ "updated": "2024-02-18 00:37:39 +0000",
50
+ "brakeman_version": "6.1.2"
51
+ }
@@ -0,0 +1,74 @@
1
+ # require 'active_model/validations/each_validator'
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ # Based on https://gist.github.com/ssimeonov/6519423
6
+ #
7
+ # Validates the values of an Array with other validators.
8
+ # Generates error messages that include the index and value of
9
+ # invalid elements.
10
+ #
11
+ # Example:
12
+ #
13
+ # validates :values, array: { presence: true, inclusion: { in: %w{ big small } } }
14
+ #
15
+ class ArrayValidator < EachValidator
16
+ attr_reader :record, :attribute, :proxy_attribute
17
+
18
+ def validate_each(record, attribute, values)
19
+ @record = record
20
+ @attribute = attribute
21
+
22
+ # Cache any existing errors temporarily.
23
+ @existing_errors = record.errors.delete(attribute) || []
24
+
25
+ # Run validations
26
+ validate_each_internal values
27
+
28
+ # Restore any existing errors.
29
+ return if @existing_errors.blank?
30
+
31
+ @existing_errors.each { |e| record.errors.add attribute, e }
32
+ end
33
+
34
+ private
35
+
36
+ def validate_each_internal(values)
37
+ [values].flatten.each_with_index do |value, index|
38
+ options.except(:if, :unless, :on, :strict).each do |key, args|
39
+ validator_options = {attributes: attribute}
40
+ validator_options.merge!(args) if args.is_a?(Hash)
41
+
42
+ next if skip? value, validator_options
43
+
44
+ validator = validator_class_for(key).new(validator_options)
45
+ validator.validate_each(record, attribute, value)
46
+ end
47
+ maybe_normalize_errors index
48
+ end
49
+ end
50
+
51
+ def maybe_normalize_errors(index)
52
+ errors = record.errors.delete attribute
53
+ return if errors.nil?
54
+
55
+ @existing_errors += errors.map { |e| "item #{index + 1} #{e}" }
56
+ end
57
+
58
+ def skip?(value, validator_options)
59
+ return true if value.nil? && validator_options[:allow_nil]
60
+
61
+ true if value.blank? && validator_options[:allow_blank]
62
+ end
63
+
64
+ def validator_class_for(key)
65
+ validator_class_name = "#{key.to_s.camelize}Validator"
66
+ begin
67
+ validator_class_name.constantize
68
+ rescue NameError
69
+ "ActiveModel::Validations::#{validator_class_name}".constantize
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ # Validates that a file is attached
6
+ #
7
+ # Example:
8
+ #
9
+ # validates :logo, attached: true
10
+ #
11
+ class AttachedValidator < EachValidator
12
+ def validate_each(record, attribute, value)
13
+ record.errors.add(attribute, (options[:message] || "must be attached")) unless value.attached?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ # Validates that the value is a url
6
+ #
7
+ # Example:
8
+ #
9
+ # validates :website_url, url: true
10
+ #
11
+ class UrlValidator < EachValidator
12
+ URL_PATTERN = URI::DEFAULT_PARSER.make_regexp(%w[http https]).freeze
13
+
14
+ attr_reader :record, :attribute, :value
15
+
16
+ def validate_each(record, attribute, value)
17
+ @record = record
18
+ @attribute = attribute
19
+ @value = value
20
+
21
+ return if skip?
22
+ return unless validate_url
23
+
24
+ nil unless maybe_validate_image_url
25
+ end
26
+
27
+ private
28
+
29
+ def skip?
30
+ return true if value.nil? && options[:allow_nil]
31
+
32
+ true if value.blank? && options[:allow_blank]
33
+ end
34
+
35
+ def validate_url
36
+ return true if URL_PATTERN.match?(value)
37
+
38
+ record.errors.add attribute, (options[:message] || "is not a valid URL")
39
+ false
40
+ end
41
+
42
+ def maybe_validate_image_url
43
+ return true unless options[:image].present?
44
+ return true unless FastImage.type(value).nil?
45
+
46
+ record.errors.add(attribute, (options[:message] || "is not a valid image URL"))
47
+ false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plutonium_generators"
4
+
5
+ module Pu
6
+ module Core
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ desc "Set up the base requirements for Plutonium"
13
+
14
+ def start
15
+ setup_packaging_system
16
+ install_required_gems
17
+ setup_app
18
+ rescue => e
19
+ exception "#{self.class} failed:", e
20
+ end
21
+
22
+ private
23
+
24
+ def setup_packaging_system
25
+ copy_file "config/packages.rb"
26
+ create_file "packages/.keep"
27
+ insert_into_file "config/application.rb", "\nrequire_relative \"packages\"\n", after: /Bundler\.require.*\n/
28
+ # insert_into_file "config/application.rb", indent("Plutonium.configure_rails config\n\n", 4), after: /.*< Rails::Application\n/
29
+ end
30
+
31
+ def setup_app
32
+ directory "config"
33
+ directory "app"
34
+ end
35
+
36
+ def install_required_gems
37
+ # invoke "pu:gem:simple_form"
38
+ # invoke "pu:gem:pagy"
39
+ # invoke "pu:gem:rabl"
40
+ end
41
+ end
42
+ end
43
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ class ResourceController < Plutonium::Reactor::ResourceController
2
+ end
@@ -0,0 +1,2 @@
1
+ class ResourceInteraction < Plutonium::Reactor::ResourceInteraction
2
+ end
@@ -0,0 +1,5 @@
1
+ class ResourceRecord < ApplicationRecord
2
+ include Plutonium::Reactor::ResourceRecord
3
+
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,2 @@
1
+ class ResourcePolicy < Plutonium::Reactor::ResourcePolicy
2
+ end
@@ -0,0 +1,2 @@
1
+ class ResourcePresenter < Plutonium::Reactor::ResourcePresenter
2
+ end
@@ -0,0 +1,2 @@
1
+ # get the ball rolling
2
+ Plutonium::Reactor::Core.achieve_criticality!
@@ -0,0 +1,3 @@
1
+ Dir[File.expand_path("../packages/**/lib/engine.rb", __dir__)].each do |package|
2
+ load package
3
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plutonium_generators"
4
+
5
+ module Pu
6
+ module Gem
7
+ class PagyGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ desc "Install Pagy"
13
+
14
+ def start
15
+ Bundler.with_unbundled_env do
16
+ run "bundle add pagy"
17
+ end
18
+
19
+ directory "config"
20
+ rescue => e
21
+ exception "#{self.class} failed:", e
22
+ end
23
+ end
24
+ end
25
+ end
File without changes