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,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "rails/generators"
|
|
5
|
+
require "seams"
|
|
6
|
+
require "generators/seams/engine/engine_generator"
|
|
7
|
+
require "seams/generators/host_injector"
|
|
8
|
+
require "seams/generators/eject_aware"
|
|
9
|
+
require "seams/generators/dummy_app_writer"
|
|
10
|
+
|
|
11
|
+
module Seams
|
|
12
|
+
module Generators
|
|
13
|
+
# Generates a canonical Core engine on top of the generic engine
|
|
14
|
+
# scaffold. Ships the seven primitive concerns + AuditLog model
|
|
15
|
+
# other engines lean on:
|
|
16
|
+
#
|
|
17
|
+
# - Core::Auditable — records create/update/destroy in audit_logs
|
|
18
|
+
# - Core::SoftDeletable — deleted_at column + default_scope
|
|
19
|
+
# - Core::Sluggable — auto-generated URL slug from a configurable field
|
|
20
|
+
# - Core::TenantScoped — scopes records to Current.team
|
|
21
|
+
# - Core::HasCurrentAttributes — Current.user / Current.team
|
|
22
|
+
# - Core::EventPublisher — convenience wrapper around Seams::Events::Publisher
|
|
23
|
+
# - Core::EmailFormatValidator — custom validator
|
|
24
|
+
#
|
|
25
|
+
# Run with: bin/rails generate seams:core
|
|
26
|
+
class CoreGenerator < Rails::Generators::Base
|
|
27
|
+
include Seams::Generators::HostInjector
|
|
28
|
+
include Seams::Generators::EjectAware
|
|
29
|
+
|
|
30
|
+
source_root File.expand_path("templates", __dir__)
|
|
31
|
+
|
|
32
|
+
ENGINE_NAME = "core"
|
|
33
|
+
|
|
34
|
+
def create_base_engine
|
|
35
|
+
EngineGenerator.start([ENGINE_NAME], destination_root: destination_root)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def overwrite_engine_entry_point
|
|
39
|
+
# engine.rb / lib/core.rb stay framework-managed.
|
|
40
|
+
template "lib/engine.rb.tt", engine_path("lib/core/engine.rb"), force: true
|
|
41
|
+
template "lib/core.rb.tt", engine_path("lib/core.rb"), force: true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def create_models
|
|
45
|
+
template_unless_ejected "app/models/application_record.rb.tt",
|
|
46
|
+
engine_path("app/models/core/application_record.rb")
|
|
47
|
+
template_unless_ejected "app/models/audit_log.rb.tt",
|
|
48
|
+
engine_path("app/models/core/audit_log.rb")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_concerns
|
|
52
|
+
template_unless_ejected "app/models/concerns/auditable.rb.tt",
|
|
53
|
+
engine_path("app/models/concerns/core/auditable.rb")
|
|
54
|
+
template_unless_ejected "app/models/concerns/soft_deletable.rb.tt",
|
|
55
|
+
engine_path("app/models/concerns/core/soft_deletable.rb")
|
|
56
|
+
template_unless_ejected "app/models/concerns/sluggable.rb.tt",
|
|
57
|
+
engine_path("app/models/concerns/core/sluggable.rb")
|
|
58
|
+
template_unless_ejected "app/models/concerns/tenant_scoped.rb.tt",
|
|
59
|
+
engine_path("app/models/concerns/core/tenant_scoped.rb")
|
|
60
|
+
template_unless_ejected "app/controllers/concerns/has_current_attributes.rb.tt",
|
|
61
|
+
engine_path("app/controllers/concerns/core/has_current_attributes.rb")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def create_services_and_validators
|
|
65
|
+
template_unless_ejected "app/services/event_publisher.rb.tt",
|
|
66
|
+
engine_path("app/services/core/event_publisher.rb")
|
|
67
|
+
template_unless_ejected "app/validators/email_format_validator.rb.tt",
|
|
68
|
+
engine_path("app/validators/core/email_format_validator.rb")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def create_current
|
|
72
|
+
template_unless_ejected "app/models/current.rb.tt",
|
|
73
|
+
engine_path("app/models/core/current.rb")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def create_migration
|
|
77
|
+
template "db/migrate/create_core_audit_logs.rb.tt",
|
|
78
|
+
engine_path("db/migrate/#{timestamp}_create_core_audit_logs.rb")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def create_specs
|
|
82
|
+
template_unless_ejected "spec/models/audit_log_spec.rb.tt",
|
|
83
|
+
engine_path("spec/models/core/audit_log_spec.rb")
|
|
84
|
+
template_unless_ejected "spec/concerns/auditable_spec.rb.tt",
|
|
85
|
+
engine_path("spec/concerns/core/auditable_spec.rb")
|
|
86
|
+
template_unless_ejected "spec/concerns/sluggable_spec.rb.tt",
|
|
87
|
+
engine_path("spec/concerns/core/sluggable_spec.rb")
|
|
88
|
+
template_unless_ejected "spec/validators/email_format_validator_spec.rb.tt",
|
|
89
|
+
engine_path("spec/validators/core/email_format_validator_spec.rb")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def overwrite_readme
|
|
93
|
+
template "README.md.tt", engine_path("README.md"), force: true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def update_exposed_concerns
|
|
97
|
+
rubocop_path = engine_path(".rubocop.yml")
|
|
98
|
+
return unless File.exist?(rubocop_path)
|
|
99
|
+
|
|
100
|
+
contents = File.read(rubocop_path)
|
|
101
|
+
replacement = " ExposedConcerns:\n " \
|
|
102
|
+
"- Core::Auditable\n " \
|
|
103
|
+
"- Core::SoftDeletable\n " \
|
|
104
|
+
"- Core::Sluggable\n " \
|
|
105
|
+
"- Core::TenantScoped\n " \
|
|
106
|
+
"- Core::HasCurrentAttributes"
|
|
107
|
+
contents.sub!(/^ ExposedConcerns: \[\]$/, replacement)
|
|
108
|
+
File.write(rubocop_path, contents)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def create_dummy_app
|
|
112
|
+
# Post Wave 9: the dummy app does NOT ship a host User model.
|
|
113
|
+
# The auditable spec stubs its own `Article` (via stub_const)
|
|
114
|
+
# and the tenant_scoped/sluggable specs work against the
|
|
115
|
+
# `articles`/`teams` tables in dummy_schema. There is no
|
|
116
|
+
# remaining caller for a top-level User class.
|
|
117
|
+
Seams::Generators::DummyAppWriter.write!(
|
|
118
|
+
engine_path: File.join(destination_root, "engines", ENGINE_NAME),
|
|
119
|
+
engine_module: "Core",
|
|
120
|
+
schema: dummy_schema
|
|
121
|
+
)
|
|
122
|
+
template "spec/runtime/boot_spec.rb.tt",
|
|
123
|
+
engine_path("spec/runtime/core_boot_spec.rb")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def wire_into_host
|
|
127
|
+
host_inject_mount(engine_class: "Core::Engine", at: "/")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def report_summary
|
|
131
|
+
say ""
|
|
132
|
+
say " Core engine generated at engines/core/", :green
|
|
133
|
+
say ""
|
|
134
|
+
say " Next steps:", :yellow
|
|
135
|
+
say " 1. bin/rails db:migrate"
|
|
136
|
+
say " 2. Mix concerns into your models as needed:"
|
|
137
|
+
say " include Core::Auditable, Core::SoftDeletable, Core::Sluggable, etc."
|
|
138
|
+
say " 3. Run the engine specs: bin/rails seams:test[core]"
|
|
139
|
+
say ""
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def engine_path(relative)
|
|
145
|
+
File.join(destination_root, "engines", ENGINE_NAME, relative)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def dummy_schema
|
|
149
|
+
<<~SCHEMA
|
|
150
|
+
create_table :core_audit_logs do |t|
|
|
151
|
+
t.string :action, null: false
|
|
152
|
+
t.string :auditable_type
|
|
153
|
+
t.string :auditable_id
|
|
154
|
+
t.bigint :actor_id
|
|
155
|
+
t.jsonb :payload, null: false, default: {}
|
|
156
|
+
t.timestamps
|
|
157
|
+
end
|
|
158
|
+
add_index :core_audit_logs, %i[auditable_type auditable_id]
|
|
159
|
+
|
|
160
|
+
create_table :articles do |t|
|
|
161
|
+
t.string :title
|
|
162
|
+
t.string :slug
|
|
163
|
+
t.datetime :deleted_at
|
|
164
|
+
t.bigint :team_id
|
|
165
|
+
t.timestamps
|
|
166
|
+
end
|
|
167
|
+
add_index :articles, :slug, unique: true
|
|
168
|
+
|
|
169
|
+
create_table :teams do |t|
|
|
170
|
+
t.string :name
|
|
171
|
+
t.string :slug
|
|
172
|
+
t.timestamps
|
|
173
|
+
end
|
|
174
|
+
add_index :teams, :slug, unique: true
|
|
175
|
+
SCHEMA
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Core's migration sits AHEAD of the canonical engine offsets
|
|
179
|
+
# (auth +0/+1, notifications +100, billing +200..+202, teams
|
|
180
|
+
# +300..+302) so other engines' tables can reference audit_logs
|
|
181
|
+
# if they ever need to.
|
|
182
|
+
def timestamp
|
|
183
|
+
base = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
|
184
|
+
# Use a NEGATIVE offset (subtract 1000) so core migrates first
|
|
185
|
+
# in the standard ordering. This is safe because timestamps are
|
|
186
|
+
# just sortable strings.
|
|
187
|
+
(base - 1000).to_s
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Core
|
|
2
|
+
|
|
3
|
+
> Foundation primitives every Seams engine can build on. Concerns for
|
|
4
|
+
> auditing, soft delete, slugs, multi-tenancy, plus shared services
|
|
5
|
+
> and validators.
|
|
6
|
+
|
|
7
|
+
## Events emitted
|
|
8
|
+
|
|
9
|
+
| Event name | Payload | Emitted when |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `record.audited.core` | `{ action:, type:, id:, actor_id: }` | `Core::Auditable` records an entry on create/update/destroy |
|
|
12
|
+
|
|
13
|
+
## Events consumed
|
|
14
|
+
|
|
15
|
+
This engine does not subscribe to any other engine's events.
|
|
16
|
+
|
|
17
|
+
## Exposed concerns
|
|
18
|
+
|
|
19
|
+
| Concern | Purpose |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `Core::Auditable` | Records audit entries for the model on CRUD. |
|
|
22
|
+
| `Core::SoftDeletable` | `deleted_at` tombstone + default scope. |
|
|
23
|
+
| `Core::Sluggable` | Auto-generated URL slug with collision suffixing. |
|
|
24
|
+
| `Core::TenantScoped` | Auto-scopes records to `Core::Current.team`. |
|
|
25
|
+
| `Core::HasCurrentAttributes` | Populates `Core::Current` on every request. |
|
|
26
|
+
|
|
27
|
+
## Services + validators
|
|
28
|
+
|
|
29
|
+
| Symbol | Purpose |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `Core::EventPublisher.publish` | Wraps `Seams::Events::Publisher.publish` and adds actor/team/request id. |
|
|
32
|
+
| `Core::EmailFormatValidator` | Stricter email-shape validator (`validates :email, email_format: true`). |
|
|
33
|
+
| `Core::Current` (CurrentAttributes) | Per-request `user`, `team`, `request_id`. |
|
|
34
|
+
|
|
35
|
+
## Mounting
|
|
36
|
+
|
|
37
|
+
The engine has no routes — it ships shared code, not endpoints. The
|
|
38
|
+
generator mounts at `/` for completeness; you can remove the mount
|
|
39
|
+
line if you don't need any routing entry point.
|
|
40
|
+
|
|
41
|
+
## Running the specs
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bin/rails seams:test[core]
|
|
45
|
+
```
|
data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Core
|
|
6
|
+
# Mix into ApplicationController to populate Core::Current at the
|
|
7
|
+
# start of every request. Hosts override #resolve_current_user and
|
|
8
|
+
# #resolve_current_team to plug in their own auth/tenant lookup —
|
|
9
|
+
# the default implementations look for `Auth::Current.identity`
|
|
10
|
+
# (the Auth engine's per-request namespace post-Wave-9) and
|
|
11
|
+
# `params[:team_id]` (the Teams engine).
|
|
12
|
+
#
|
|
13
|
+
# `Core::Current.user` is best read as "the current actor" — the
|
|
14
|
+
# auditable concern writes `actor_id = Core::Current.user&.id`. By
|
|
15
|
+
# default that's the signed-in Identity. Hosts that maintain a
|
|
16
|
+
# domain-specific User on top of Auth::Identity can override
|
|
17
|
+
# #resolve_current_user to point at that User instead.
|
|
18
|
+
#
|
|
19
|
+
# class ApplicationController < ActionController::Base
|
|
20
|
+
# include Core::HasCurrentAttributes
|
|
21
|
+
# end
|
|
22
|
+
module HasCurrentAttributes
|
|
23
|
+
extend ActiveSupport::Concern
|
|
24
|
+
|
|
25
|
+
included do
|
|
26
|
+
before_action :populate_current_attributes
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def populate_current_attributes
|
|
32
|
+
Core::Current.user = resolve_current_user
|
|
33
|
+
Core::Current.team = resolve_current_team
|
|
34
|
+
Core::Current.request_id = request.request_id if respond_to?(:request)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Default resolution order (post-Wave-9):
|
|
38
|
+
# 1. `Auth::Current.identity` — the canonical signed-in human.
|
|
39
|
+
# 2. `current_identity` — the helper Auth engine exposes.
|
|
40
|
+
# 3. `current_user` — the legacy Wave-8 helper, kept
|
|
41
|
+
# so hosts that maintain a User on top of Auth::Identity
|
|
42
|
+
# stay working without overriding the concern.
|
|
43
|
+
# Hosts override this method to plug in a different actor.
|
|
44
|
+
def resolve_current_user
|
|
45
|
+
if defined?(Auth::Current) && Auth::Current.respond_to?(:identity) && Auth::Current.identity
|
|
46
|
+
return Auth::Current.identity
|
|
47
|
+
end
|
|
48
|
+
return current_identity if respond_to?(:current_identity)
|
|
49
|
+
return current_user if respond_to?(:current_user)
|
|
50
|
+
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Default: read `Teams::Current.team` (set by the host's teams
|
|
55
|
+
# before_action), which the boundary cop exempts as a per-request
|
|
56
|
+
# namespace. Hosts that want a different binding (e.g. resolving
|
|
57
|
+
# the team from `params[:team_id]` without delegating to teams)
|
|
58
|
+
# override this method in their ApplicationController.
|
|
59
|
+
#
|
|
60
|
+
# Why we no longer reach into `Teams::Team` here: doing so makes
|
|
61
|
+
# core depend on teams, which inverts the dependency direction
|
|
62
|
+
# (teams may include core's concerns; core must not know about
|
|
63
|
+
# teams' models). Reading `Teams::Current.team` keeps the
|
|
64
|
+
# cross-engine surface to the documented per-request namespace.
|
|
65
|
+
def resolve_current_team
|
|
66
|
+
return nil unless defined?(Teams::Current) && Teams::Current.respond_to?(:team)
|
|
67
|
+
|
|
68
|
+
Teams::Current.team
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Core
|
|
4
|
+
# Records create/update/destroy events for any model that includes
|
|
5
|
+
# Core::Auditable. Schema is intentionally polymorphic so the audit
|
|
6
|
+
# table can serve every engine.
|
|
7
|
+
class AuditLog < ApplicationRecord
|
|
8
|
+
self.table_name = "core_audit_logs"
|
|
9
|
+
|
|
10
|
+
ACTIONS = %w[create update destroy].freeze
|
|
11
|
+
|
|
12
|
+
belongs_to :auditable, polymorphic: true, optional: true
|
|
13
|
+
|
|
14
|
+
validates :action, inclusion: { in: ACTIONS }
|
|
15
|
+
|
|
16
|
+
scope :for, ->(record) { where(auditable_type: record.class.name, auditable_id: record.id) }
|
|
17
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Core
|
|
6
|
+
# Mix into any model to get an audit log entry on every
|
|
7
|
+
# create/update/destroy. Records the actor from Current.user and
|
|
8
|
+
# the changed attributes for updates.
|
|
9
|
+
#
|
|
10
|
+
# class Article < ApplicationRecord
|
|
11
|
+
# include Core::Auditable
|
|
12
|
+
# end
|
|
13
|
+
module Auditable
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
|
|
16
|
+
included do
|
|
17
|
+
has_many :audit_logs, class_name: "Core::AuditLog",
|
|
18
|
+
as: :auditable, dependent: :nullify
|
|
19
|
+
|
|
20
|
+
# after_commit (not after_create / after_update / after_destroy)
|
|
21
|
+
# so the publish doesn't fire inside the host model's open
|
|
22
|
+
# transaction. Subscribers run synchronously; an in-transaction
|
|
23
|
+
# publish lets a slow subscriber hold the table lock.
|
|
24
|
+
after_commit :record_audit_create, on: :create
|
|
25
|
+
after_commit :record_audit_update, on: :update
|
|
26
|
+
after_commit :record_audit_destroy, on: :destroy
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def record_audit_create
|
|
32
|
+
record_audit("create", attributes_snapshot)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def record_audit_update
|
|
36
|
+
changed = saved_changes.transform_values(&:last)
|
|
37
|
+
return if changed.empty?
|
|
38
|
+
|
|
39
|
+
record_audit("update", changed)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def record_audit_destroy
|
|
43
|
+
record_audit("destroy", attributes_snapshot)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def record_audit(action, payload)
|
|
47
|
+
Core::AuditLog.create!(
|
|
48
|
+
action: action,
|
|
49
|
+
auditable_type: self.class.name,
|
|
50
|
+
auditable_id: id,
|
|
51
|
+
actor_id: Core::Current.user&.id,
|
|
52
|
+
payload: payload
|
|
53
|
+
)
|
|
54
|
+
Seams::Events::Publisher.publish(
|
|
55
|
+
"record.audited.core",
|
|
56
|
+
action: action, type: self.class.name, id: id, actor_id: Core::Current.user&.id
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def attributes_snapshot
|
|
61
|
+
attributes.except("created_at", "updated_at")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Core
|
|
6
|
+
# Auto-generates a URL slug from a configured source attribute on
|
|
7
|
+
# create. Appends a numeric suffix on collision (acme, acme-2,
|
|
8
|
+
# acme-3, ...) so users see "creating Acme" succeed even when the
|
|
9
|
+
# name is taken.
|
|
10
|
+
#
|
|
11
|
+
# class Team < ApplicationRecord
|
|
12
|
+
# include Core::Sluggable
|
|
13
|
+
# sluggable_from :name
|
|
14
|
+
# end
|
|
15
|
+
module Sluggable
|
|
16
|
+
extend ActiveSupport::Concern
|
|
17
|
+
|
|
18
|
+
class_methods do
|
|
19
|
+
def sluggable_from(attribute)
|
|
20
|
+
@slug_source = attribute
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def slug_source
|
|
24
|
+
@slug_source || :name
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
included do
|
|
29
|
+
validates :slug, presence: true, uniqueness: true,
|
|
30
|
+
format: { with: /\A[a-z0-9-]+\z/ }
|
|
31
|
+
before_validation :assign_slug, on: :create
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def assign_slug
|
|
37
|
+
return if slug.present?
|
|
38
|
+
|
|
39
|
+
base = slug_base
|
|
40
|
+
candidate = base
|
|
41
|
+
counter = 2
|
|
42
|
+
while self.class.where.not(id: id).exists?(slug: candidate)
|
|
43
|
+
candidate = "#{base}-#{counter}"
|
|
44
|
+
counter += 1
|
|
45
|
+
end
|
|
46
|
+
self.slug = candidate
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def slug_base
|
|
50
|
+
send(self.class.slug_source).to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/(^-|-$)/, "")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Core
|
|
6
|
+
# Tombstone-style soft delete. Sets `deleted_at` instead of actually
|
|
7
|
+
# destroying the row, and adds a default scope that hides deleted
|
|
8
|
+
# records. Use `unscoped` to query them.
|
|
9
|
+
#
|
|
10
|
+
# class Article < ApplicationRecord
|
|
11
|
+
# include Core::SoftDeletable
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Requires a `deleted_at` timestamp column on the model's table.
|
|
15
|
+
module SoftDeletable
|
|
16
|
+
extend ActiveSupport::Concern
|
|
17
|
+
|
|
18
|
+
included do
|
|
19
|
+
default_scope { where(deleted_at: nil) }
|
|
20
|
+
|
|
21
|
+
scope :deleted, -> { unscoped.where.not(deleted_at: nil) }
|
|
22
|
+
scope :with_deleted, -> { unscoped }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def soft_delete!
|
|
26
|
+
update!(deleted_at: Time.current)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def restore!
|
|
30
|
+
update!(deleted_at: nil)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def deleted?
|
|
34
|
+
deleted_at.present?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Core
|
|
6
|
+
# Multi-tenant row scoping. Auto-fills team_id from Core::Current.team
|
|
7
|
+
# on create and adds a default scope that filters to the current
|
|
8
|
+
# team's rows.
|
|
9
|
+
#
|
|
10
|
+
# class Article < ApplicationRecord
|
|
11
|
+
# include Core::TenantScoped
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Requires a `team_id` column on the model's table. Pair with the
|
|
15
|
+
# Teams engine, or any model that exposes Core::Current.team.
|
|
16
|
+
module TenantScoped
|
|
17
|
+
extend ActiveSupport::Concern
|
|
18
|
+
|
|
19
|
+
included do
|
|
20
|
+
belongs_to :team, optional: true
|
|
21
|
+
|
|
22
|
+
default_scope do
|
|
23
|
+
if Core::Current.team
|
|
24
|
+
where(team_id: Core::Current.team.id)
|
|
25
|
+
else
|
|
26
|
+
all
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
before_validation :assign_team, on: :create
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def assign_team
|
|
36
|
+
self.team_id ||= Core::Current.team&.id
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Core
|
|
4
|
+
# Per-request global state. Hosts set Current.user (typically from
|
|
5
|
+
# an Auth before-action) and Current.team (from a tenant-scope
|
|
6
|
+
# before-action) so deeper layers — concerns, jobs invoked from
|
|
7
|
+
# the request, etc. — can read them without threading args through.
|
|
8
|
+
#
|
|
9
|
+
# Backed by ActiveSupport::CurrentAttributes which clears between
|
|
10
|
+
# requests automatically.
|
|
11
|
+
class Current < ActiveSupport::CurrentAttributes
|
|
12
|
+
attribute :user, :team, :request_id
|
|
13
|
+
|
|
14
|
+
resets { Time.zone = nil }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Core
|
|
4
|
+
# Thin convenience wrapper around Seams::Events::Publisher. Adds
|
|
5
|
+
# the request id, actor id, and team id from Core::Current to every
|
|
6
|
+
# payload so subscribers don't have to thread that context through
|
|
7
|
+
# by hand.
|
|
8
|
+
#
|
|
9
|
+
# Core::EventPublisher.publish("subscription.created.billing", id: 42)
|
|
10
|
+
module EventPublisher
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def publish(event_name, payload = {})
|
|
14
|
+
enriched = payload.merge(
|
|
15
|
+
actor_id: Core::Current.user&.id,
|
|
16
|
+
team_id: Core::Current.team&.id,
|
|
17
|
+
request_id: Core::Current.request_id
|
|
18
|
+
).compact
|
|
19
|
+
|
|
20
|
+
Seams::Events::Publisher.publish(event_name, enriched)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Core
|
|
4
|
+
# ActiveModel validator. Use as `validates :email, "core/email_format": true`
|
|
5
|
+
# or via the convenience: `validates :email, email_format: true` (Rails
|
|
6
|
+
# auto-discovers `EmailFormatValidator`).
|
|
7
|
+
#
|
|
8
|
+
# Stricter than URI::MailTo::EMAIL_REGEXP — also rejects consecutive
|
|
9
|
+
# dots, leading/trailing dots in the local part, and trailing dots
|
|
10
|
+
# in the domain.
|
|
11
|
+
class EmailFormatValidator < ActiveModel::EachValidator
|
|
12
|
+
PATTERN = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/
|
|
13
|
+
|
|
14
|
+
def validate_each(record, attribute, value)
|
|
15
|
+
return if value.blank?
|
|
16
|
+
|
|
17
|
+
record.errors.add(attribute, :invalid) unless PATTERN.match?(value.to_s)
|
|
18
|
+
record.errors.add(attribute, :invalid) if value.to_s.include?("..")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates the core_audit_logs table.
|
|
4
|
+
# Why: every engine that mixes Core::Auditable into a model needs
|
|
5
|
+
# somewhere to write audit entries. Single shared table keeps
|
|
6
|
+
# cross-engine queries trivial ("show me everything actor X did
|
|
7
|
+
# last week" doesn't have to UNION across N tables).
|
|
8
|
+
# Risk: append-only writes from after_create/update/destroy hooks.
|
|
9
|
+
# Indexed by auditable + actor for the common queries.
|
|
10
|
+
class CreateCoreAuditLogs < ActiveRecord::Migration[7.1]
|
|
11
|
+
def change
|
|
12
|
+
create_table :core_audit_logs do |t|
|
|
13
|
+
t.string :action, null: false
|
|
14
|
+
t.string :auditable_type
|
|
15
|
+
# String, not bigint — auditable can be a Wave-9 UUID-PK Account
|
|
16
|
+
# or Membership, OR a bigint-PK Identity / Notification / Plan.
|
|
17
|
+
# Polymorphic over both ID-types means the column has to be
|
|
18
|
+
# widest-of (string). Actors stay bigint (Identity is bigint-PK).
|
|
19
|
+
t.string :auditable_id
|
|
20
|
+
t.bigint :actor_id
|
|
21
|
+
t.jsonb :payload, null: false, default: {}
|
|
22
|
+
t.timestamps
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
add_index :core_audit_logs, %i[auditable_type auditable_id]
|
|
26
|
+
add_index :core_audit_logs, :actor_id
|
|
27
|
+
add_index :core_audit_logs, %i[action created_at]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Core
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace Core
|
|
6
|
+
|
|
7
|
+
config.generators do |g|
|
|
8
|
+
g.test_framework :rspec
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
initializer "core.register_events" do
|
|
12
|
+
Seams::EventRegistry.register("record.audited.core", emitted_by: "Core")
|
|
13
|
+
# Follow-up generators that emit new core events (e.g. record.soft_deleted.core) register them here.
|
|
14
|
+
# seams:insertion-point core.engine.events
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
initializer "core.append_migrations" do |app|
|
|
18
|
+
unless app.root == root
|
|
19
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
20
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Follow-up generators that need their own initializer block (audit subscribers, validators) declare them here.
|
|
26
|
+
# seams:insertion-point core.engine.initializers
|
|
27
|
+
end
|
|
28
|
+
end
|