plutonium 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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