seams 0.1.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 (414) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +335 -0
  3. data/LICENSE +21 -0
  4. data/README.md +104 -0
  5. data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
  6. data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
  7. data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
  8. data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
  9. data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
  10. data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
  11. data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
  12. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
  13. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
  14. data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
  15. data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
  16. data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
  17. data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
  18. data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
  19. data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
  20. data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
  21. data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
  22. data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
  23. data/lib/generators/seams/admin/admin_generator.rb +852 -0
  24. data/lib/generators/seams/admin/templates/README.md.tt +266 -0
  25. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
  26. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
  27. data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
  28. data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
  29. data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
  30. data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
  31. data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
  32. data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
  33. data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
  34. data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
  35. data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
  36. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
  37. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
  38. data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
  39. data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
  40. data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
  41. data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
  42. data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
  43. data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
  44. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
  45. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
  46. data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
  47. data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
  48. data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
  49. data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
  50. data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
  51. data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
  52. data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
  53. data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
  54. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
  55. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
  56. data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
  57. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
  58. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
  59. data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
  60. data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
  61. data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
  62. data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
  63. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
  64. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
  65. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
  66. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
  67. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
  68. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
  69. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
  70. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
  71. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
  72. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
  73. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
  74. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
  75. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
  76. data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
  77. data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
  78. data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
  79. data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
  80. data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
  81. data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
  82. data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
  83. data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
  84. data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
  85. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
  86. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
  87. data/lib/generators/seams/auth/auth_generator.rb +311 -0
  88. data/lib/generators/seams/auth/templates/README.md.tt +289 -0
  89. data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
  90. data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
  91. data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
  92. data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
  93. data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
  94. data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
  95. data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
  96. data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
  97. data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
  98. data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
  99. data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
  100. data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
  101. data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
  102. data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
  103. data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
  104. data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
  105. data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
  106. data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
  107. data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
  108. data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
  109. data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
  110. data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
  111. data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
  112. data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
  113. data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
  114. data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
  115. data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
  116. data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
  117. data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
  118. data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
  119. data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
  120. data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
  121. data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
  122. data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
  123. data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
  124. data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
  125. data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
  126. data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
  127. data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
  128. data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
  129. data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
  130. data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
  131. data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
  132. data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
  133. data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
  134. data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
  135. data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
  136. data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
  137. data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
  138. data/lib/generators/seams/billing/billing_generator.rb +476 -0
  139. data/lib/generators/seams/billing/templates/README.md.tt +355 -0
  140. data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
  141. data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
  142. data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
  143. data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
  144. data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
  145. data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
  146. data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
  147. data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
  148. data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
  149. data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
  150. data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
  151. data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
  152. data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
  153. data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
  154. data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
  155. data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
  156. data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
  157. data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
  158. data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
  159. data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
  160. data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
  161. data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
  162. data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
  163. data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
  164. data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
  165. data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
  166. data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
  167. data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
  168. data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
  169. data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
  170. data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
  171. data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
  172. data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
  173. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
  174. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
  175. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
  176. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
  177. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
  178. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
  179. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
  180. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
  181. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
  182. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
  183. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
  184. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
  185. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
  186. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
  187. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
  188. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
  189. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
  190. data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
  191. data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
  192. data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
  193. data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
  194. data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
  195. data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
  196. data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
  197. data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
  198. data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
  199. data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
  200. data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
  201. data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
  202. data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
  203. data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
  204. data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
  205. data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
  206. data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
  207. data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
  208. data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
  209. data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
  210. data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
  211. data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
  212. data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
  213. data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
  214. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
  215. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
  216. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
  217. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
  218. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
  219. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
  220. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
  221. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
  222. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
  223. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
  224. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
  225. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
  226. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
  227. data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
  228. data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
  229. data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
  230. data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
  231. data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
  232. data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
  233. data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
  234. data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
  235. data/lib/generators/seams/core/core_generator.rb +191 -0
  236. data/lib/generators/seams/core/templates/README.md.tt +45 -0
  237. data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
  238. data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
  239. data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
  240. data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
  241. data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
  242. data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
  243. data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
  244. data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
  245. data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
  246. data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
  247. data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
  248. data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
  249. data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
  250. data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
  251. data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
  252. data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
  253. data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
  254. data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
  255. data/lib/generators/seams/engine/engine_generator.rb +165 -0
  256. data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
  257. data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
  258. data/lib/generators/seams/engine/templates/README.md.tt +40 -0
  259. data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
  260. data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
  261. data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
  262. data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
  263. data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
  264. data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
  265. data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
  266. data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
  267. data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
  268. data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
  269. data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
  270. data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
  271. data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
  272. data/lib/generators/seams/install/install_generator.rb +211 -0
  273. data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
  274. data/lib/generators/seams/install/templates/Procfile.tt +14 -0
  275. data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
  276. data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
  277. data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
  278. data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
  279. data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
  280. data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
  281. data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
  282. data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
  283. data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
  284. data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
  285. data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
  286. data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
  287. data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
  288. data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
  289. data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
  290. data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
  291. data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
  292. data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
  293. data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
  294. data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
  295. data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
  296. data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
  297. data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
  298. data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
  299. data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
  300. data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
  301. data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
  302. data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
  303. data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
  304. data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
  305. data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
  306. data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
  307. data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
  308. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
  309. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
  310. data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
  311. data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
  312. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
  313. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
  314. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
  315. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
  316. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
  317. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
  318. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
  319. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
  320. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
  321. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
  322. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
  323. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
  324. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
  325. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
  326. data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
  327. data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
  328. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
  329. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
  330. data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
  331. data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
  332. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
  333. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
  334. data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
  335. data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
  336. data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
  337. data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
  338. data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
  339. data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
  340. data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
  341. data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
  342. data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
  343. data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
  344. data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
  345. data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
  346. data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
  347. data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
  348. data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
  349. data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
  350. data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
  351. data/lib/generators/seams/remove/remove_generator.rb +259 -0
  352. data/lib/generators/seams/teams/teams_generator.rb +298 -0
  353. data/lib/generators/seams/teams/templates/README.md.tt +88 -0
  354. data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
  355. data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
  356. data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
  357. data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
  358. data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
  359. data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
  360. data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
  361. data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
  362. data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
  363. data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
  364. data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
  365. data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
  366. data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
  367. data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
  368. data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
  369. data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
  370. data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
  371. data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
  372. data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
  373. data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
  374. data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
  375. data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
  376. data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
  377. data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
  378. data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
  379. data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
  380. data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
  381. data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
  382. data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
  383. data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
  384. data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
  385. data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
  386. data/lib/seams/cli/list.rb +111 -0
  387. data/lib/seams/cli/quality.rb +99 -0
  388. data/lib/seams/cli/resolve.rb +276 -0
  389. data/lib/seams/cli/test_changed.rb +116 -0
  390. data/lib/seams/cli.rb +48 -0
  391. data/lib/seams/configuration.rb +19 -0
  392. data/lib/seams/cops/known_queue_names.rb +42 -0
  393. data/lib/seams/cops/migration_comments.rb +68 -0
  394. data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
  395. data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
  396. data/lib/seams/cops.rb +18 -0
  397. data/lib/seams/event_registry.rb +49 -0
  398. data/lib/seams/events/adapter.rb +24 -0
  399. data/lib/seams/events/adapters/active_support.rb +31 -0
  400. data/lib/seams/events/publisher.rb +178 -0
  401. data/lib/seams/events.rb +39 -0
  402. data/lib/seams/generators/dummy_app_writer.rb +424 -0
  403. data/lib/seams/generators/eject_aware.rb +102 -0
  404. data/lib/seams/generators/follow_up_generator.rb +148 -0
  405. data/lib/seams/generators/host_injector.rb +124 -0
  406. data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
  407. data/lib/seams/generators/splicer.rb +217 -0
  408. data/lib/seams/observability/adapter.rb +33 -0
  409. data/lib/seams/observability/adapters/rails_logger.rb +59 -0
  410. data/lib/seams/observability.rb +34 -0
  411. data/lib/seams/runtime.rb +23 -0
  412. data/lib/seams/version.rb +5 -0
  413. data/lib/seams.rb +23 -0
  414. metadata +493 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe Core::Auditable do
6
+ # Stub a named ActiveRecord class. The polymorphic `auditable_type`
7
+ # column needs `self.class.name` to be non-nil; `Class.new(...)`
8
+ # returns an anonymous class whose `.name` is nil, which breaks the
9
+ # `dependent: :nullify` cleanup on destroy.
10
+ before do
11
+ stub_const("Article", Class.new(ActiveRecord::Base) {
12
+ self.table_name = "articles"
13
+ include Core::Auditable
14
+ })
15
+ end
16
+
17
+ let(:model_class) { Article }
18
+
19
+ it "creates an audit log on create" do
20
+ expect { model_class.create!(title: "Hi") }
21
+ .to change(Core::AuditLog, :count).by(1)
22
+ end
23
+
24
+ it "records the changed attributes on update" do
25
+ article = model_class.create!(title: "Hi")
26
+ expect { article.update!(title: "Bye") }
27
+ .to change(Core::AuditLog, :count).by(1)
28
+
29
+ last = Core::AuditLog.recent.first
30
+ expect(last.action).to eq("update")
31
+ expect(last.payload).to include("title" => "Bye")
32
+ end
33
+
34
+ it "creates an audit log on destroy" do
35
+ article = model_class.create!(title: "Hi")
36
+ expect { article.destroy }
37
+ .to change(Core::AuditLog, :count).by(1)
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe Core::Sluggable do
6
+ let(:model_class) do
7
+ Class.new(ActiveRecord::Base) do
8
+ self.table_name = "teams"
9
+ include Core::Sluggable
10
+ sluggable_from :name
11
+ end
12
+ end
13
+
14
+ it "auto-assigns a slug from the source attribute" do
15
+ record = model_class.create!(name: "Acme Corp!")
16
+ expect(record.slug).to eq("acme-corp")
17
+ end
18
+
19
+ it "appends a numeric suffix on collision" do
20
+ model_class.create!(name: "Acme")
21
+ second = model_class.create!(name: "Acme")
22
+ expect(second.slug).to eq("acme-2")
23
+ end
24
+
25
+ it "respects an explicitly-set slug" do
26
+ record = model_class.create!(name: "Whatever", slug: "my-custom")
27
+ expect(record.slug).to eq("my-custom")
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe Core::AuditLog do
6
+ it "rejects unknown actions" do
7
+ log = described_class.new(action: "made_up", auditable_type: "Article", auditable_id: 1)
8
+ expect(log).not_to be_valid
9
+ end
10
+
11
+ describe ".for" do
12
+ it "filters audit logs to a specific record" do
13
+ article = double(class: double(name: "Article"), id: "7")
14
+ described_class.create!(action: "create", auditable_type: "Article", auditable_id: "7")
15
+ described_class.create!(action: "create", auditable_type: "Article", auditable_id: "8")
16
+
17
+ # auditable_id is string-typed (polymorphic over both bigint-PK
18
+ # and UUID-PK auditables — see the migration's comment).
19
+ expect(described_class.for(article).pluck(:auditable_id)).to eq(["7"])
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../rails_helper"
4
+
5
+ RSpec.describe "Core engine boot", type: :integration do
6
+ it "loads the engine" do
7
+ expect(defined?(Core::Engine)).to eq("constant")
8
+ end
9
+
10
+ it "registers record.audited.core in Seams::EventRegistry on boot" do
11
+ expect(Seams::EventRegistry.registered?("record.audited.core")).to be(true)
12
+ end
13
+
14
+ it "exposes the canonical concerns as constants" do
15
+ expect(defined?(Core::Auditable)).to eq("constant")
16
+ expect(defined?(Core::SoftDeletable)).to eq("constant")
17
+ expect(defined?(Core::Sluggable)).to eq("constant")
18
+ expect(defined?(Core::TenantScoped)).to eq("constant")
19
+ expect(defined?(Core::HasCurrentAttributes)).to eq("constant")
20
+ end
21
+
22
+ it "creates the core_audit_logs table from the dummy schema" do
23
+ expect(ActiveRecord::Base.connection.table_exists?(:core_audit_logs)).to be(true)
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe Core::EmailFormatValidator do
6
+ let(:model_class) do
7
+ Class.new do
8
+ include ActiveModel::Validations
9
+ attr_accessor :email
10
+ validates :email, "core/email_format": true
11
+ end
12
+ end
13
+
14
+ it "accepts a normal email" do
15
+ expect(model_class.new.tap { |o| o.email = "x@y.com" }).to be_valid
16
+ end
17
+
18
+ it "rejects an email without @" do
19
+ expect(model_class.new.tap { |o| o.email = "no-at" }).not_to be_valid
20
+ end
21
+
22
+ it "rejects emails with consecutive dots" do
23
+ expect(model_class.new.tap { |o| o.email = "x..y@z.com" }).not_to be_valid
24
+ end
25
+
26
+ it "rejects emails without a TLD" do
27
+ expect(model_class.new.tap { |o| o.email = "x@y" }).not_to be_valid
28
+ end
29
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "rails/generators"
5
+ require "seams"
6
+ require "seams/generators/host_injector"
7
+ require "seams/generators/sibling_rubocop_writer"
8
+ require "seams/generators/dummy_app_writer"
9
+
10
+ module Seams
11
+ module Generators
12
+ # Generates a new Rails engine under engines/<name>/, fully isolated
13
+ # via `isolate_namespace`, with the Seams boundary cops pre-wired in
14
+ # the engine's own .rubocop.yml. After generating the new engine,
15
+ # the cop config of every existing sibling engine is updated so
16
+ # boundary enforcement covers the new engine without manual edits.
17
+ #
18
+ # Run with: bin/rails generate seams:engine billing
19
+ class EngineGenerator < Rails::Generators::NamedBase
20
+ include Seams::Generators::HostInjector
21
+
22
+ source_root File.expand_path("templates", __dir__)
23
+
24
+ NAME_PATTERN = /\A[a-z][a-z0-9_]*\z/
25
+
26
+ def validate_name
27
+ unless NAME_PATTERN.match?(name)
28
+ raise Seams::GeneratorError,
29
+ "Engine name #{name.inspect} must be lowercase letters, digits, " \
30
+ "and underscores, starting with a letter."
31
+ end
32
+
33
+ engine_root = File.join(destination_root, "engines", name)
34
+ return unless File.exist?(engine_root)
35
+
36
+ raise Seams::GeneratorError, "Engine #{name.inspect} already exists at #{engine_root}"
37
+ end
38
+
39
+ def create_gemspec
40
+ template "gemspec.tt", "engines/#{name}/#{name}.gemspec"
41
+ end
42
+
43
+ def create_lib
44
+ template "lib/engine.rb.tt", "engines/#{name}/lib/#{name}/engine.rb"
45
+ template "lib/version.rb.tt", "engines/#{name}/lib/#{name}/version.rb"
46
+ template "lib/root.rb.tt", "engines/#{name}/lib/#{name}.rb"
47
+ end
48
+
49
+ def create_config
50
+ template "config/routes.rb.tt", "engines/#{name}/config/routes.rb"
51
+ end
52
+
53
+ def create_app
54
+ template "app/application_controller.rb.tt",
55
+ "engines/#{name}/app/controllers/#{name}/application_controller.rb"
56
+ # Phase 1.6 — engine-scoped abstract AR base.
57
+ template "app/application_record.rb.tt",
58
+ "engines/#{name}/app/models/#{name}/application_record.rb"
59
+ end
60
+
61
+ # Phase 1.6 — i18n stub + standalone Gemfile + Rakefile so the
62
+ # engine can be tested in isolation (cd engines/<name> && rake).
63
+ def create_locales
64
+ template "config/locales/en.yml.tt",
65
+ "engines/#{name}/config/locales/en.yml"
66
+ end
67
+
68
+ def create_engine_gemfile
69
+ template "Gemfile.tt", "engines/#{name}/Gemfile"
70
+ end
71
+
72
+ def create_engine_rakefile
73
+ template "Rakefile.tt", "engines/#{name}/Rakefile"
74
+ end
75
+
76
+ def create_rubocop_config
77
+ template "rubocop.yml.tt", "engines/#{name}/.rubocop.yml"
78
+ end
79
+
80
+ def create_spec_helper
81
+ template "spec/spec_helper.rb.tt", "engines/#{name}/spec/spec_helper.rb"
82
+ template "spec/example_spec.rb.tt", "engines/#{name}/spec/#{name}_spec.rb"
83
+ end
84
+
85
+ def create_license
86
+ template "LICENSE.tt", "engines/#{name}/LICENSE"
87
+ end
88
+
89
+ def create_readme
90
+ template "README.md.tt", "engines/#{name}/README.md"
91
+ end
92
+
93
+ # Phase 1.6 — per-engine dummy app via the shared DummyAppWriter.
94
+ # Generic engines start with an empty schema; canonical
95
+ # generators (auth/billing/etc) overwrite this with their own
96
+ # schema by calling DummyAppWriter.write! again with the right
97
+ # SCHEMA body. Runs AFTER create_spec_helper so DummyAppWriter's
98
+ # File.write silently overwrites the lightweight Thor template
99
+ # rather than tripping a Thor "conflict" prompt.
100
+ def create_dummy_app
101
+ Seams::Generators::DummyAppWriter.write!(
102
+ engine_path: File.join(destination_root, "engines", name),
103
+ engine_module: module_name,
104
+ mount_at: "/#{name}",
105
+ schema: "# generic engine — no schema yet"
106
+ )
107
+ end
108
+
109
+ def wire_into_host
110
+ # Mount the engine into the host's routes (idempotent — skips
111
+ # if the line already exists, so canonical generators that
112
+ # call this and ALSO mount themselves are safe).
113
+ host_inject_mount(engine_class: "#{module_name}::Engine", at: "/#{name}")
114
+
115
+ # Drop a host-side initializer stub the user can fill in.
116
+ # Skipped if a canonical generator (or the host) has already
117
+ # created one.
118
+ initializer_path = File.join(destination_root, "config/initializers/#{name}.rb")
119
+ if File.exist?(initializer_path)
120
+ say " exist config/initializers/#{name}.rb (kept)", :blue
121
+ elsif File.directory?(File.join(destination_root, "config/initializers"))
122
+ template "host_initializer.rb.tt", "config/initializers/#{name}.rb"
123
+ end
124
+ end
125
+
126
+ def update_sibling_engines
127
+ sibling_dirs = sibling_engine_dirs
128
+ return if sibling_dirs.empty?
129
+
130
+ Seams::Generators::SiblingRubocopWriter.rewrite!(
131
+ engines_root: engines_root,
132
+ dirs: (sibling_dirs + [name]).sort
133
+ )
134
+
135
+ say " update .rubocop.yml of #{sibling_dirs.size} sibling engine(s)", :green
136
+ end
137
+
138
+ def report
139
+ say ""
140
+ say " Engine `#{name}` generated at engines/#{name}/", :green
141
+ say " Run its specs with: bin/rails seams:test[#{name}]"
142
+ say ""
143
+ end
144
+
145
+ private
146
+
147
+ def module_name
148
+ name.split("_").map(&:capitalize).join
149
+ end
150
+
151
+ def engines_root
152
+ File.join(destination_root, "engines")
153
+ end
154
+
155
+ def sibling_engine_dirs
156
+ return [] unless Dir.exist?(engines_root)
157
+
158
+ Dir.children(engines_root)
159
+ .select { |child| File.directory?(File.join(engines_root, child)) }
160
+ .reject { |child| child.start_with?(".") || child == name }
161
+ .sort
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,19 @@
1
+ # Engine-local Gemfile. Mostly redundant in the in-tree layout — the
2
+ # host's Gemfile already loads the engine via path-resolution from
3
+ # seams_engines.rb. This file exists so the engine can ALSO be
4
+ # extracted and bundle-installed standalone (CI matrix run, future
5
+ # gem release, etc.) without rewriting the dependency surface.
6
+ #
7
+ # Keep this file aligned with the engine's gemspec — runtime deps
8
+ # go there, dev/test-only tools go here.
9
+ source "https://rubygems.org"
10
+
11
+ # Specify your gem's dependencies in <%= name %>.gemspec.
12
+ gemspec
13
+
14
+ group :development, :test do
15
+ gem "rspec-rails", "~> 7.1"
16
+ gem "rubocop", require: false
17
+ gem "rubocop-rails", require: false
18
+ gem "rubocop-rspec", require: false
19
+ end
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) <%= Time.now.year %> TODO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # <%= module_name %>
2
+
3
+ > TODO: one-line summary of this engine.
4
+
5
+ ## Events emitted
6
+
7
+ | Event name | Payload | Emitted when |
8
+ | --- | --- | --- |
9
+ | _none yet_ | | |
10
+
11
+ Register every event in `lib/<%= name %>/engine.rb` so it shows up in
12
+ `bin/rails seams:list`.
13
+
14
+ ## Events consumed
15
+
16
+ | Event name | Subscriber | What it does |
17
+ | --- | --- | --- |
18
+ | _none yet_ | | |
19
+
20
+ ## Exposed concerns
21
+
22
+ Modules in this list are intentionally part of the engine's public
23
+ contract. Other engines may `include` them; `Seams/NoCrossEngineModelAccess`
24
+ will not flag them.
25
+
26
+ | Concern | Purpose |
27
+ | --- | --- |
28
+ | _none yet_ | |
29
+
30
+ ## Adapters
31
+
32
+ | Interface | Default | Override via |
33
+ | --- | --- | --- |
34
+ | _none yet_ | | |
35
+
36
+ ## Running the specs
37
+
38
+ ```bash
39
+ bin/rails seams:test[<%= name %>]
40
+ ```
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Engine-local Rakefile. Like the Gemfile, this is mostly redundant
4
+ # in-tree — the host's `rake` invokes the engine's specs via
5
+ # seams:test[<name>]. It exists so the engine can be tested
6
+ # standalone:
7
+ #
8
+ # cd engines/<%= name %> && bundle exec rake
9
+ require "bundler/setup"
10
+ require "rspec/core/rake_task"
11
+
12
+ RSpec::Core::RakeTask.new(:spec)
13
+
14
+ task default: :spec
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= module_name %>
4
+ class ApplicationController < ::ApplicationController
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= module_name %>
4
+ # Engine-scoped abstract Active Record base. Every <%= module_name %>::*
5
+ # AR model inherits from this rather than from
6
+ # ::ApplicationRecord so the engine remains usable in a host that
7
+ # has its own ApplicationRecord with conflicting concerns.
8
+ #
9
+ # Hosts that want shared behaviour across all engines can include
10
+ # a concern here — but only behaviour the engine itself needs.
11
+ # Cross-engine domain logic belongs in the host's ApplicationRecord
12
+ # or in a Core engine concern.
13
+ class ApplicationRecord < ActiveRecord::Base
14
+ self.abstract_class = true
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # Engine-scoped i18n. Every translation key SHOULD start with
2
+ # `<%= name %>.` so two engines can never collide on a key (the host's
3
+ # I18n.load_path picks up engines/*/config/locales/*.yml automatically
4
+ # via Rails::Engine).
5
+ #
6
+ # Usage from inside the engine:
7
+ #
8
+ # I18n.t("<%= name %>.welcome", name: "Ada")
9
+ #
10
+ # Override in the host by dropping a file at
11
+ # config/locales/<%= name %>.en.yml — keys merge top-down, host wins.
12
+ en:
13
+ <%= name %>:
14
+ placeholder: "Translate me — engines/<%= name %>/config/locales/en.yml"
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ <%= module_name %>::Engine.routes.draw do
4
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/<%= name %>/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "<%= name %>"
7
+ spec.version = <%= module_name %>::VERSION
8
+ spec.authors = ["TODO"]
9
+ spec.email = ["TODO"]
10
+ spec.summary = "TODO: short summary of the <%= name %> engine"
11
+ spec.description = "TODO: longer description"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "README.md"]
15
+
16
+ spec.required_ruby_version = ">= 3.2.0"
17
+
18
+ spec.add_dependency "rails", ">= 7.1"
19
+ spec.add_dependency "seams"
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configuration for the <%= module_name %> engine. Uncomment and edit
4
+ # the values you need to override; everything is set to a sensible
5
+ # default in the engine's `Configuration` class.
6
+ #
7
+ # This file is created by `bin/rails generate seams:engine <%= name %>`
8
+ # only on first run; subsequent runs leave it alone.
9
+
10
+ # <%= module_name %>.configure do |config|
11
+ # # Add your settings here. Run `bin/rails console` and inspect
12
+ # # `<%= module_name %>.configuration` to see the available options.
13
+ # end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= module_name %>
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace <%= module_name %>
6
+
7
+ config.generators do |g|
8
+ g.test_framework :rspec
9
+ end
10
+
11
+ initializer "<%= name %>.register_events" do
12
+ # Declare events that this engine emits. Other engines look these
13
+ # up via Seams::EventRegistry to know what to subscribe to.
14
+ #
15
+ # Example:
16
+ # Seams::EventRegistry.register("<%= name %>.something_happened.<%= name %>", emitted_by: "<%= module_name %>")
17
+ end
18
+
19
+ initializer "<%= name %>.append_migrations" do |app|
20
+ unless app.root == root
21
+ config.paths["db/migrate"].expanded.each do |expanded_path|
22
+ app.config.paths["db/migrate"] << expanded_path
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "<%= name %>/version"
4
+ require "<%= name %>/engine"
5
+
6
+ module <%= module_name %>
7
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= module_name %>
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,55 @@
1
+ # Self-contained — engines do not inherit from the host's .rubocop.yml
2
+ # so a host using `rubocop-rails-omakase` (or any other style) doesn't
3
+ # force engine code to follow rules written for application code.
4
+ # Engine code is gem-shaped and uses default RuboCop styles.
5
+
6
+ require:
7
+ - seams/cops
8
+
9
+ AllCops:
10
+ TargetRubyVersion: 4.0
11
+ NewCops: enable
12
+ SuggestExtensions: false
13
+ Exclude:
14
+ - "spec/dummy/**/*"
15
+ - "db/migrate/**/*_create_*.rb"
16
+ - "tmp/**/*"
17
+ - "vendor/**/*"
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+
22
+ Style/StringLiterals:
23
+ EnforcedStyle: double_quotes
24
+
25
+ Layout/LineLength:
26
+ Max: 120
27
+
28
+ Metrics/MethodLength:
29
+ Max: 25
30
+
31
+ # Engine-scoped boundary enforcement. List every other engine in your
32
+ # host application here so this engine cannot reach into them
33
+ # accidentally. Add concerns the other engines expose (e.g.
34
+ # `Billing::Billable`) to ExposedConcerns to allow `include` calls.
35
+ Seams/NoCrossEngineModelAccess:
36
+ Enabled: true
37
+ OwnEngine: <%= module_name %>
38
+ OtherEngines: []
39
+ ExposedConcerns: []
40
+
41
+ Seams/NoCrossEngineDependency:
42
+ Enabled: true
43
+ OwnEngine: <%= name %>
44
+ OtherEngines: []
45
+
46
+ Seams/KnownQueueNames:
47
+ Enabled: true
48
+ KnownQueues:
49
+ - default
50
+ - <%= name %>
51
+
52
+ Seams/MigrationComments:
53
+ Enabled: true
54
+ Include:
55
+ - "db/migrate/**/*.rb"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Force-load this engine's lib/ so the constant resolves whether rspec
4
+ # is run from the host root (`bundle exec rspec engines/<%= name %>/spec`)
5
+ # or from inside the engine. The host's spec_helper does not require us.
6
+ # Rails must load before lib/<%= name %>.rb because engine.rb references
7
+ # Rails::Engine.
8
+ require "rails"
9
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
10
+ require "<%= name %>"
11
+
12
+ RSpec.describe <%= module_name %> do
13
+ it "defines a VERSION constant" do
14
+ expect(<%= module_name %>::VERSION).to match(/\A\d+\.\d+\.\d+\z/)
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["RAILS_ENV"] ||= "test"
4
+
5
+ # This engine isn't a published gem; force-load its lib/ so the
6
+ # constant resolves whether rspec is invoked from the engine or from
7
+ # the host root (`bundle exec rspec engines/<%= name %>/spec`).
8
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
9
+ require "<%= name %>"
10
+
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
14
+ end
15
+
16
+ config.mock_with :rspec do |mocks|
17
+ mocks.verify_partial_doubles = true
18
+ end
19
+
20
+ config.disable_monkey_patching!
21
+ config.order = :random
22
+ Kernel.srand config.seed
23
+ end