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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +335 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
- data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
- data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
- data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
- data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
- data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
- data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
- data/lib/generators/seams/admin/admin_generator.rb +852 -0
- data/lib/generators/seams/admin/templates/README.md.tt +266 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
- data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
- data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
- data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
- data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
- data/lib/generators/seams/auth/auth_generator.rb +311 -0
- data/lib/generators/seams/auth/templates/README.md.tt +289 -0
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
- data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
- data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
- data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
- data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
- data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
- data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
- data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
- data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
- data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
- data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
- data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
- data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
- data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
- data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
- data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
- data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
- data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
- data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
- data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
- data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
- data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
- data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
- data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
- data/lib/generators/seams/billing/billing_generator.rb +476 -0
- data/lib/generators/seams/billing/templates/README.md.tt +355 -0
- data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
- data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
- data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
- data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
- data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
- data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
- data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
- data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
- data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
- data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
- data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
- data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
- data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
- data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
- data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
- data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
- data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
- data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
- data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
- data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
- data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
- data/lib/generators/seams/core/core_generator.rb +191 -0
- data/lib/generators/seams/core/templates/README.md.tt +45 -0
- data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
- data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
- data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
- data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
- data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
- data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
- data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
- data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
- data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
- data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
- data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
- data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
- data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
- data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
- data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
- data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
- data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
- data/lib/generators/seams/engine/engine_generator.rb +165 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
- data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
- data/lib/generators/seams/engine/templates/README.md.tt +40 -0
- data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
- data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
- data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
- data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
- data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
- data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
- data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
- data/lib/generators/seams/install/install_generator.rb +211 -0
- data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
- data/lib/generators/seams/install/templates/Procfile.tt +14 -0
- data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
- data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
- data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
- data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
- data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
- data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
- data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
- data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
- data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
- data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
- data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
- data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
- data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
- data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
- data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
- data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
- data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
- data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
- data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
- data/lib/generators/seams/remove/remove_generator.rb +259 -0
- data/lib/generators/seams/teams/teams_generator.rb +298 -0
- data/lib/generators/seams/teams/templates/README.md.tt +88 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
- data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
- data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
- data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
- data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
- data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
- data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
- data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
- data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
- data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
- data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
- data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
- data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
- data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
- data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
- data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
- data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
- data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
- data/lib/seams/cli/list.rb +111 -0
- data/lib/seams/cli/quality.rb +99 -0
- data/lib/seams/cli/resolve.rb +276 -0
- data/lib/seams/cli/test_changed.rb +116 -0
- data/lib/seams/cli.rb +48 -0
- data/lib/seams/configuration.rb +19 -0
- data/lib/seams/cops/known_queue_names.rb +42 -0
- data/lib/seams/cops/migration_comments.rb +68 -0
- data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
- data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
- data/lib/seams/cops.rb +18 -0
- data/lib/seams/event_registry.rb +49 -0
- data/lib/seams/events/adapter.rb +24 -0
- data/lib/seams/events/adapters/active_support.rb +31 -0
- data/lib/seams/events/publisher.rb +178 -0
- data/lib/seams/events.rb +39 -0
- data/lib/seams/generators/dummy_app_writer.rb +424 -0
- data/lib/seams/generators/eject_aware.rb +102 -0
- data/lib/seams/generators/follow_up_generator.rb +148 -0
- data/lib/seams/generators/host_injector.rb +124 -0
- data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
- data/lib/seams/generators/splicer.rb +217 -0
- data/lib/seams/observability/adapter.rb +33 -0
- data/lib/seams/observability/adapters/rails_logger.rb +59 -0
- data/lib/seams/observability.rb +34 -0
- data/lib/seams/runtime.rb +23 -0
- data/lib/seams/version.rb +5 -0
- data/lib/seams.rb +23 -0
- 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 "$@"
|