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,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "seams/generators/host_injector"
5
+
6
+ module Seams
7
+ module Generators
8
+ # Adds the Seams framework to a host Rails application:
9
+ #
10
+ # - config/initializers/seams.rb (configure adapters)
11
+ # - config/initializers/seams_engines.rb (load engines/* into autoload)
12
+ # - engines/.keep (where future engines live)
13
+ # - lib/tasks/seams.rake (rake namespace)
14
+ #
15
+ # Run with: bin/rails generate seams:install
16
+ class InstallGenerator < Rails::Generators::Base
17
+ include Seams::Generators::HostInjector
18
+
19
+ source_root File.expand_path("templates", __dir__)
20
+
21
+ def create_initializer
22
+ template "seams.rb.tt", "config/initializers/seams.rb"
23
+ end
24
+
25
+ def create_engines_directory
26
+ empty_directory "engines"
27
+ create_file "engines/.keep"
28
+ end
29
+
30
+ def create_rake_tasks
31
+ template "seams.rake.tt", "lib/tasks/seams.rake"
32
+ end
33
+
34
+ def append_engines_to_eager_load
35
+ template "seams_engines.rb.tt", "config/seams_engines.rb"
36
+ end
37
+
38
+ def wire_engines_into_application_rb
39
+ # Each engine under engines/ must be required BEFORE
40
+ # Rails.application.initialize! so its Railtie registers paths
41
+ # (db/migrate, app/*) and initializers with the host. The
42
+ # `require_relative` is injected directly after Bundler.require.
43
+ application_rb = File.join(destination_root, "config/application.rb")
44
+ return unless File.exist?(application_rb)
45
+
46
+ snippet = %(require_relative "seams_engines")
47
+ contents = File.read(application_rb)
48
+ return if contents.include?(snippet)
49
+
50
+ # The default Rails 8 application.rb contains
51
+ # `Bundler.require(*Rails.groups)` verbatim. If a host has
52
+ # customised it (Rails 4-style asset groups, multi-arg
53
+ # Bundler.require, brace-form do-block, trailing comment, ...),
54
+ # the regex misses and Thor silently warns "File unchanged!"
55
+ # — leaving the host bootable but with engines never required.
56
+ # That is the worst kind of failure: silent + production-bug.
57
+ # Print a loud red warning so the user knows to wire it by hand.
58
+ anchor = /Bundler\.require\(\*Rails\.groups\)\n/
59
+ unless contents.match?(anchor)
60
+ say " WARNING config/application.rb has no `Bundler.require(*Rails.groups)` line — " \
61
+ "add `#{snippet}` manually after Bundler.require so engines load before initialize!",
62
+ :red
63
+ return
64
+ end
65
+
66
+ say " inject config/application.rb (require_relative \"seams_engines\")", :green
67
+ inject_into_file(application_rb, "\n#{snippet}\n", after: anchor)
68
+ end
69
+
70
+ def create_host_rubocop
71
+ # Three cases:
72
+ # 1. Host has no .rubocop.yml → write the seams baseline.
73
+ # 2. Host already has one → don't overwrite, but inject an
74
+ # `engines/**/*` Exclude so host RuboCop (which may use
75
+ # rubocop-rails-omakase or another flavor) doesn't lint
76
+ # engine code under rules written for application code.
77
+ # Engines have their own self-contained .rubocop.yml.
78
+ host_path = File.join(destination_root, ".rubocop.yml")
79
+ unless File.exist?(host_path)
80
+ template "rubocop.yml.tt", ".rubocop.yml"
81
+ return
82
+ end
83
+
84
+ return if File.read(host_path).include?("engines/**/*")
85
+
86
+ say " inject .rubocop.yml (Exclude engines + seams.rake)", :green
87
+ append_to_file(host_path, <<~YML)
88
+
89
+ # Engines have their own self-contained .rubocop.yml. Linting them
90
+ # from the host runs gem-style code under whatever flavor of rules
91
+ # the host uses (omakase / etc.) and produces noisy false positives.
92
+ # The gem-generated lib/tasks/seams.rake is excluded for the same
93
+ # reason.
94
+ AllCops:
95
+ Exclude:
96
+ - "engines/**/*"
97
+ - "lib/tasks/seams.rake"
98
+ YML
99
+ end
100
+
101
+ def create_ruby_version
102
+ # The host CI workflow does `ruby-version: ".ruby-version"`, so the
103
+ # host needs a `.ruby-version` file. Rails 8's `rails new` doesn't
104
+ # ship one. Don't overwrite if the host has pinned their own.
105
+ return if File.exist?(File.join(destination_root, ".ruby-version"))
106
+
107
+ template "ruby-version.tt", ".ruby-version"
108
+ end
109
+
110
+ def create_ci_workflow
111
+ template "ci.yml.tt", ".github/workflows/ci.yml"
112
+ end
113
+
114
+ def create_deployment_templates
115
+ # Skip any file the host already has — Rails 8 ships its own
116
+ # Dockerfile and bin/docker-entrypoint.
117
+ template_if_missing "Dockerfile.tt", "Dockerfile"
118
+ template_if_missing "docker-entrypoint.tt", "bin/docker-entrypoint"
119
+ template_if_missing "Procfile.tt", "Procfile"
120
+ template_if_missing "deploy.yml.tt", "config/deploy.yml"
121
+
122
+ full = File.join(destination_root, "bin/docker-entrypoint")
123
+ File.chmod(0o755, full) if File.exist?(full)
124
+ end
125
+
126
+ def create_bin_seams
127
+ template "bin_seams.tt", "bin/seams"
128
+ full_path = File.join(destination_root, "bin/seams")
129
+ File.chmod(0o755, full_path) if File.exist?(full_path)
130
+ end
131
+
132
+ # Phase 1.5 — per-host helper scripts and architecture doc.
133
+ def create_helper_scripts
134
+ template_if_missing "script/collate_coverage.rb.tt", "script/collate_coverage.rb"
135
+ template_if_missing "script/run_affected_tests.sh.tt", "script/run_affected_tests.sh"
136
+
137
+ runner = File.join(destination_root, "script/run_affected_tests.sh")
138
+ File.chmod(0o755, runner) if File.exist?(runner)
139
+ end
140
+
141
+ def create_architecture_doc
142
+ template_if_missing "doc/ARCHITECTURE.md.tt", "doc/ARCHITECTURE.md"
143
+ end
144
+
145
+ def wire_into_host
146
+ # Auto-add seams to the host Gemfile if not already present —
147
+ # covers the `gem install seams` global-install path. Pinned to
148
+ # a pessimistic 0.x to keep major-version bumps explicit.
149
+ host_inject_gem("seams", "~> #{Seams::VERSION}")
150
+ # Every Seams host needs rspec-rails so the per-engine
151
+ # spec/dummy specs can actually run. Idempotent — skipped if
152
+ # the host already has these gems.
153
+ host_inject_gem("rspec-rails", "~> 7.1", group: :test)
154
+ end
155
+
156
+ private
157
+
158
+ def template_if_missing(source, destination_relative)
159
+ full = File.join(destination_root, destination_relative)
160
+ if File.exist?(full)
161
+ say " exist #{destination_relative} (kept)", :blue
162
+ else
163
+ template source, destination_relative
164
+ end
165
+ end
166
+
167
+ public
168
+
169
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
170
+ def post_install_message
171
+ say ""
172
+ say " Seams is installed. Generate your first engine with:", :green
173
+ say " bin/seams core (or bin/rails generate seams:core)"
174
+ say ""
175
+ say " Canonical generators (run in this order):", :yellow
176
+ say " bin/seams core - Core engine (concerns, audit log)"
177
+ say " bin/seams auth - Auth engine (Identity, sessions, OAuth)"
178
+ say " bin/seams accounts - Accounts engine (tenant + Membership + system actor)"
179
+ say " bin/seams notifications - Notifications engine"
180
+ say " bin/seams billing - Billing engine"
181
+ say " bin/seams teams - Teams engine"
182
+ say ""
183
+ say " Optional engines (generate after the canonical six are in place):", :yellow
184
+ say " bin/seams admin - Admin engine (Administrate-backed dashboards"
185
+ say " for the canonical models, Pundit-gated,"
186
+ say " audit-log auto-write). Requires auth + accounts."
187
+ say ""
188
+ say " Follow-up generators (extend an already-installed engine):", :yellow
189
+ say " bin/rails generate seams:auth:add_oauth_provider <name>"
190
+ say " - add a new OAuth provider adapter"
191
+ say " (e.g. linkedin, apple, microsoft)"
192
+ say ""
193
+ say " Other useful commands:", :yellow
194
+ say " bin/seams list - list engines + their events"
195
+ say " bin/seams resolve --eject <engine>/<file>"
196
+ say " - mark a host file as host-owned"
197
+ say " (skipped on regenerate)"
198
+ say " bin/seams resolve --list-markers <engine>"
199
+ say " - list insertion-point markers"
200
+ say " bin/seams resolve --list-ejected - list every ejected file under engines/"
201
+ say ""
202
+ say " Recommended order: core -> auth -> accounts -> notifications -> billing -> teams.", :yellow
203
+ say " Optional: append `admin` last for an Administrate-backed admin surface.", :yellow
204
+ say " See doc/CURRENT_ATTRIBUTES.md (after install) for the per-request namespace cascade.", :yellow
205
+ say " See doc/WRITING_FOLLOW_UP_GENERATORS.md to write your own follow-up generator.", :yellow
206
+ say ""
207
+ end
208
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,52 @@
1
+ # Multi-stage Dockerfile tuned for a Seams-powered Rails app.
2
+ #
3
+ # RUBY_VERSION is read from the host's `.ruby-version` at build time
4
+ # (default 4.0.3 — bump when you bump .ruby-version). Override with
5
+ # `--build-arg RUBY_VERSION=...` if you need to.
6
+ #
7
+ # This file is compatible with Rails 8's `bin/docker-build`.
8
+
9
+ ARG RUBY_VERSION=4.0.3
10
+ FROM ruby:${RUBY_VERSION}-slim AS base
11
+
12
+ ENV BUNDLE_PATH="/usr/local/bundle" \
13
+ BUNDLE_WITHOUT="development:test" \
14
+ BUNDLE_DEPLOYMENT="1" \
15
+ LANG=C.UTF-8 \
16
+ RAILS_ENV=production \
17
+ RAILS_LOG_TO_STDOUT=1
18
+
19
+ WORKDIR /rails
20
+
21
+ RUN apt-get update -qq && \
22
+ apt-get install --no-install-recommends -y \
23
+ build-essential libpq-dev libyaml-dev nodejs git curl && \
24
+ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
25
+
26
+ # --- Bundle install layer ----------------------------------------------------
27
+ FROM base AS gems
28
+
29
+ # Copy the host's Gemfile + gem dependency manifests for the engines.
30
+ # Add new engines by updating the COPY list once.
31
+ COPY Gemfile Gemfile.lock ./
32
+ COPY engines/ ./engines/
33
+
34
+ RUN bundle install --jobs $(nproc) && \
35
+ bundle exec bootsnap precompile --gemfile
36
+
37
+ # --- App layer ---------------------------------------------------------------
38
+ FROM base AS app
39
+
40
+ COPY --from=gems /usr/local/bundle /usr/local/bundle
41
+ COPY . .
42
+
43
+ RUN bundle exec bootsnap precompile app/ lib/ engines/ && \
44
+ SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile
45
+
46
+ RUN useradd rails --create-home --shell /bin/bash && \
47
+ chown -R rails:rails /rails db log storage tmp
48
+ USER rails
49
+
50
+ EXPOSE 3000
51
+ ENTRYPOINT ["./bin/docker-entrypoint"]
52
+ CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
@@ -0,0 +1,14 @@
1
+ # Foreman / Kamal Procfile for a Seams-powered host.
2
+ #
3
+ # The worker process expects Solid Queue tables to already exist.
4
+ # If you haven't installed Solid Queue's migrations yet:
5
+ #
6
+ # bin/rails solid_queue:install:migrations
7
+ # bin/rails db:migrate
8
+ #
9
+ # Solid Queue Recurring is also needed if you ship the Notifications
10
+ # engine (it relies on the recurring sweeper). After installing,
11
+ # add the engine's recurring entries to config/recurring.yml — see
12
+ # the engine's README for the schedule.
13
+ web: bundle exec rails server -p ${PORT:-3000} -b 0.0.0.0
14
+ worker: bundle exec rails solid_queue:start
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Convenience wrapper around the seams:* generators and rake tasks.
5
+ #
6
+ # bin/seams install # bin/rails generate seams:install
7
+ # bin/seams engine billing # bin/rails generate seams:engine billing
8
+ # bin/seams core | auth | accounts | notifications | billing | teams | admin
9
+ # bin/seams remove billing # bin/rails generate seams:remove billing
10
+ # bin/seams list # bin/rails seams:list
11
+ # bin/seams test billing # bin/rails seams:test[billing]
12
+ # bin/seams quality billing # bin/rails seams:quality[billing]
13
+ # bin/seams resolve --eject <engine>/<file>
14
+ # bin/seams resolve --list-markers <engine>
15
+ # bin/seams resolve --list-ejected
16
+
17
+ require "shellwords"
18
+
19
+ CANONICAL = %w[install core auth accounts notifications billing teams admin].freeze
20
+ GENERIC = %w[engine remove].freeze
21
+ TASKS = %w[list].freeze
22
+ ENGINE_TASKS = %w[test quality].freeze
23
+
24
+ # `resolve` runs in-process — it doesn't need bin/rails because it
25
+ # doesn't need the Rails environment. We require seams from the host
26
+ # bundle and dispatch directly. This keeps `bin/seams resolve` fast
27
+ # (~50ms) compared to ~3s for any bin/rails invocation.
28
+ def run_resolve(rest)
29
+ if rest.empty? || %w[--help -h help].include?(rest.first)
30
+ puts <<~HELP
31
+ Usage:
32
+ bin/seams resolve --eject <engine>/<file_relative>
33
+ Mark a single host file as host-owned (skipped on regenerate).
34
+ Example: bin/seams resolve --eject auth/app/mailers/auth/passwords_mailer.rb
35
+
36
+ bin/seams resolve --list-markers <engine>
37
+ List every insertion-point marker the engine ships.
38
+
39
+ bin/seams resolve --list-ejected
40
+ List every ejected file under engines/.
41
+ HELP
42
+ exit 0
43
+ end
44
+
45
+ flag = rest.shift
46
+ argument = rest.shift
47
+
48
+ mode =
49
+ case flag
50
+ when "--eject" then :eject
51
+ when "--list-markers" then :list_markers
52
+ when "--list-ejected" then :list_ejected
53
+ else
54
+ abort "Unknown resolve flag: #{flag.inspect}. Run `bin/seams resolve --help` for usage."
55
+ end
56
+
57
+ require "bundler/setup"
58
+ require "seams/cli"
59
+ ok = Seams::CLI.resolve(mode: mode, argument: argument)
60
+ exit(ok ? 0 : 1)
61
+ end
62
+
63
+ cmd, *rest = ARGV
64
+ case cmd
65
+ when nil, "help", "-h", "--help"
66
+ puts <<~HELP
67
+ Usage: bin/seams <command> [args]
68
+
69
+ Canonical generators:
70
+ install Install the framework
71
+ engine <name> Generate a generic engine
72
+ core | auth | accounts | notifications | billing | teams | admin
73
+ Generate a canonical engine
74
+ remove <name> Remove an engine
75
+
76
+ Follow-up generators (extend an already-installed engine):
77
+ bin/rails generate seams:auth:add_oauth_provider <name>
78
+ Add a new OAuth provider adapter
79
+ (e.g. linkedin, apple, microsoft)
80
+ See doc/WRITING_FOLLOW_UP_GENERATORS.md for how to write your own.
81
+
82
+ Diagnostics + escape hatch:
83
+ list List engines + their events
84
+ test <engine> Run an engine's specs
85
+ quality <engine> Run rubocop on an engine
86
+ resolve --eject <engine>/<file>
87
+ Mark a host file as host-owned (skipped on regenerate)
88
+ resolve --list-markers <engine>
89
+ List insertion-point markers in the engine
90
+ resolve --list-ejected List every ejected file under engines/
91
+ resolve --help Detail on each resolve mode
92
+ HELP
93
+ exit 0
94
+ when "resolve"
95
+ run_resolve(rest)
96
+ when *CANONICAL
97
+ exec "bin/rails", "generate", "seams:#{cmd}", *rest
98
+ when *GENERIC
99
+ exec "bin/rails", "generate", "seams:#{cmd}", *rest
100
+ when *TASKS
101
+ exec "bin/rails", "seams:#{cmd}"
102
+ when *ENGINE_TASKS
103
+ abort "Usage: bin/seams #{cmd} <engine>" if rest.empty?
104
+ exec "bin/rails", "seams:#{cmd}[#{rest.first}]"
105
+ else
106
+ abort "Unknown command: #{cmd.inspect}. Run `bin/seams help` for the list."
107
+ end
@@ -0,0 +1,123 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ env:
13
+ CI: "true"
14
+ BUNDLE_JOBS: 4
15
+ BUNDLE_RETRY: 3
16
+
17
+ jobs:
18
+ lint:
19
+ name: RuboCop
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ".ruby-version"
26
+ bundler-cache: true
27
+ - run: bundle exec rubocop --parallel
28
+
29
+ security:
30
+ name: Security (brakeman + bundle-audit)
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - uses: ruby/setup-ruby@v1
35
+ with:
36
+ ruby-version: ".ruby-version"
37
+ bundler-cache: true
38
+ - run: bundle exec brakeman --no-pager --no-progress --quiet
39
+ - run: bundle exec bundle-audit check --update
40
+
41
+ # Discover engines once and fan out — every engine's specs run in
42
+ # parallel under its own job. Seams custom RuboCop cops still run
43
+ # against engine code (in the lint job above) but unit specs run
44
+ # per engine for fast signal.
45
+ discover:
46
+ name: Discover engines
47
+ runs-on: ubuntu-latest
48
+ outputs:
49
+ engines: ${{ steps.set.outputs.engines }}
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - id: set
53
+ run: |
54
+ if [ -d engines ]; then
55
+ engines=$(find engines -mindepth 1 -maxdepth 1 -type d \
56
+ -not -name '.*' -printf '%f\n' \
57
+ | jq -R -s -c 'split("\n") | map(select(length > 0))')
58
+ else
59
+ engines='[]'
60
+ fi
61
+ echo "engines=$engines" >> "$GITHUB_OUTPUT"
62
+
63
+ test_engine:
64
+ name: Test ${{ matrix.engine }}
65
+ needs: discover
66
+ if: needs.discover.outputs.engines != '[]'
67
+ runs-on: ubuntu-latest
68
+ strategy:
69
+ fail-fast: false
70
+ matrix:
71
+ engine: ${{ fromJson(needs.discover.outputs.engines) }}
72
+ services:
73
+ postgres:
74
+ image: postgres:16
75
+ env:
76
+ POSTGRES_PASSWORD: postgres
77
+ ports: ["5432:5432"]
78
+ options: >-
79
+ --health-cmd pg_isready
80
+ --health-interval 10s
81
+ --health-timeout 5s
82
+ --health-retries 5
83
+ env:
84
+ DATABASE_URL: "postgres://postgres:postgres@localhost:5432"
85
+ PGHOST: "localhost"
86
+ PGPORT: "5432"
87
+ PGUSER: "postgres"
88
+ PGPASSWORD: "postgres"
89
+ steps:
90
+ - uses: actions/checkout@v4
91
+ - uses: ruby/setup-ruby@v1
92
+ with:
93
+ ruby-version: ".ruby-version"
94
+ bundler-cache: true
95
+ - run: bundle exec rspec --default-path engines/${{ matrix.engine }}/spec engines/${{ matrix.engine }}/spec
96
+
97
+ test_app:
98
+ name: Test host app
99
+ runs-on: ubuntu-latest
100
+ services:
101
+ postgres:
102
+ image: postgres:16
103
+ env:
104
+ POSTGRES_PASSWORD: postgres
105
+ ports: ["5432:5432"]
106
+ options: >-
107
+ --health-cmd pg_isready
108
+ --health-interval 10s
109
+ --health-timeout 5s
110
+ --health-retries 5
111
+ env:
112
+ DATABASE_URL: "postgres://postgres:postgres@localhost:5432"
113
+ PGHOST: "localhost"
114
+ PGPORT: "5432"
115
+ PGUSER: "postgres"
116
+ PGPASSWORD: "postgres"
117
+ steps:
118
+ - uses: actions/checkout@v4
119
+ - uses: ruby/setup-ruby@v1
120
+ with:
121
+ ruby-version: ".ruby-version"
122
+ bundler-cache: true
123
+ - run: bundle exec rspec spec
@@ -0,0 +1,63 @@
1
+ # Kamal deploy config — TEMPLATE. Every value below is a placeholder
2
+ # and `kamal setup` will fail until you replace them. Reference:
3
+ # https://kamal-deploy.org
4
+ #
5
+ # Required edits before first deploy:
6
+ # - service / image — your app + container registry repo
7
+ # - servers.web / servers.worker hosts — real IPs or hostnames
8
+ # - registry.username + KAMAL_REGISTRY_PASSWORD secret in .kamal/secrets
9
+ # - accessories.postgres — choose between Kamal-managed Postgres
10
+ # (uncomment + size the volume) or a managed service (delete the
11
+ # accessory + provide DATABASE_URL via env.secret)
12
+ # - solid_queue migrations: run `bin/rails solid_queue:install:migrations`
13
+ # once locally, commit the migrations, and they'll apply on first deploy
14
+ # via bin/docker-entrypoint.
15
+
16
+ # TODO: replace placeholders before running `kamal setup`.
17
+ service: TODO_my_app
18
+ image: TODO_org/TODO_my_app
19
+
20
+ servers:
21
+ web:
22
+ - TODO.web.example.com
23
+ worker:
24
+ cmd: bundle exec rails solid_queue:start
25
+ hosts:
26
+ - TODO.worker.example.com
27
+
28
+ registry:
29
+ username: TODO_registry_user
30
+ password:
31
+ - KAMAL_REGISTRY_PASSWORD
32
+
33
+ env:
34
+ clear:
35
+ RAILS_LOG_TO_STDOUT: "1"
36
+ RAILS_SERVE_STATIC_FILES: "1"
37
+ secret:
38
+ - RAILS_MASTER_KEY
39
+ - DATABASE_URL
40
+ - REDIS_URL
41
+ - STRIPE_SECRET_KEY
42
+ - STRIPE_WEBHOOK_SECRET
43
+
44
+ # Uncomment + size if you want Kamal to manage Postgres for you.
45
+ # Otherwise delete this block and provide DATABASE_URL via env.secret.
46
+ # accessories:
47
+ # postgres:
48
+ # image: postgres:16
49
+ # host: TODO.web.example.com
50
+ # port: "127.0.0.1:5432:5432"
51
+ # env:
52
+ # clear:
53
+ # POSTGRES_USER: TODO_app
54
+ # POSTGRES_DB: TODO_app_production
55
+ # secret:
56
+ # - POSTGRES_PASSWORD
57
+ # volumes:
58
+ # - /var/lib/postgresql/data:/var/lib/postgresql/data
59
+
60
+ asset_path: /rails/public/assets
61
+
62
+ # Engines run in the same container — no per-engine deployment.
63
+ # Migrations apply automatically via bin/docker-entrypoint.
@@ -0,0 +1,86 @@
1
+ # Architecture (host)
2
+
3
+ > Generated by `seams:install`. Edit freely — this is YOUR doc, not
4
+ > the gem's. Cross-reference the gem's own `doc/ARCHITECTURE.md` (in
5
+ > the seams gem source) for framework-level details.
6
+
7
+ ## The model
8
+
9
+ ```
10
+ +-------------------+ +---------------------+
11
+ | This Rails app | | engines/auth/ |
12
+ | | | engines/billing/ |
13
+ | routes.rb mounts | -----> | engines/teams/ |
14
+ | every engine | | engines/notifs/ |
15
+ | | | engines/<your>/ |
16
+ +-------------------+ +---------------------+
17
+ | ^
18
+ | events (Seams::Events |
19
+ | ::Publisher) |
20
+ v |
21
+ +-------------------+ |
22
+ | Subscribers in | -----------------+
23
+ | engines/*/ |
24
+ +-------------------+
25
+ ```
26
+
27
+ Every engine is a real `Rails::Engine` with `isolate_namespace`. The
28
+ host application loads them all in one process. There are NO HTTP
29
+ calls between engines — they communicate via the in-process event
30
+ bus.
31
+
32
+ ## Where things live
33
+
34
+ | Concern | Lives under |
35
+ | --- | --- |
36
+ | Domain logic per concern | `engines/<name>/` |
37
+ | Cross-engine events | `engines/<name>/lib/<name>/engine.rb` (Publisher.subscribe) |
38
+ | Routing | host `config/routes.rb` (mount lines) + each engine's own |
39
+ | Migrations | each engine ships its own; the host's `db/migrate/` carries the timestamps |
40
+ | Tests | each engine has its own `spec/dummy/` + `spec/runtime/` |
41
+
42
+ ## Cross-engine rules
43
+
44
+ The seams gem ships four RuboCop cops (`require: seams/cops`) that
45
+ enforce these. CI fails if you break them:
46
+
47
+ 1. **No cross-engine model access.** `Engines::A` cannot
48
+ `::B::Model.find(...)` directly. Subscribe to events instead.
49
+ 2. **No cross-engine constant references** other than concerns
50
+ exposed via `ExposedConcerns:` in the engine's `.rubocop.yml`.
51
+ 3. **No cross-engine database joins.** Engines must own their tables.
52
+ 4. **No cross-engine factories** in tests.
53
+
54
+ If you find yourself wanting to break one of these — that's usually a
55
+ signal the boundary is wrong. Either the responsibility belongs in
56
+ the other engine, or there's a missing third engine.
57
+
58
+ ## Adding a new engine
59
+
60
+ ```bash
61
+ bin/seams engine reporting # generic
62
+ bin/rails generate seams:auth # canonical
63
+ ```
64
+
65
+ The generator wires up the host (mount line, initializer,
66
+ gem dependencies). See `doc/ADDING_AN_ENGINE.md` (if your team has
67
+ extracted it from the seams gem) for the full conventions.
68
+
69
+ ## Removing an engine
70
+
71
+ ```bash
72
+ bin/seams remove reporting --force
73
+ bin/rails db:migrate # applies the auto-generated drop migration
74
+ ```
75
+
76
+ The remover writes a drop-table migration into `db/migrate/` for the
77
+ tables that engine created. Run `db:migrate` to apply.
78
+
79
+ ## Testing
80
+
81
+ ```bash
82
+ bundle exec rspec # everything
83
+ script/run_affected_tests.sh # only engines changed vs main
84
+ script/run_affected_tests.sh --all # every engine, no diff filtering
85
+ ruby script/collate_coverage.rb # merge coverage reports across engines
86
+ ```
@@ -0,0 +1,27 @@
1
+ #!/bin/bash
2
+ # bin/docker-entrypoint
3
+ #
4
+ # Runs db:prepare on the WEB container only — workers skip it. With
5
+ # Kamal, web boots before workers, so by the time worker starts the
6
+ # migrations are already applied.
7
+ #
8
+ # If you want stricter migration control (CI-driven, separate job),
9
+ # comment the db:prepare branch out and run migrations from a Kamal
10
+ # pre-deploy hook:
11
+ #
12
+ # kamal app exec --reuse "bin/rails db:migrate"
13
+ #
14
+ # Then redeploy the web image. The race-free pattern.
15
+
16
+ set -e
17
+
18
+ # Web command is `bundle exec rails server` (or `./bin/rails server`).
19
+ # Workers use `bundle exec rails solid_queue:start` — never run
20
+ # db:prepare from a worker, two containers racing CREATE TABLE breaks.
21
+ case "${1}${2}${3}" in
22
+ *server*|*"./bin/rails"*"server"*|*"bin/rails"*"server"*)
23
+ bundle exec rails db:prepare
24
+ ;;
25
+ esac
26
+
27
+ exec "$@"