rolemodel-rails 0.26.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 (251) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +161 -0
  4. data/Rakefile +6 -0
  5. data/lib/generators/rolemodel/README.md +29 -0
  6. data/lib/generators/rolemodel/USAGE +5 -0
  7. data/lib/generators/rolemodel/all_generator.rb +29 -0
  8. data/lib/generators/rolemodel/base_generator.rb +14 -0
  9. data/lib/generators/rolemodel/editors/README.md +9 -0
  10. data/lib/generators/rolemodel/editors/USAGE +9 -0
  11. data/lib/generators/rolemodel/editors/editors_generator.rb +30 -0
  12. data/lib/generators/rolemodel/editors/vscode_helpers.rb +22 -0
  13. data/lib/generators/rolemodel/github/README.md +23 -0
  14. data/lib/generators/rolemodel/github/USAGE +9 -0
  15. data/lib/generators/rolemodel/github/github_generator.rb +43 -0
  16. data/lib/generators/rolemodel/good_job/README.md +13 -0
  17. data/lib/generators/rolemodel/good_job/USAGE +5 -0
  18. data/lib/generators/rolemodel/good_job/good_job_generator.rb +73 -0
  19. data/lib/generators/rolemodel/good_job/templates/config/initializers/active_job.rb +17 -0
  20. data/lib/generators/rolemodel/good_job/templates/config/initializers/good_job.rb +24 -0
  21. data/lib/generators/rolemodel/heroku/README.md +8 -0
  22. data/lib/generators/rolemodel/heroku/USAGE +9 -0
  23. data/lib/generators/rolemodel/heroku/heroku_generator.rb +52 -0
  24. data/lib/generators/rolemodel/heroku/templates/Procfile +2 -0
  25. data/lib/generators/rolemodel/heroku/templates/app.json.tt +30 -0
  26. data/lib/generators/rolemodel/kaminari/README.md +14 -0
  27. data/lib/generators/rolemodel/kaminari/USAGE +5 -0
  28. data/lib/generators/rolemodel/kaminari/kaminari_generator.rb +17 -0
  29. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_first_page.html.slim +9 -0
  30. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_gap.html.slim +8 -0
  31. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_last_page.html.slim +9 -0
  32. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_next_page.html.slim +12 -0
  33. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_page.html.slim +10 -0
  34. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_paginator.html.slim +19 -0
  35. data/lib/generators/rolemodel/kaminari/templates/app/views/kaminari/_prev_page.html.slim +12 -0
  36. data/lib/generators/rolemodel/linters/README.md +32 -0
  37. data/lib/generators/rolemodel/linters/USAGE +5 -0
  38. data/lib/generators/rolemodel/linters/all_generator.rb +14 -0
  39. data/lib/generators/rolemodel/linters/eslint/README.md +5 -0
  40. data/lib/generators/rolemodel/linters/eslint/USAGE +8 -0
  41. data/lib/generators/rolemodel/linters/eslint/eslint_generator.rb +33 -0
  42. data/lib/generators/rolemodel/linters/eslint/templates/eslint.config.js +92 -0
  43. data/lib/generators/rolemodel/linters/rubocop/README.md +7 -0
  44. data/lib/generators/rolemodel/linters/rubocop/USAGE +9 -0
  45. data/lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb +23 -0
  46. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/form_error_response.rb +54 -0
  47. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/no_chrome_tag.rb +45 -0
  48. data/lib/generators/rolemodel/lograge/README.md +10 -0
  49. data/lib/generators/rolemodel/lograge/USAGE +8 -0
  50. data/lib/generators/rolemodel/lograge/lograge_generator.rb +15 -0
  51. data/lib/generators/rolemodel/lograge/templates/config/initializers/lograge.rb +16 -0
  52. data/lib/generators/rolemodel/mailers/README.md +13 -0
  53. data/lib/generators/rolemodel/mailers/USAGE +22 -0
  54. data/lib/generators/rolemodel/mailers/mailers_generator.rb +99 -0
  55. data/lib/generators/rolemodel/mailers/templates/app/assets/stylesheets/mailer.scss +61 -0
  56. data/lib/generators/rolemodel/mailers/templates/app/mailers/example_mailer.rb +8 -0
  57. data/lib/generators/rolemodel/mailers/templates/app/mailers/staging_mailer_interceptor.rb +9 -0
  58. data/lib/generators/rolemodel/mailers/templates/app/views/example_mailer/example_email.html.slim +16 -0
  59. data/lib/generators/rolemodel/mailers/templates/app/views/layouts/mailer.html.slim +16 -0
  60. data/lib/generators/rolemodel/mailers/templates/config/initializers/premailer_rails.rb +15 -0
  61. data/lib/generators/rolemodel/mailers/templates/public/logo.png +0 -0
  62. data/lib/generators/rolemodel/mailers/templates/spec/mailers/previews/example_mailer_preview.rb +6 -0
  63. data/lib/generators/rolemodel/optics/README.md +8 -0
  64. data/lib/generators/rolemodel/optics/USAGE +5 -0
  65. data/lib/generators/rolemodel/optics/all_generator.rb +12 -0
  66. data/lib/generators/rolemodel/optics/base/README.md +9 -0
  67. data/lib/generators/rolemodel/optics/base/USAGE +5 -0
  68. data/lib/generators/rolemodel/optics/base/base_generator.rb +21 -0
  69. data/lib/generators/rolemodel/optics/icons/README.md +7 -0
  70. data/lib/generators/rolemodel/optics/icons/USAGE +8 -0
  71. data/lib/generators/rolemodel/optics/icons/icons_generator.rb +48 -0
  72. data/lib/generators/rolemodel/optics/icons/templates/app/helpers/icon_helper.rb.tt +24 -0
  73. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/custom_icon_builder.rb +53 -0
  74. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/feather_icon_builder.rb +22 -0
  75. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/icon_builder.rb +73 -0
  76. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/lucide_icon_builder.rb +22 -0
  77. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/material_icon_builder.rb +31 -0
  78. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/phosphor_icon_builder.rb +27 -0
  79. data/lib/generators/rolemodel/optics/icons/templates/app/icon_builders/tabler_icon_builder.rb +22 -0
  80. data/lib/generators/rolemodel/react/README.md +12 -0
  81. data/lib/generators/rolemodel/react/USAGE +5 -0
  82. data/lib/generators/rolemodel/react/react_generator.rb +36 -0
  83. data/lib/generators/rolemodel/react/templates/app/helpers/react_helper.rb +11 -0
  84. data/lib/generators/rolemodel/react/templates/app/javascript/components/HelloReact.jsx +14 -0
  85. data/lib/generators/rolemodel/react/templates/app/javascript/controllers/react_controller.js +24 -0
  86. data/lib/generators/rolemodel/readme/README.md +9 -0
  87. data/lib/generators/rolemodel/readme/USAGE +8 -0
  88. data/lib/generators/rolemodel/readme/readme_generator.rb +11 -0
  89. data/lib/generators/rolemodel/readme/templates/README.md.erb +116 -0
  90. data/lib/generators/rolemodel/replace_content_helper.rb +29 -0
  91. data/lib/generators/rolemodel/saas/README.md +7 -0
  92. data/lib/generators/rolemodel/saas/USAGE +5 -0
  93. data/lib/generators/rolemodel/saas/all_generator.rb +15 -0
  94. data/lib/generators/rolemodel/saas/devise/README.md +24 -0
  95. data/lib/generators/rolemodel/saas/devise/USAGE +25 -0
  96. data/lib/generators/rolemodel/saas/devise/devise_generator.rb +172 -0
  97. data/lib/generators/rolemodel/saas/devise/templates/app/assets/stylesheets/login.css +30 -0
  98. data/lib/generators/rolemodel/saas/devise/templates/app/controllers/admin/users_controller.rb +34 -0
  99. data/lib/generators/rolemodel/saas/devise/templates/app/controllers/application_controller.rb +11 -0
  100. data/lib/generators/rolemodel/saas/devise/templates/app/controllers/users/invitations_controller.rb +77 -0
  101. data/lib/generators/rolemodel/saas/devise/templates/app/controllers/users/omniauth_callbacks_controller.rb +30 -0
  102. data/lib/generators/rolemodel/saas/devise/templates/app/controllers/users/registrations_controller.rb +80 -0
  103. data/lib/generators/rolemodel/saas/devise/templates/app/views/admin/users/edit.html.slim +10 -0
  104. data/lib/generators/rolemodel/saas/devise/templates/app/views/admin/users/index.html.slim +27 -0
  105. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/confirmations/new.html.slim +11 -0
  106. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/invitations/edit.html.slim +13 -0
  107. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/invitations/new.html.slim +14 -0
  108. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/confirmation_instructions.html.slim +3 -0
  109. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/email_changed.html.slim +5 -0
  110. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/invitation_instructions.html.slim +11 -0
  111. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/invitation_instructions.text.slim +6 -0
  112. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/password_change.html.slim +2 -0
  113. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/reset_password_instructions.html.slim +5 -0
  114. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/mailer/unlock_instructions.html.slim +4 -0
  115. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/passwords/edit.html.slim +13 -0
  116. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/passwords/new.html.slim +11 -0
  117. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/registrations/edit.html.slim +34 -0
  118. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/registrations/new.html.slim +17 -0
  119. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/sessions/new.html.slim +14 -0
  120. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/shared/_error_messages.html.slim +9 -0
  121. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/shared/_links.html.slim +14 -0
  122. data/lib/generators/rolemodel/saas/devise/templates/app/views/devise/unlocks/new.html.slim +11 -0
  123. data/lib/generators/rolemodel/saas/devise/templates/config/locales/devise.en.yml +151 -0
  124. data/lib/generators/rolemodel/saas/devise/templates/config/locales/devise_invitable.en.yml +31 -0
  125. data/lib/generators/rolemodel/saas/devise/templates/spec/factories/organizations.rb +9 -0
  126. data/lib/generators/rolemodel/saas/devise/templates/spec/factories/users.rb +33 -0
  127. data/lib/generators/rolemodel/saas/devise/templates/spec/mailers/previews/devise_mailer_preview.rb +36 -0
  128. data/lib/generators/rolemodel/saas/devise/templates/spec/models/user_spec.rb +29 -0
  129. data/lib/generators/rolemodel/saas/devise/templates/spec/support/devise.rb +8 -0
  130. data/lib/generators/rolemodel/saas/devise/templates/spec/system/users_spec.rb +128 -0
  131. data/lib/generators/rolemodel/semaphore/README.md +15 -0
  132. data/lib/generators/rolemodel/semaphore/USAGE +11 -0
  133. data/lib/generators/rolemodel/semaphore/semaphore_generator.rb +25 -0
  134. data/lib/generators/rolemodel/semaphore/templates/heroku-deployment-commands.sh +3 -0
  135. data/lib/generators/rolemodel/semaphore/templates/production-deploy.yml.erb +17 -0
  136. data/lib/generators/rolemodel/semaphore/templates/semaphore.yml.erb +145 -0
  137. data/lib/generators/rolemodel/semaphore/templates/staging-deploy.yml.erb +17 -0
  138. data/lib/generators/rolemodel/simple_form/README.md +10 -0
  139. data/lib/generators/rolemodel/simple_form/USAGE +11 -0
  140. data/lib/generators/rolemodel/simple_form/simple_form_generator.rb +20 -0
  141. data/lib/generators/rolemodel/simple_form/templates/app/inputs/collection_check_boxes_input.rb +7 -0
  142. data/lib/generators/rolemodel/simple_form/templates/app/inputs/collection_select_input.rb +7 -0
  143. data/lib/generators/rolemodel/simple_form/templates/app/inputs/grouped_collection_select_input.rb +7 -0
  144. data/lib/generators/rolemodel/simple_form/templates/app/inputs/segmented_control_input.rb +48 -0
  145. data/lib/generators/rolemodel/simple_form/templates/app/inputs/switch_checkbox_input.rb +39 -0
  146. data/lib/generators/rolemodel/simple_form/templates/app/inputs/tailored_select_input.rb +75 -0
  147. data/lib/generators/rolemodel/simple_form/templates/config/initializers/simple_form.rb +207 -0
  148. data/lib/generators/rolemodel/simple_form/templates/config/locales/simple_form.en.yml +31 -0
  149. data/lib/generators/rolemodel/simple_form/templates/lib/templates/slim/scaffold/_form.html.slim.tt +12 -0
  150. data/lib/generators/rolemodel/slim/README.md +9 -0
  151. data/lib/generators/rolemodel/slim/USAGE +5 -0
  152. data/lib/generators/rolemodel/slim/slim_generator.rb +24 -0
  153. data/lib/generators/rolemodel/slim/templates/app/views/application/_head.html.slim.tt +13 -0
  154. data/lib/generators/rolemodel/slim/templates/app/views/layouts/application.html.slim.tt +6 -0
  155. data/lib/generators/rolemodel/slim/templates/lib/templates/slim/scaffold/edit.html.slim.tt +15 -0
  156. data/lib/generators/rolemodel/slim/templates/lib/templates/slim/scaffold/index.html.slim.tt +13 -0
  157. data/lib/generators/rolemodel/slim/templates/lib/templates/slim/scaffold/new.html.slim.tt +13 -0
  158. data/lib/generators/rolemodel/slim/templates/lib/templates/slim/scaffold/partial.html.slim.tt +10 -0
  159. data/lib/generators/rolemodel/slim/templates/lib/templates/slim/scaffold/show.html.slim.tt +33 -0
  160. data/lib/generators/rolemodel/soft_destroyable/README.md +8 -0
  161. data/lib/generators/rolemodel/soft_destroyable/USAGE +9 -0
  162. data/lib/generators/rolemodel/soft_destroyable/soft_destroyable_generator.rb +13 -0
  163. data/lib/generators/rolemodel/soft_destroyable/templates/soft_destroyable.rb +82 -0
  164. data/lib/generators/rolemodel/soft_destroyable/templates/soft_destroyable_behavior.rb +166 -0
  165. data/lib/generators/rolemodel/source_map/README.md +13 -0
  166. data/lib/generators/rolemodel/source_map/USAGE +9 -0
  167. data/lib/generators/rolemodel/source_map/source_map_generator.rb +22 -0
  168. data/lib/generators/rolemodel/source_map/templates/lib/middleware/rolemodel/source_map.rb +45 -0
  169. data/lib/generators/rolemodel/source_map/templates/lib/tasks/assets.rake +43 -0
  170. data/lib/generators/rolemodel/tailored_select/README.md +7 -0
  171. data/lib/generators/rolemodel/tailored_select/USAGE +5 -0
  172. data/lib/generators/rolemodel/tailored_select/tailored_select_generator.rb +13 -0
  173. data/lib/generators/rolemodel/testing/README.md +11 -0
  174. data/lib/generators/rolemodel/testing/USAGE +5 -0
  175. data/lib/generators/rolemodel/testing/all_generator.rb +17 -0
  176. data/lib/generators/rolemodel/testing/factory_bot/README.md +18 -0
  177. data/lib/generators/rolemodel/testing/factory_bot/USAGE +8 -0
  178. data/lib/generators/rolemodel/testing/factory_bot/factory_bot_generator.rb +20 -0
  179. data/lib/generators/rolemodel/testing/factory_bot/templates/support/factory_bot.rb +3 -0
  180. data/lib/generators/rolemodel/testing/jasmine_playwright/README.md +9 -0
  181. data/lib/generators/rolemodel/testing/jasmine_playwright/jasmine_playwright_generator.rb +52 -0
  182. data/lib/generators/rolemodel/testing/jasmine_playwright/templates/jp-runner.config.mjs +5 -0
  183. data/lib/generators/rolemodel/testing/jasmine_playwright/templates/spec/javascript/browser/example_spec.js +15 -0
  184. data/lib/generators/rolemodel/testing/jasmine_playwright/templates/spec/javascript/browser/setupTests.js +3 -0
  185. data/lib/generators/rolemodel/testing/jasmine_playwright/templates/support/react.mjs +26 -0
  186. data/lib/generators/rolemodel/testing/parallel_tests/README.md +53 -0
  187. data/lib/generators/rolemodel/testing/parallel_tests/USAGE +12 -0
  188. data/lib/generators/rolemodel/testing/parallel_tests/parallel_tests_generator.rb +42 -0
  189. data/lib/generators/rolemodel/testing/rspec/README.md +46 -0
  190. data/lib/generators/rolemodel/testing/rspec/USAGE +14 -0
  191. data/lib/generators/rolemodel/testing/rspec/rspec_generator.rb +46 -0
  192. data/lib/generators/rolemodel/testing/rspec/templates/rails_helper.rb +72 -0
  193. data/lib/generators/rolemodel/testing/rspec/templates/spec_helper.rb +97 -0
  194. data/lib/generators/rolemodel/testing/rspec/templates/support/capybara_drivers.rb +51 -0
  195. data/lib/generators/rolemodel/testing/rspec/templates/support/capybara_testid.rb +5 -0
  196. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers/action_cable_helper.rb +5 -0
  197. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers/capybara_helper.rb +72 -0
  198. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers/playwright_helper.rb +85 -0
  199. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers/select_helper.rb +33 -0
  200. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers/test_element_helper.rb +11 -0
  201. data/lib/generators/rolemodel/testing/rspec/templates/support/helpers.rb +12 -0
  202. data/lib/generators/rolemodel/testing/test_prof/README.md +5 -0
  203. data/lib/generators/rolemodel/testing/test_prof/USAGE +5 -0
  204. data/lib/generators/rolemodel/testing/test_prof/test_prof_generator.rb +14 -0
  205. data/lib/generators/rolemodel/testing/vitest/README.md +5 -0
  206. data/lib/generators/rolemodel/testing/vitest/USAGE +5 -0
  207. data/lib/generators/rolemodel/testing/vitest/templates/spec/javascript/example.spec.js +12 -0
  208. data/lib/generators/rolemodel/testing/vitest/templates/spec/javascript/test-setup.js +1 -0
  209. data/lib/generators/rolemodel/testing/vitest/templates/vitest.config.js +23 -0
  210. data/lib/generators/rolemodel/testing/vitest/vitest_generator.rb +38 -0
  211. data/lib/generators/rolemodel/ui_components/README.md +9 -0
  212. data/lib/generators/rolemodel/ui_components/USAGE +5 -0
  213. data/lib/generators/rolemodel/ui_components/all_generator.rb +15 -0
  214. data/lib/generators/rolemodel/ui_components/flash/README.md +10 -0
  215. data/lib/generators/rolemodel/ui_components/flash/USAGE +9 -0
  216. data/lib/generators/rolemodel/ui_components/flash/flash_generator.rb +22 -0
  217. data/lib/generators/rolemodel/ui_components/flash/templates/app/views/application/_flash.html.slim +6 -0
  218. data/lib/generators/rolemodel/ui_components/flash/templates/spec/support/matchers/flash_matchers.rb +19 -0
  219. data/lib/generators/rolemodel/ui_components/modals/README.md +119 -0
  220. data/lib/generators/rolemodel/ui_components/modals/USAGE +8 -0
  221. data/lib/generators/rolemodel/ui_components/modals/modals_generator.rb +74 -0
  222. data/lib/generators/rolemodel/ui_components/modals/templates/app/helpers/turbo_frame_link_helper.rb +20 -0
  223. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/controllers/morph_controller.js +23 -0
  224. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/controllers/toggle_controller.js +55 -0
  225. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/initializers/before_morph_handler.js +21 -0
  226. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/initializers/frame_missing_handler.js +12 -0
  227. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/initializers/turbo_confirm.js +3 -0
  228. data/lib/generators/rolemodel/ui_components/modals/templates/app/views/application/_confirm.html.slim +12 -0
  229. data/lib/generators/rolemodel/ui_components/modals/templates/app/views/layouts/modal.html.slim.tt +26 -0
  230. data/lib/generators/rolemodel/ui_components/modals/templates/app/views/layouts/panel.html.slim.tt +21 -0
  231. data/lib/generators/rolemodel/ui_components/navbar/README.md +11 -0
  232. data/lib/generators/rolemodel/ui_components/navbar/USAGE +10 -0
  233. data/lib/generators/rolemodel/ui_components/navbar/navbar_generator.rb +54 -0
  234. data/lib/generators/rolemodel/ui_components/navbar/templates/app/assets/stylesheets/components/shoelace/index.scss +2 -0
  235. data/lib/generators/rolemodel/ui_components/navbar/templates/app/javascript/lib/shoelace.js +9 -0
  236. data/lib/generators/rolemodel/ui_components/navbar/templates/app/views/layouts/_navbar.html.slim +29 -0
  237. data/lib/generators/rolemodel/webpack/README.md +8 -0
  238. data/lib/generators/rolemodel/webpack/USAGE +5 -0
  239. data/lib/generators/rolemodel/webpack/templates/app/assets/stylesheets/application.scss +5 -0
  240. data/lib/generators/rolemodel/webpack/templates/app/javascript/initializers/honeybadger.js +26 -0
  241. data/lib/generators/rolemodel/webpack/templates/postcss.config.cjs +14 -0
  242. data/lib/generators/rolemodel/webpack/templates/webpack.config.js +104 -0
  243. data/lib/generators/rolemodel/webpack/webpack_generator.rb +76 -0
  244. data/lib/generators/templates/generator/%filename%.rb.tt +11 -0
  245. data/lib/generators/templates/generator/README.md.tt +11 -0
  246. data/lib/generators/templates/generator/USAGE.tt +5 -0
  247. data/lib/generators/templates/generator_spec/%filename%_spec.rb.tt +5 -0
  248. data/lib/rolemodel/engine.rb +9 -0
  249. data/lib/rolemodel/version.rb +5 -0
  250. data/lib/rolemodel-rails.rb +7 -0
  251. metadata +416 -0
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'a soft destroyable' do |factory_name, **options|
4
+ let(:factory_traits) { options[:factory_traits] }
5
+ let(:dependent_destroy_relations) { options[:dependent_destroy_relations] }
6
+
7
+ # we want to access the local variables of the block scope above, which
8
+ # you lose when you enter an instance method scope, but retain when you
9
+ # enter a block scope.
10
+ define_method(:create_instance) do
11
+ create factory_name, *factory_traits
12
+ end
13
+
14
+ describe '.kept' do
15
+ it 'only returns records that haven\'t been deleted' do
16
+ # given
17
+ first_destroyable = create_instance
18
+ second_destroyable = create_instance
19
+ first_destroyable.soft_destroy!
20
+ klass = first_destroyable.class
21
+ # when
22
+ remaining = klass.kept
23
+ # then
24
+ expect(remaining).to include(second_destroyable)
25
+ expect(remaining).not_to include(first_destroyable)
26
+ end
27
+ end
28
+
29
+ describe '.only_deleted' do
30
+ it 'only returns records that have been deleted' do
31
+ # given
32
+ first_destroyable = create_instance
33
+ second_destroyable = create_instance
34
+ first_destroyable.soft_destroy!
35
+ klass = first_destroyable.class
36
+ # when
37
+ deleted = klass.only_deleted
38
+ # then
39
+ expect(deleted).to include(first_destroyable)
40
+ expect(deleted).not_to include(second_destroyable)
41
+ end
42
+ end
43
+
44
+ describe '#assign_attributes' do
45
+ it 'ignores other attributes when _soft_destroy is present' do
46
+ destroyable = create_instance
47
+ destroyable.assign_attributes(updated_at: Time.current, _soft_destroy: '1')
48
+ expect(destroyable.changes).to_not include(:updated_at)
49
+ end
50
+
51
+ it 'includes all attributes when _soft_destroy is not present' do
52
+ destroyable = create_instance
53
+ destroyable.assign_attributes(updated_at: Time.current)
54
+ expect(destroyable.changes).to include(:updated_at)
55
+ end
56
+ end
57
+
58
+ describe '#soft_destroy!' do
59
+ it 'sets deleted_at' do
60
+ # given
61
+ destroyable = create_instance
62
+ expect(destroyable.deleted_at).to be_nil
63
+ # when
64
+ destroyable.soft_destroy!
65
+ # then
66
+ expect(destroyable.deleted_at).not_to be_nil
67
+ end
68
+ context 'with dependents', if: options[:dependent_destroy_relations]&.present? do
69
+ it 'soft_destroys dependents' do
70
+ # given
71
+ destroyable = create_instance
72
+ dependent_objects = dependent_destroy_relations.map { |relation_name| destroyable.send(relation_name) }.flatten
73
+ dependent_deleted_timestamps = dependent_objects.map(&:deleted_at)
74
+ expect(dependent_deleted_timestamps.compact).to be_empty
75
+ # when
76
+ destroyable.soft_destroy!
77
+ # then
78
+ dependent_deleted_timestamps = dependent_objects.map { |dependent_object| dependent_object.reload.deleted_at }.compact.uniq
79
+ expect(dependent_deleted_timestamps).not_to be_empty
80
+ end
81
+ it 'uses the parent deleted_at timestamp for children' do
82
+ # given
83
+ destroyable = create_instance
84
+ dependent_objects = dependent_destroy_relations.map { |relation_name| destroyable.send(relation_name) }.flatten
85
+ dependent_deleted_timestamps = dependent_objects.map(&:deleted_at)
86
+ expect(dependent_deleted_timestamps.compact).to be_empty
87
+ # when
88
+ destroyable.soft_destroy!
89
+ # then
90
+ dependent_deleted_timestamps = dependent_objects.map { |dependent_object| dependent_object.reload.deleted_at }.compact.uniq
91
+ expect(dependent_deleted_timestamps.size).to eq(1)
92
+ expect(dependent_deleted_timestamps.first).to eq(destroyable.reload.deleted_at)
93
+ end
94
+ end
95
+
96
+ it 'raises errors when they are encountered' do
97
+ destroyable = create_instance
98
+ destroyable.define_singleton_method(:update) do |_arg|
99
+ errors.add(:base, 'This record is invalid')
100
+ false
101
+ end
102
+ expect { destroyable.soft_destroy! }.to raise_error(ActiveModel::ValidationError)
103
+ end
104
+ end
105
+
106
+ describe 'restore!' do
107
+ it 'clears out deleted_at' do
108
+ # given
109
+ destroyable = create_instance
110
+ destroyable.update(deleted_at: 1.day.ago)
111
+ expect(destroyable.deleted_at).not_to be_nil
112
+ # when
113
+ destroyable.restore!
114
+ # then
115
+ expect(destroyable.deleted_at).to be_nil
116
+ end
117
+ context 'with dependents', if: options[:dependent_destroy_relations]&.present? do
118
+ it 'restores dependents (that were soft_destroyed at the same time)' do
119
+ destroyable = create_instance
120
+ timestamp = 1.day.ago
121
+ destroyable.update(deleted_at: timestamp)
122
+ deleted_associated_objects = []
123
+ dependent_destroy_relations.each do |assoc|
124
+ associated_objects = Array.wrap(destroyable.send(assoc))
125
+ associated_objects.each do |obj|
126
+ obj.update(deleted_at: timestamp)
127
+ deleted_associated_objects << obj
128
+ end
129
+ end
130
+ # when
131
+ destroyable.reload.restore!
132
+ # then
133
+ expect(deleted_associated_objects).not_to be_empty
134
+ deleted_associated_objects.each { |obj| expect(obj.reload).not_to be_soft_destroyed }
135
+ end
136
+ it 'does not restore dependents (when they have different deleted_at timestamps)' do
137
+ destroyable = create_instance
138
+ timestamp = 1.day.ago
139
+ destroyable.update(deleted_at: timestamp)
140
+ associated_objects_that_were_already_deleted = []
141
+ dependent_destroy_relations.each do |assoc|
142
+ associated_objects = Array.wrap(destroyable.send(assoc))
143
+ associated_objects.each do |obj|
144
+ obj.update(deleted_at: 2.days.ago)
145
+ associated_objects_that_were_already_deleted << obj
146
+ end
147
+ end
148
+ # when
149
+ destroyable.reload.restore!
150
+ # then
151
+ expect(associated_objects_that_were_already_deleted).not_to be_empty
152
+ expect(associated_objects_that_were_already_deleted.map(&:reload)).to all be_soft_destroyed
153
+ end
154
+ end
155
+
156
+ it 'raises errors when they are encountered' do
157
+ destroyable = create_instance
158
+ destroyable.update(deleted_at: 1.day.ago)
159
+ destroyable.define_singleton_method(:update) do |_arg|
160
+ errors.add(:base, 'This record is invalid')
161
+ false
162
+ end
163
+ expect { destroyable.restore! }.to raise_error(ActiveModel::ValidationError)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,13 @@
1
+ # Rack middleware Generator
2
+
3
+ ## What you get
4
+
5
+ * Rack middleware for serving source maps
6
+ * Enhanced assets rake tasks to relocate and manage source map files
7
+
8
+ Adds Rack middleware for serving js source maps only to allowed users in a production environment.
9
+
10
+ ## Usage
11
+
12
+ * Ensure that app uses Warden::Manager middleware
13
+ * Set ENV['SOURCE_MAPS_ALLOWED_USERS_EMAILS'] to a comma-separated list of emails or update the middleware to allow all super admins
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Adds Rack middleware for serving js source maps
3
+
4
+ Example:
5
+ rails generate rolemodel:source_map
6
+
7
+ This will create:
8
+ lib/middleware/rolemodel/source_map.rb
9
+ lib/tasks/assets.rake
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ class SourceMapGenerator < BaseGenerator
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def inject_config_for_production
8
+ inject_into_file 'config/environments/production.rb',
9
+ "\nrequire_relative Rails.root.join('lib/middleware/rolemodel/source_map.rb')\n", after: "require \"active_support/core_ext/integer/time\"\n"
10
+ inject_into_file 'config/environments/production.rb',
11
+ "\n config.middleware.insert_after Warden::Manager, Middleware::Rolemodel::SourceMap\n\n", after: "Rails.application.configure do\n"
12
+ end
13
+
14
+ def copy_middleware_files
15
+ copy_file 'lib/middleware/rolemodel/source_map.rb'
16
+ end
17
+
18
+ def copy_assets_rake_task
19
+ copy_file 'lib/tasks/assets.rake'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Middleware
4
+ module Rolemodel
5
+ class SourceMap
6
+ def initialize(app, **options)
7
+ root = options.delete(:root) || 'maps'
8
+ default_headers = { 'Set-Cookie' => 'Same-Site=None', 'Cache-Control' => 'max-age=0;no-cache' }
9
+ custom_headers = default_headers.merge(options.delete(:headers) || {})
10
+ @app = app
11
+ @allowed_users_emails = ENV.fetch('SOURCE_MAPS_ALLOWED_USERS_EMAILS', '').split(',')
12
+ @file_server = Rack::Files.new(root, custom_headers)
13
+ end
14
+
15
+ def call(env)
16
+ if allowed_sourcemap?(env)
17
+ @file_server.call(env.tap { |env| env['PATH_INFO'].sub!(%r{^/[\w-]+[^/]}, '') })
18
+ else
19
+ @app.call(env)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def allowed_sourcemap?(env)
26
+ return false unless env['PATH_INFO'].end_with?('.map')
27
+
28
+ permit_user?(env)
29
+ end
30
+
31
+ def permit_user?(env)
32
+ # Alternatively, allow sourcemaps for all super admins
33
+ # current_user(env)&.super_admin?
34
+ current_user_email = current_user(env)&.email
35
+ return false if current_user_email.blank?
36
+
37
+ @allowed_users_emails.include?(current_user_email)
38
+ end
39
+
40
+ def current_user(env)
41
+ env['warden']&.user
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :assets do
4
+ task clean_sourcemaps: :environment do
5
+ maps_root = Rails.root.join('maps')
6
+ next unless maps_root.exist?
7
+
8
+ assets_root = Rails.public_path.join('assets')
9
+
10
+ puts 'Cleaning sourcemaps folder'
11
+ maps_root.glob('*.map').each do |map_file|
12
+ asset_file = map_file.basename('.map')
13
+ map_file.delete unless assets_root.join(asset_file).exist?
14
+ end
15
+ end
16
+
17
+ task relocate_sourcemaps: :environment do
18
+ maps_root = Rails.root.join('maps')
19
+ maps_root.mkpath
20
+
21
+ puts 'Relocating sourcemap files'
22
+ Rails.root.glob('public/assets/*.map').each do |map_file|
23
+ map_file.rename(maps_root.join(map_file.basename))
24
+ end
25
+ end
26
+
27
+ task clear_sourcemaps: :environment do
28
+ maps_root = Rails.root.join('maps')
29
+ maps_root.rmtree if maps_root.exist?
30
+ end
31
+ end
32
+
33
+ Rake::Task['assets:clean'].enhance do
34
+ Rake::Task['assets:clean_sourcemaps'].invoke
35
+ end
36
+
37
+ Rake::Task['assets:precompile'].enhance do
38
+ Rake::Task['assets:relocate_sourcemaps'].invoke
39
+ end
40
+
41
+ Rake::Task['assets:clobber'].enhance do
42
+ Rake::Task['assets:clear_sourcemaps'].invoke
43
+ end
@@ -0,0 +1,7 @@
1
+ # TailoredSelect Generator
2
+
3
+ `rails g rolemodel:tailored_select`
4
+
5
+ ## What you get
6
+
7
+ The [Tailored Select](https://github.com/RoleModel/tailored-select) web component
@@ -0,0 +1,5 @@
1
+ Description:
2
+ runs the tailored select generator
3
+
4
+ Example:
5
+ rails generate rolemodel:tailored_select
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ class TailoredSelectGenerator < BaseGenerator
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def add_tailored_select_package
8
+ say 'Installing Tailored Select package', :green
9
+
10
+ run 'yarn add @rolemodel/tailored-select'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # Testing Generator
2
+
3
+ [USAGE](./USAGE)
4
+
5
+ ## What you get
6
+
7
+ * [RSpec](./rspec)
8
+ * [JasminePlaywright](./jasmine_playwright)
9
+ * [FactoryBot](./factory_bot)
10
+ * [TestProf](./test_prof)
11
+ * [ParallelTests](./parallel_tests)
@@ -0,0 +1,5 @@
1
+ Description:
2
+ runs all the testing generators
3
+
4
+ Example:
5
+ rails generate rolemodel:testing:all
@@ -0,0 +1,17 @@
1
+ module Rolemodel
2
+ module Testing
3
+ class AllGenerator < BaseGenerator
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ def run_all_the_generators
7
+ generate 'rolemodel:testing:factory_bot'
8
+ if yes?('Would you like to add jasmine-playwright-runner for browser testing?')
9
+ generate 'rolemodel:testing:jasmine_playwright'
10
+ end
11
+ generate 'rolemodel:testing:parallel_tests'
12
+ generate 'rolemodel:testing:rspec'
13
+ generate 'rolemodel:testing:test_prof'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # FactoryBot Generator
2
+
3
+ Depends on [RSpec Generator](../rspec)
4
+
5
+ ## What you get
6
+
7
+ Installs [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails)
8
+
9
+ ### Factory Syntax methods
10
+
11
+ Allow you to use the short hand methods for creating or building factories
12
+
13
+ ```ruby
14
+ it 'tests something' do
15
+ created_user = create(:user)
16
+ built_user = build(:user)
17
+ end
18
+ ```
@@ -0,0 +1,8 @@
1
+ Description:
2
+ installs and configures FactoryBot
3
+
4
+ Example:
5
+ rails generate rolemodel:testing:factory_bot
6
+
7
+ This will create:
8
+ spec/support/factory_bot.rb
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ module Testing
5
+ class FactoryBotGenerator < BaseGenerator
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def install_factory_bot_rails
9
+ gem_group :development, :test do
10
+ gem 'factory_bot_rails'
11
+ end
12
+ run_bundle
13
+ end
14
+
15
+ def add_spec_files
16
+ template 'support/factory_bot.rb', 'spec/support/factory_bot.rb'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryBot::Syntax::Methods
3
+ end
@@ -0,0 +1,9 @@
1
+ # Jasmine Playwright Generator
2
+ Sets up Jasmine Playwright
3
+
4
+ **Notes**:
5
+ * Configured for testing Web Components and generic DOM. If you need tests for stimulus controllers and/or react components, see the documentation for jasmine-playwright-runner.
6
+
7
+ ## What you get
8
+ * A command for running component tests (yarn test:browser)
9
+ * An example test (example_spec.js)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ module Testing
5
+ class JasminePlaywrightGenerator < BaseGenerator
6
+ include ReplaceContentHelper
7
+ source_root File.expand_path('templates', __dir__)
8
+ class_option :github_package_token, type: :string, default: ENV['GITHUB_PACKAGES_TOKEN'], desc: 'GitHub Packages token with access to @rolemodel packages'
9
+
10
+ TEST_COMMAND = 'NODE_ENV=test jp-runner --config jp-runner.config.mjs --webpack-config webpack.config.cjs'
11
+ DEV_DEPENDENCIES = %w[
12
+ @rolemodel/jasmine-playwright-runner
13
+ playwright
14
+ lit-html
15
+ ].freeze
16
+
17
+ def fail_without_github_token
18
+ raise 'a --github_package_token option or GITHUB_PACKAGES_TOKEN environment variable is required' if options[:github_package_token].blank?
19
+ end
20
+
21
+ def yarn_init_unless_package_json_exists
22
+ run 'yarn init' unless File.exist?(File.expand_path('package.json', destination_root))
23
+ end
24
+
25
+ def add_browser_test_script
26
+ say 'Adding yarn test:browser command', :green
27
+
28
+ add_package_json_script 'test:browser', TEST_COMMAND
29
+ end
30
+
31
+ def set_node_version
32
+ say "Setting local Node version to #{set_color(NODE_VERSION, :yellow)}", :green
33
+
34
+ create_file '.node-version', NODE_VERSION, force: true
35
+ end
36
+
37
+ def add_dev_dependencies
38
+ say 'Adding new dev dependency to package.json', :green
39
+
40
+ run "yarn add --dev #{DEV_DEPENDENCIES.join(' ')}"
41
+ end
42
+
43
+ def add_spec_config_files
44
+ say 'Adding Jasmine Playwright configuration files', :green
45
+
46
+ template 'spec/javascript/browser/example_spec.js'
47
+ template 'jp-runner.config.mjs'
48
+ template 'spec/javascript/browser/setupTests.js'
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ export default {
2
+ setupFiles: [
3
+ 'spec/javascript/browser/setupTests.js'
4
+ ]
5
+ }
@@ -0,0 +1,15 @@
1
+ import { html } from '@rolemodel/jasmine-playwright-runner/dom'
2
+ import { page } from '@rolemodel/jasmine-playwright-runner/page'
3
+
4
+ describe('example', () => {
5
+ it('is there', async () => {
6
+ const content = 'Hello World'
7
+ await html`
8
+ <div>
9
+ ${content}
10
+ </div>
11
+ `
12
+
13
+ await expectAsync(page.getByText(content)).toBeInTheDocument()
14
+ })
15
+ })
@@ -0,0 +1,3 @@
1
+ import { setup as setupDom } from '@rolemodel/jasmine-playwright-runner/dom'
2
+
3
+ setupDom()
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+
4
+ let reactDOMRoot
5
+
6
+ export function setup() {
7
+ window.React = React
8
+ window.ReactDOM = ReactDOM
9
+
10
+ afterEach(() => {
11
+ if (reactDOMRoot) {
12
+ ReactDOM.unmountComponentAtNode(reactDOMRoot)
13
+ reactDOMRoot.remove()
14
+ reactDOMRoot = undefined
15
+ }
16
+ })
17
+ }
18
+
19
+ export function render(reactNode) {
20
+ if (!reactDOMRoot) {
21
+ reactDOMRoot = document.createElement('div')
22
+ document.body.append(reactDOMRoot)
23
+ }
24
+
25
+ ReactDOM.render(reactNode, reactDOMRoot)
26
+ }
@@ -0,0 +1,53 @@
1
+ # Parallel Tests Generator
2
+
3
+ ## What you get
4
+
5
+ ### Parallel Tests Setup
6
+
7
+ This generator configures your Rails application to run RSpec tests in parallel, greatly reducing the time it takes to run your test suite.
8
+
9
+ ### Gemfile Addition
10
+
11
+ The generator will add the `parallel_tests`, `turbo_tests`, and `rspec_junit_formatter` gems to your Gemfile and run bundle install.
12
+
13
+ ```rb
14
+ group :development, :test do
15
+ gem 'parallel_tests'
16
+ gem 'turbo_tests', require: false
17
+ gem 'rspec_junit_formatter', require: false
18
+ end
19
+ ```
20
+
21
+ ### RSpec Configuration
22
+ The generator will create a `.rspec_parallel` file in your Rails root directory.
23
+
24
+ ```
25
+ --format RspecJunitFormatter --out tmp/rspec-<%= ENV['TEST_ENV_NUMBER'] %>.xml
26
+ --format ParallelTests::RSpec::RuntimeLogger --out tmp/turbo_rspec_runtime.log
27
+ --format ParallelTests::RSpec::FailuresLogger
28
+ ```
29
+
30
+ ### Database Configuration
31
+
32
+ Your `database.yml` will be updated to support parallel test databases. This allows each test process to use its own isolated database.
33
+
34
+ ```yaml
35
+ test:
36
+ # ...existing configuration...
37
+ database: your_app_test<%= ENV['TEST_ENV_NUMBER'] %>
38
+ ```
39
+
40
+ ### Running Parallel Tests
41
+
42
+ Once configured, you can run your tests in parallel using:
43
+
44
+ ```bash
45
+ # Run entire test suite in parallel
46
+ bundle exec rake parallel:spec
47
+
48
+ # Run specific tests in parallel
49
+ bundle exec rake parallel:spec[spec/models]
50
+
51
+ # Specify number of processes (default is the number of CPUs)
52
+ bundle exec rake parallel:spec[spec,4]
53
+ ```
@@ -0,0 +1,12 @@
1
+ Description:
2
+ generates configuration for running RSpec tests in parallel
3
+
4
+ Example:
5
+ rails generate rolemodel:testing:parallel_rspec
6
+
7
+ This will add:
8
+ parallel_tests to your Gemfile
9
+ turbo_tests to your Gemfile for a nicer testing experience
10
+ rspec_junit_formatter for test reporting
11
+ .rspec_parallel file for RSpec configuration
12
+ database.yml configuration for parallel testing
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ module Testing
5
+ class ParallelTestsGenerator < BaseGenerator # rubocop:disable Style/Documentation
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def install_parallel_tests
9
+ gem_group :development, :test do
10
+ gem 'parallel_tests'
11
+ gem 'turbo_tests', require: false
12
+ gem 'rspec_junit_formatter', require: false
13
+ end
14
+ run_bundle
15
+ end
16
+
17
+ def add_rspec_parallel_file
18
+ template '.rspec_parallel', '.rspec_parallel'
19
+ end
20
+
21
+ def configure_database_for_parallel_testing
22
+ say_status :update, 'Updating database.yml for parallel testing', :blue
23
+
24
+ # If there is, append the TEST_ENV_NUMBER to it
25
+ gsub_file 'config/database.yml', /(test:.*\n\s+database:.*_test)/m, "\\1<%= ENV['TEST_ENV_NUMBER'] %>"
26
+
27
+ say_status :update, 'Updated database configuration successfully', :green
28
+ end
29
+
30
+ def setup_parallel_test_databases
31
+ say_status :setup, 'Setting up parallel test databases...', :blue
32
+ bundle_command 'exec rake parallel:setup'
33
+ end
34
+
35
+ def display_post_install_message
36
+ say_status :info, 'Parallel Tests has been configured!', :green
37
+ say_status :info, 'You can now run your tests in parallel with:', :green
38
+ say_status :info, 'bundle exec rake parallel:spec', :green
39
+ end
40
+ end
41
+ end
42
+ end