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,19 @@
1
+ require "pundit"
2
+
3
+ module Plutonium
4
+ module Policy
5
+ class Scope
6
+ include Plutonium::Policy::Initializer
7
+
8
+ def resolve
9
+ scope = context.resource_class.all
10
+ if @context.parent.present?
11
+ scope = scope.associated_with(@context.parent)
12
+ elsif @context.scope.present?
13
+ scope = scope.associated_with(@context.scope)
14
+ end
15
+ scope
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Plutonium
2
+ module Policy
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Initializer
6
+ autoload :Scope
7
+ end
8
+ end
@@ -0,0 +1,65 @@
1
+ module Plutonium
2
+ module UI
3
+ class Field
4
+ attr_reader :name, :label, :helper, :options
5
+
6
+ def initialize(name, **options)
7
+ @name = name
8
+ @label = options.delete :label
9
+ @helper = options.delete :helper
10
+ @options = options
11
+ end
12
+
13
+ def label
14
+ @label ||= name.to_s.titleize(keep_id_suffix: true)
15
+ end
16
+
17
+ def self.build(name, type:, **options)
18
+ options[:helper] ||= case type
19
+ when :string, :text, :citext
20
+ :display_url_value if name.ends_with? "_url"
21
+ when :integer, :float, :decimal
22
+ :display_numeric_value
23
+ when :datetime, :timestamp, :time, :date
24
+ :display_datetime_value
25
+ when :boolean
26
+ :display_boolean_value
27
+ when :association
28
+ :display_association_value
29
+ when :attachment
30
+ :display_attachment_value
31
+ end
32
+
33
+ # binary: { name: "blob" },
34
+ # blob: { name: "blob" },
35
+ # json: { name: "json" },
36
+
37
+ options[:pu_max_width] ||= 400 if %i[string text citext].include? type
38
+ options[:pu_max_width] ||= 250
39
+
40
+ new name, **options
41
+ end
42
+
43
+ def self.for_attribute(model_class, name, type: nil, **options)
44
+ association = attachment = nil
45
+ column = model_class.column_for_attribute name if model_class.respond_to? :column_for_attribute
46
+ if model_class.respond_to? :reflect_on_association
47
+ association = model_class.reflect_on_association name
48
+ attachment = model_class.reflect_on_association(:"#{name}_attachment") || model_class.reflect_on_association(:"#{name}_attachments")
49
+ end
50
+
51
+ if attachment.present?
52
+ type ||= :attachment
53
+ options[:stack_multiple] = false if options[:stack_multiple].nil?
54
+ elsif association.present?
55
+ type ||= :association if %i[belongs_to has_one].include? association.macro
56
+ elsif column.present?
57
+ type ||= column.type
58
+ options[:stack_multiple] = column.array? if options[:stack_multiple].nil? && column.respond_to?(:array?)
59
+ end
60
+
61
+ build name, type:, **options
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,93 @@
1
+ module Plutonium
2
+ module UI
3
+ class Input
4
+ attr_reader :name, :options
5
+
6
+ def initialize(name, **options)
7
+ @name = name
8
+ @options = options
9
+ end
10
+
11
+ def self.build(name, type:, **options)
12
+ multiple = options[:multiple]
13
+
14
+ definition = {}
15
+ case type
16
+ when :string, :text, :citext
17
+ definition = {
18
+ input_html: {
19
+ data: {controller: "textarea-autogrow"}
20
+ }
21
+ }
22
+ when :datetime, :timestamp, :time, :date
23
+ definition = {
24
+ html5: false
25
+ }
26
+ when :slim_select
27
+ definition = {
28
+ wrapper: :slim_select,
29
+ input_html: {multiple:}
30
+ }
31
+
32
+ if multiple
33
+ placeholder = options[:placeholder] || "Select #{name.to_s.humanize(capitalize: false).pluralize}"
34
+ definition.deep_merge! input_html: {
35
+ data: {
36
+ slim_select_placeholder_value: placeholder,
37
+ slim_select_close_on_select_value: false
38
+ }
39
+ }
40
+ else
41
+ placeholder = options[:placeholder] || "Select #{name.to_s.humanize(capitalize: false)}"
42
+ definition.deep_merge! include_blank: placeholder,
43
+ input_html: {
44
+ data: {slim_select_allow_deselect_value: true}
45
+ }
46
+ end
47
+ when :quill
48
+ definition = {wrapper: :quill}
49
+ when :money
50
+ currency = options.delete(:currency) || "$"
51
+ definition = {wrapper: :input_group, prepend: currency}
52
+ when :attachment
53
+ type = :file
54
+ definition = {
55
+ input_html: {multiple:},
56
+ attachment: true,
57
+ direct_upload: true
58
+ }
59
+ when :association
60
+ definition = {
61
+ as: :association
62
+ }
63
+ end
64
+
65
+ options = definition.deep_merge options
66
+
67
+ new name, **options
68
+ end
69
+
70
+ def self.for_attribute(model_class, name, type: nil, **options)
71
+ column = model_class.column_for_attribute name if model_class.respond_to? :column_for_attribute
72
+ if model_class.respond_to? :reflect_on_association
73
+ attachment = model_class.reflect_on_association(:"#{name}_attachment") || model_class.reflect_on_association(:"#{name}_attachments")
74
+ association = model_class.reflect_on_association(name)
75
+ end
76
+
77
+ type ||= :slim_select if options.key? :collection
78
+
79
+ if attachment.present?
80
+ type ||= :attachment
81
+ options[:multiple] = true if options[:multiple].nil? && attachment.macro == :has_many
82
+ elsif association.present?
83
+ type ||= :association
84
+ elsif column.present?
85
+ type ||= column.type
86
+ options[:multiple] = column.array? if options[:multiple].nil? && column.respond_to?(:array?)
87
+ end
88
+
89
+ build name, type:, **options
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,61 @@
1
+ # Be sure to restart your server when you modify this file.
2
+ # This is a glorified initializer
3
+
4
+ module Plutonium
5
+ module Reactor
6
+ class Core
7
+ def self.achieve_criticality!
8
+ Dir[Plutonium.lib_root.join("initializers", "**", "*.rb")].each do |file|
9
+ require file
10
+ end
11
+
12
+ # setup a middleware to serve our assets
13
+ Rails.application.config.middleware.insert_before(
14
+ ActionDispatch::Static,
15
+ Rack::Static,
16
+ urls: ["/plutonium-assets"],
17
+ root: Plutonium.root.join("public"),
18
+ cascade: true
19
+ )
20
+
21
+ start_reloader!
22
+ end
23
+
24
+ def self.start_reloader!
25
+ return unless Plutonium::Config.reload_files
26
+
27
+ # GLORIOUS hot reloading!!!
28
+ @listener ||= begin
29
+ require "listen"
30
+
31
+ plutonium_lib_dir = Plutonium.lib_root.to_s
32
+ packages_dir = Rails.root.join("packages/").to_s
33
+ listener = Listen.to(plutonium_lib_dir, packages_dir, only: /\.rb$/) do |modified, added, removed|
34
+ (modified + added).each do |file|
35
+ if file.starts_with?(packages_dir)
36
+ # if package file was added, ignore it
37
+ # otherwise rails gets mad at us since engines cannot be loaded after initial boot
38
+ # TODO: check if guard has apis to control reloading dynamically
39
+ next if added.include? file
40
+
41
+ case File.basename(file)
42
+ when "engine.rb"
43
+ # reload engines. due to how we load packages, rails does not support
44
+ load file
45
+ # reload routes to pick up any registration changes
46
+ Rails.application.reload_routes!
47
+ else
48
+ # non engine package files are reloaded by rails automatically
49
+ end
50
+ else
51
+ load file
52
+ end
53
+ end
54
+ end
55
+ listener.start
56
+ listener
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,15 @@
1
+ module Plutonium
2
+ module Reactor
3
+ class ResourceContext
4
+ attr_reader :user, :resource_record, :resource_class, :parent, :scope
5
+
6
+ def initialize(user:, resource_record:, resource_class:, parent: nil, scope: nil)
7
+ @user = user
8
+ @resource_record = resource_record
9
+ @resource_class = resource_class || resource_record&.class
10
+ @parent = parent
11
+ @scope = scope
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,108 @@
1
+ require "action_controller"
2
+ require "pagy"
3
+ require "ransack"
4
+
5
+ require File.expand_path("refinements/parameter_refinements", Plutonium.lib_root)
6
+ using Plutonium::Refinements::ParameterRefinements
7
+
8
+ module Plutonium
9
+ module Reactor
10
+ class ResourceController < ActionController::Base
11
+ # remove this controller from the view lookup
12
+ # has the side effect of marking all public methods as private.
13
+ abstract!
14
+
15
+ include Pagy::Backend
16
+ include Plutonium::Core::Controllers::Bootable
17
+ include Plutonium::Core::Controllers::Authorizable
18
+ include Plutonium::Core::Controllers::Presentable
19
+
20
+ def self.inherited(child)
21
+ # Include our actions after we are inherited else they are marked as private due to our call to abstract!
22
+ child.send :include, Plutonium::Core::Controllers::CrudActions
23
+ child.send :include, Plutonium::Core::Controllers::InteractiveActions
24
+
25
+ # TODO: move this into interactive actions module
26
+ child.helper_method :current_interactive_action
27
+ super
28
+ end
29
+
30
+ add_flash_types :success, :warning, :error
31
+ append_view_path File.expand_path("app/views", Plutonium.root)
32
+
33
+ layout "resource"
34
+ helper Plutonium::Helpers
35
+
36
+ before_action :set_page_title
37
+ before_action :set_sidebar_menu
38
+
39
+ # https://github.com/ddnexus/pagy/blob/master/docs/extras/headers.md#headers
40
+ after_action { pagy_headers_merge(@pagy) if @pagy }
41
+
42
+ private
43
+
44
+ # def current_layout
45
+ # send :_layout, lookup_context, []
46
+ # end
47
+
48
+ # Resource
49
+
50
+ def resource_record
51
+ @resource_record ||= (policy_scope(resource_class).from_path_param(params[:id]).first! if params[:id].present?)
52
+ end
53
+ helper_method :resource_record
54
+
55
+ def resource_params
56
+ # Example of documenting an ignore in the source code
57
+ # NOTE: Brakeman warning ignored for MassAssignment because inputs are filtered manually
58
+ input_params = params.require(resource_param_key).permit!.nilify.to_h
59
+
60
+ # Override any entity scoping params
61
+ input_params[scoped_entity_param_key] = current_scoped_entity if scoped_to_entity?
62
+ input_params[:"#{scoped_entity_param_key}_id"] = current_scoped_entity.id if scoped_to_entity?
63
+ # Override any parent params
64
+ input_params[parent_input_param] = current_parent if current_parent.present?
65
+ input_params[:"#{parent_input_param}_id"] = current_parent.id if current_parent.present?
66
+
67
+ current_presenter.defined_inputs_for(permitted_attributes)
68
+ .values.map { |input| input.collect input_params }
69
+ .reduce(:merge)
70
+ end
71
+
72
+ def resource_param_key
73
+ resource_class.model_name.singular_route_key
74
+ end
75
+ helper_method :resource_param_key
76
+
77
+ # Layout
78
+
79
+ def set_page_title
80
+ @page_title = "Pluton8"
81
+ end
82
+
83
+ def set_sidebar_menu
84
+ @sidebar_menu = build_sidebar_menu
85
+ end
86
+
87
+ def build_sidebar_menu
88
+ raise NotImplementedError, "#{self.class}#build_sidebar_menu"
89
+ end
90
+
91
+ def resource_context
92
+ Plutonium::Reactor::ResourceContext.new(
93
+ user: current_user,
94
+ resource_class:,
95
+ resource_record: @resource_record,
96
+ parent: current_parent,
97
+ scope: scoped_to_entity? ? current_scoped_entity : nil
98
+ )
99
+ end
100
+
101
+ ############
102
+
103
+ def current_package
104
+ @current_package ||= self.class.module_parents[-2]
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,8 @@
1
+ require "active_interaction"
2
+
3
+ module Plutonium
4
+ module Reactor
5
+ class ResourceInteraction < ActiveInteraction::Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,105 @@
1
+ module Plutonium
2
+ module Reactor
3
+ class ResourcePolicy
4
+ include Plutonium::Policy::Initializer
5
+
6
+ class Scope < Plutonium::Policy::Scope
7
+ end
8
+
9
+ # Core actions
10
+
11
+ def create?
12
+ true
13
+ end
14
+
15
+ def read?
16
+ true
17
+ end
18
+
19
+ def update?
20
+ true
21
+ end
22
+
23
+ def destroy?
24
+ true
25
+ end
26
+
27
+ # Inferred actions
28
+
29
+ def index?
30
+ read?
31
+ end
32
+
33
+ def new?
34
+ create?
35
+ end
36
+
37
+ def show?
38
+ read?
39
+ end
40
+
41
+ def edit?
42
+ update?
43
+ end
44
+
45
+ # Core attributes
46
+
47
+ def permitted_attributes_for_create
48
+ autodetect_fields_for :permitted_attributes_for_create
49
+ end
50
+
51
+ def permitted_attributes_for_read
52
+ autodetect_fields_for :permitted_attributes_for_read
53
+ end
54
+
55
+ def permitted_attributes_for_update
56
+ permitted_attributes_for_create
57
+ end
58
+
59
+ # Inferred attributes
60
+
61
+ def permitted_attributes_for_index
62
+ permitted_attributes_for_read
63
+ end
64
+
65
+ def permitted_attributes_for_show
66
+ permitted_attributes_for_read
67
+ end
68
+
69
+ def permitted_attributes_for_new
70
+ permitted_attributes_for_create
71
+ end
72
+
73
+ def permitted_attributes_for_edit
74
+ permitted_attributes_for_update
75
+ end
76
+
77
+ def permitted_associations
78
+ []
79
+ end
80
+
81
+ private
82
+
83
+ def autodetect_fields_for(method_name)
84
+ maybe_warn_autodetect_usage method_name
85
+
86
+ context.resource_class.resource_field_names
87
+ end
88
+
89
+ def maybe_warn_autodetect_usage(method)
90
+ raise "Resource field auto-detection: #{self.class}##{method} outside development" unless Rails.env.development?
91
+
92
+ Rails.logger.warn %(
93
+ 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
94
+
95
+ Resource field auto-detection: #{self.class}##{method}
96
+
97
+ Auto-detected resource fields result in security holes and will fail outside of development.
98
+ Override #{context.resource_class}Policy or #{self.class} with your own ##{method} method.
99
+
100
+ 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,42 @@
1
+ module Plutonium
2
+ module Reactor
3
+ class ResourcePresenter
4
+ include Plutonium::Core::Definers::FieldDefiner
5
+ include Plutonium::Core::Definers::ActionDefiner
6
+
7
+ def initialize(context)
8
+ @context = context
9
+
10
+ define_standard_actions
11
+ define_actions
12
+ define_fields
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :context
18
+
19
+ def define_fields
20
+ # override this in child presenters for custom field definitions
21
+ end
22
+
23
+ def define_actions
24
+ # override this in child presenters for custom action definitions
25
+ end
26
+
27
+ def define_standard_actions
28
+ define_action Plutonium::Core::Actions::NewAction.new(:new)
29
+ define_action Plutonium::Core::Actions::ShowAction.new(:show)
30
+ define_action Plutonium::Core::Actions::EditAction.new(:edit)
31
+ define_action Plutonium::Core::Actions::DestroyAction.new(:destroy)
32
+ end
33
+
34
+ # TODO: move this to its own definer
35
+ def define_interactive_action(name, interaction:, **)
36
+ define_action Plutonium::Core::Actions::InteractiveAction.new(name, interaction:, **)
37
+ end
38
+
39
+ def resource_class = context.resource_class
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,123 @@
1
+ module Plutonium
2
+ module Reactor
3
+ module ResourceRecord
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ scope :from_path_param, ->(param) { where(id: param) }
8
+
9
+ scope :associated_with, ->(record) do
10
+ named_scope = :"associated_with_#{record.model_name.singular}"
11
+ return send(named_scope, record) if respond_to?(named_scope)
12
+
13
+ # TODO: add logging
14
+ if (own_association = reflect_on_all_associations.find { |assoc| assoc.klass == record.class })
15
+ case own_association.macro
16
+ when :has_one
17
+ joins(own_association.name).where({
18
+ own_association.name.to_sym => {
19
+ record.class.primary_key => record.id
20
+ }
21
+ })
22
+ when :belongs_to
23
+ where(own_association.name => record)
24
+ when :has_many
25
+ joins(own_association.name).where(own_association.klass.table_name.to_sym => record)
26
+ else
27
+ raise Net::HTTPNotImplemented, "associated_with->##{own_association.macro}"
28
+ end
29
+ elsif (record_association = record.class.reflect_on_all_associations.find { |assoc| assoc.klass == klass })
30
+ # TODO: add a warning here about a potentially poor performing query
31
+ where(id: record.send(record_association.name))
32
+ else
33
+ raise "Could not resolve the association between '#{klass.name}' and '#{record.class.name}'\n\n" \
34
+ "Define\n" \
35
+ " 1. the associations between the models\n" \
36
+ " 2. a named scope e.g.\n\n" \
37
+ "scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
38
+ end
39
+ end
40
+ end
41
+
42
+ module ClassMethods
43
+ # Path parameters
44
+
45
+ def path_parameter(param_name)
46
+ param_name = param_name.to_sym
47
+
48
+ scope :from_path_param, ->(param) { where(param_name => param) }
49
+
50
+ define_method :to_param do
51
+ return nil unless persisted?
52
+
53
+ send param_name
54
+ end
55
+ end
56
+
57
+ def dynamic_path_parameter(param_name)
58
+ param_name = param_name.to_sym
59
+
60
+ scope :from_path_param, ->(param) { where(id: param.split("-").first) }
61
+
62
+ define_method :to_param do
63
+ return nil unless persisted?
64
+
65
+ "#{id}-#{send(param_name)}".parameterize
66
+ end
67
+ end
68
+
69
+ # Ransack
70
+
71
+ def ransackable_attributes(_auth_object = nil)
72
+ _ransackers.keys
73
+ end
74
+
75
+ def ransackable_associations(_auth_object = nil)
76
+ [] # reflect_on_all_associations.map { |a| a.name.to_s }
77
+ end
78
+
79
+ def ransortable_attributes(auth_object = nil)
80
+ ransackable_attributes(auth_object) + %w[id created_at updated_at]
81
+ end
82
+
83
+ def ransackable_scopes(_auth_object = nil)
84
+ []
85
+ end
86
+
87
+ def resource_field_names
88
+ @resource_field_names ||= belongs_to_association_field_names + has_one_association_field_names +
89
+ has_many_association_field_names + content_column_field_names
90
+ end
91
+
92
+ def belongs_to_association_field_names
93
+ @belongs_to_association_field_names ||= reflect_on_all_associations(:belongs_to).map { |assoc| assoc.name.to_sym }
94
+ end
95
+
96
+ def has_one_association_field_names
97
+ @has_one_association_field_names ||= reflect_on_all_associations(:has_one).map { |assoc| assoc.name.to_sym }
98
+ end
99
+
100
+ def has_many_association_field_names
101
+ @has_many_association_field_names ||= reflect_on_all_associations(:has_many).map { |assoc| assoc.name.to_sym }
102
+ end
103
+
104
+ def content_column_field_names
105
+ @content_column_field_names ||= content_columns.map { |col| col.name.to_sym }
106
+ end
107
+
108
+ def has_many_association_routes
109
+ @has_many_association_routes ||= reflect_on_all_associations(:has_many).map { |assoc| assoc.klass.model_name.plural }
110
+ end
111
+ end
112
+
113
+ def to_label
114
+ %i[name title].each do |method|
115
+ name = send(method) if respond_to?(method)
116
+ return name if name.present?
117
+ end
118
+
119
+ "#{model_name.human} ##{to_param}"
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,13 @@
1
+ module Plutonium
2
+ module Reactor
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Core
6
+ autoload :ResourceContext
7
+ autoload :ResourceController
8
+ autoload :ResourceInteraction
9
+ autoload :ResourcePolicy
10
+ autoload :ResourcePresenter
11
+ autoload :ResourceRecord
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Plutonium
2
+ module Refinements
3
+ module ParameterRefinements
4
+ refine ActionController::Parameters do
5
+ def nilify
6
+ transform_values { |value| nilify_internal value }
7
+ end
8
+
9
+ private
10
+
11
+ def nilify_internal(value)
12
+ case value
13
+ when String
14
+ value.presence
15
+ when Hash
16
+ nilify value
17
+ when Array
18
+ value.map { |val| nilify_internal val }.compact
19
+ else
20
+ value
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end