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,424 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Seams
|
|
6
|
+
module Generators
|
|
7
|
+
# Writes a slim spec/dummy/ Rails app inside a generated engine so
|
|
8
|
+
# the engine's specs can boot Rails and run against a real
|
|
9
|
+
# ActiveRecord database without requiring the host application.
|
|
10
|
+
#
|
|
11
|
+
# The boilerplate (application.rb, boot.rb, environment.rb,
|
|
12
|
+
# database.yml, environments/test.rb, application_record.rb,
|
|
13
|
+
# log/.gitkeep, spec_helper.rb, rails_helper.rb) is emitted by
|
|
14
|
+
# this helper. Engines supply the bits that vary: the schema, the
|
|
15
|
+
# optional host User model, and the routes block.
|
|
16
|
+
#
|
|
17
|
+
# Seams::Generators::DummyAppWriter.write!(
|
|
18
|
+
# engine_path: "engines/auth",
|
|
19
|
+
# engine_module: "Auth",
|
|
20
|
+
# mount_at: "/auth",
|
|
21
|
+
# schema: "<schema body>",
|
|
22
|
+
# host_user: "<class User body>", # optional
|
|
23
|
+
# host_user_path: "app/models/user.rb", # optional, defaults to app/models/user.rb
|
|
24
|
+
# )
|
|
25
|
+
#
|
|
26
|
+
# `host_user_path` lets engines whose dummy "user" model lives at a
|
|
27
|
+
# different autoload path (e.g. `app/models/auth/identity.rb`)
|
|
28
|
+
# write the file where Zeitwerk will find it.
|
|
29
|
+
module DummyAppWriter
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def write!(engine_path:, engine_module:, schema:, mount_at: nil, host_user: nil,
|
|
33
|
+
host_user_path: "app/models/user.rb")
|
|
34
|
+
ensure_directories(engine_path)
|
|
35
|
+
write_dummy_config(engine_path, engine_module, mount_at)
|
|
36
|
+
write_dummy_app(engine_path, host_user, host_user_path)
|
|
37
|
+
write_dummy_db(engine_path, schema)
|
|
38
|
+
write_dummy_meta(engine_path)
|
|
39
|
+
write_spec_helpers(engine_path)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ensure_directories(engine_path)
|
|
43
|
+
%w[
|
|
44
|
+
spec/dummy/config/environments
|
|
45
|
+
spec/dummy/config/initializers
|
|
46
|
+
spec/dummy/db
|
|
47
|
+
spec/dummy/app/models
|
|
48
|
+
spec/dummy/app/controllers
|
|
49
|
+
spec/dummy/app/mailers
|
|
50
|
+
spec/dummy/app/jobs
|
|
51
|
+
spec/dummy/log
|
|
52
|
+
spec/dummy/tmp
|
|
53
|
+
spec/runtime
|
|
54
|
+
].each { |dir| FileUtils.mkdir_p(File.join(engine_path, dir)) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def write_dummy_config(engine_path, engine_module, mount_at)
|
|
58
|
+
write(File.join(engine_path, "spec/dummy/config/boot.rb"), boot_rb)
|
|
59
|
+
write(File.join(engine_path, "spec/dummy/config/application.rb"), application_rb(engine_module))
|
|
60
|
+
write(File.join(engine_path, "spec/dummy/config/environment.rb"), environment_rb)
|
|
61
|
+
write(File.join(engine_path, "spec/dummy/config/database.yml"), database_yml(engine_path))
|
|
62
|
+
write(File.join(engine_path, "spec/dummy/config/environments/test.rb"), test_environment_rb)
|
|
63
|
+
write(File.join(engine_path, "spec/dummy/config/initializers/secret_key.rb"), secret_key_rb)
|
|
64
|
+
write(File.join(engine_path, "spec/dummy/config/routes.rb"), routes_rb(engine_module, mount_at))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def write_dummy_app(engine_path, host_user, host_user_path)
|
|
68
|
+
write(File.join(engine_path, "spec/dummy/app/models/application_record.rb"), application_record_rb)
|
|
69
|
+
write(File.join(engine_path, "spec/dummy/app/controllers/application_controller.rb"), application_controller_rb)
|
|
70
|
+
write(File.join(engine_path, "spec/dummy/app/mailers/application_mailer.rb"), application_mailer_rb)
|
|
71
|
+
write(File.join(engine_path, "spec/dummy/app/jobs/application_job.rb"), application_job_rb)
|
|
72
|
+
return unless host_user
|
|
73
|
+
|
|
74
|
+
full_user_path = File.join(engine_path, "spec/dummy", host_user_path)
|
|
75
|
+
FileUtils.mkdir_p(File.dirname(full_user_path))
|
|
76
|
+
write(full_user_path, host_user)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def write_dummy_db(engine_path, schema)
|
|
80
|
+
write(File.join(engine_path, "spec/dummy/db/schema.rb"), schema_rb(schema))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def write_dummy_meta(engine_path)
|
|
84
|
+
write(File.join(engine_path, "spec/dummy/log/.keep"), "")
|
|
85
|
+
write(File.join(engine_path, "spec/dummy/tmp/.keep"), "")
|
|
86
|
+
write(File.join(engine_path, "spec/dummy/Rakefile"), rakefile_rb)
|
|
87
|
+
write(File.join(engine_path, "spec/dummy/config.ru"), config_ru)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def write_spec_helpers(engine_path)
|
|
91
|
+
write(File.join(engine_path, "spec/spec_helper.rb"), spec_helper_rb(engine_path))
|
|
92
|
+
write(File.join(engine_path, "spec/rails_helper.rb"), rails_helper_rb)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def write(path, content)
|
|
96
|
+
File.write(path, content)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def boot_rb
|
|
100
|
+
<<~RB
|
|
101
|
+
# frozen_string_literal: true
|
|
102
|
+
|
|
103
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
|
|
104
|
+
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
|
|
105
|
+
RB
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def application_rb(engine_module)
|
|
109
|
+
<<~RB
|
|
110
|
+
# frozen_string_literal: true
|
|
111
|
+
|
|
112
|
+
require_relative "boot"
|
|
113
|
+
|
|
114
|
+
require "rails/all"
|
|
115
|
+
|
|
116
|
+
Bundler.require(*Rails.groups)
|
|
117
|
+
|
|
118
|
+
# The engine isn't a published gem; it lives at engines/<name>/.
|
|
119
|
+
# Put its lib/ on the load path before requiring its root file.
|
|
120
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
|
|
121
|
+
require "#{engine_module.downcase}"
|
|
122
|
+
|
|
123
|
+
module Dummy
|
|
124
|
+
class Application < Rails::Application
|
|
125
|
+
# Pin root to the dummy app so Rails doesn't walk up
|
|
126
|
+
# and pick up the host application's Rakefile/config.ru.
|
|
127
|
+
config.root = File.expand_path("..", __dir__)
|
|
128
|
+
|
|
129
|
+
config.load_defaults Rails::VERSION::STRING.to_f
|
|
130
|
+
config.eager_load = false
|
|
131
|
+
config.active_support.deprecation = :stderr
|
|
132
|
+
config.action_controller.include_all_helpers = false
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
RB
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def environment_rb
|
|
139
|
+
<<~RB
|
|
140
|
+
# frozen_string_literal: true
|
|
141
|
+
|
|
142
|
+
require_relative "application"
|
|
143
|
+
Rails.application.initialize!
|
|
144
|
+
RB
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def database_yml(engine_path)
|
|
148
|
+
# Postgres-only — engine schemas use jsonb columns. Per-engine
|
|
149
|
+
# database name keeps parallel test runs from clobbering each
|
|
150
|
+
# other. CI sets PG* env vars; locally they default to the
|
|
151
|
+
# current user with no password (Homebrew Postgres default).
|
|
152
|
+
db_name = "#{File.basename(engine_path)}_dummy_test"
|
|
153
|
+
<<~YML
|
|
154
|
+
test:
|
|
155
|
+
adapter: postgresql
|
|
156
|
+
database: #{db_name}
|
|
157
|
+
host: <%= ENV.fetch("PGHOST", "localhost") %>
|
|
158
|
+
port: <%= ENV.fetch("PGPORT", "5432") %>
|
|
159
|
+
username: <%= ENV.fetch("PGUSER", ENV["USER"]) %>
|
|
160
|
+
password: <%= ENV.fetch("PGPASSWORD", "") %>
|
|
161
|
+
pool: 5
|
|
162
|
+
encoding: unicode
|
|
163
|
+
YML
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_environment_rb
|
|
167
|
+
<<~RB
|
|
168
|
+
# frozen_string_literal: true
|
|
169
|
+
|
|
170
|
+
Rails.application.configure do
|
|
171
|
+
config.cache_classes = true
|
|
172
|
+
config.eager_load = false
|
|
173
|
+
config.public_file_server.enabled = true
|
|
174
|
+
config.consider_all_requests_local = true
|
|
175
|
+
config.action_controller.perform_caching = false
|
|
176
|
+
config.action_dispatch.show_exceptions = :rescuable
|
|
177
|
+
config.action_controller.allow_forgery_protection = false
|
|
178
|
+
config.active_support.deprecation = :stderr
|
|
179
|
+
|
|
180
|
+
# Throwaway keys for the dummy app so models that declare
|
|
181
|
+
# `encrypts` can round-trip in specs. The dummy DB is wiped
|
|
182
|
+
# every run, so deterministic strings are safe here.
|
|
183
|
+
# Hosts use `bin/rails db:encryption:init` + Rails credentials.
|
|
184
|
+
config.active_record.encryption.primary_key = "dummy_primary_key_for_tests_only"
|
|
185
|
+
config.active_record.encryption.deterministic_key = "dummy_deterministic_key_for_tests_only"
|
|
186
|
+
config.active_record.encryption.key_derivation_salt = "dummy_key_derivation_salt_for_tests_only"
|
|
187
|
+
config.active_record.encryption.support_unencrypted_data = true
|
|
188
|
+
|
|
189
|
+
# Mailer specs render views that call URL helpers (e.g.
|
|
190
|
+
# `edit_password_reset_url`). Without a host they raise
|
|
191
|
+
# "Missing host to link to!". `test.host` is the Rails
|
|
192
|
+
# test convention.
|
|
193
|
+
config.action_mailer.delivery_method = :test
|
|
194
|
+
config.action_mailer.default_url_options = { host: "test.host" }
|
|
195
|
+
end
|
|
196
|
+
RB
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def secret_key_rb
|
|
200
|
+
<<~RB
|
|
201
|
+
# frozen_string_literal: true
|
|
202
|
+
|
|
203
|
+
Rails.application.config.secret_key_base = "test_secret_key_base_for_dummy_app"
|
|
204
|
+
RB
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def routes_rb(engine_module, mount_at)
|
|
208
|
+
body = mount_at ? %( mount #{engine_module}::Engine, at: "#{mount_at}") : ""
|
|
209
|
+
<<~RB
|
|
210
|
+
# frozen_string_literal: true
|
|
211
|
+
|
|
212
|
+
Rails.application.routes.draw do
|
|
213
|
+
#{body}
|
|
214
|
+
end
|
|
215
|
+
RB
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def schema_rb(schema_body)
|
|
219
|
+
# Match Rails::VERSION at write-time so schema-format defaults
|
|
220
|
+
# match the host's Rails. A 7.1 schema declared on Rails 8.x
|
|
221
|
+
# still loads, but column-default semantics drift. defined?
|
|
222
|
+
# check guards a Rails-loaded-but-VERSION-not-yet-required boot
|
|
223
|
+
# window the generator specs hit.
|
|
224
|
+
rails_version =
|
|
225
|
+
if defined?(Rails::VERSION::STRING)
|
|
226
|
+
Rails::VERSION::STRING.split(".")[0, 2].join(".")
|
|
227
|
+
else
|
|
228
|
+
"8.1"
|
|
229
|
+
end
|
|
230
|
+
<<~RB
|
|
231
|
+
# frozen_string_literal: true
|
|
232
|
+
|
|
233
|
+
ActiveRecord::Schema[#{rails_version}].define(version: 0) do
|
|
234
|
+
#{schema_body.lines.map { |l| " #{l}" }.join.rstrip}
|
|
235
|
+
end
|
|
236
|
+
RB
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def application_record_rb
|
|
240
|
+
<<~RB
|
|
241
|
+
# frozen_string_literal: true
|
|
242
|
+
|
|
243
|
+
class ApplicationRecord < ActiveRecord::Base
|
|
244
|
+
self.abstract_class = true
|
|
245
|
+
end
|
|
246
|
+
RB
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def application_controller_rb
|
|
250
|
+
# Minimal host ApplicationController so engine controllers that
|
|
251
|
+
# inherit from ::ApplicationController (the seams:engine generator
|
|
252
|
+
# default) can boot inside a request spec. Real hosts will have
|
|
253
|
+
# their own; this is dummy-only.
|
|
254
|
+
<<~RB
|
|
255
|
+
# frozen_string_literal: true
|
|
256
|
+
|
|
257
|
+
class ApplicationController < ActionController::Base
|
|
258
|
+
end
|
|
259
|
+
RB
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def application_mailer_rb
|
|
263
|
+
# Minimal host ApplicationMailer so engine mailers that inherit
|
|
264
|
+
# from ::ApplicationMailer (auth's PasswordsMailer,
|
|
265
|
+
# notifications' ApplicationMailer) can be autoloaded inside
|
|
266
|
+
# the dummy. Real hosts will have their own; this is
|
|
267
|
+
# dummy-only.
|
|
268
|
+
<<~RB
|
|
269
|
+
# frozen_string_literal: true
|
|
270
|
+
|
|
271
|
+
class ApplicationMailer < ActionMailer::Base
|
|
272
|
+
default from: "from@example.com"
|
|
273
|
+
end
|
|
274
|
+
RB
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def application_job_rb
|
|
278
|
+
# Minimal host ApplicationJob so engine jobs that inherit from
|
|
279
|
+
# ::ApplicationJob (notifications' ApplicationJob, billing's
|
|
280
|
+
# ApplicationJob) can be autoloaded inside the dummy. Without
|
|
281
|
+
# this, the engine's spec suite raises NameError the moment a
|
|
282
|
+
# spec touches CreateNotificationJob / StartSubscriptionJob /
|
|
283
|
+
# any other job class.
|
|
284
|
+
<<~RB
|
|
285
|
+
# frozen_string_literal: true
|
|
286
|
+
|
|
287
|
+
class ApplicationJob < ActiveJob::Base
|
|
288
|
+
end
|
|
289
|
+
RB
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def rakefile_rb
|
|
293
|
+
<<~RB
|
|
294
|
+
# frozen_string_literal: true
|
|
295
|
+
|
|
296
|
+
# Marker file so Rails::Engine.find_root anchors here, not
|
|
297
|
+
# in the parent host application.
|
|
298
|
+
require_relative "config/application"
|
|
299
|
+
Rails.application.load_tasks if defined?(Rails.application)
|
|
300
|
+
RB
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def config_ru
|
|
304
|
+
<<~RB
|
|
305
|
+
# frozen_string_literal: true
|
|
306
|
+
|
|
307
|
+
require_relative "config/environment"
|
|
308
|
+
run Rails.application
|
|
309
|
+
RB
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def spec_helper_rb(engine_path)
|
|
313
|
+
engine_name = File.basename(engine_path)
|
|
314
|
+
<<~RB
|
|
315
|
+
# frozen_string_literal: true
|
|
316
|
+
|
|
317
|
+
ENV["RAILS_ENV"] ||= "test"
|
|
318
|
+
|
|
319
|
+
# Rails has to load before the engine's lib/<name>.rb runs,
|
|
320
|
+
# because engine.rb references Rails::Engine. Specs that need
|
|
321
|
+
# ActiveRecord should `require "rails_helper"` instead — that
|
|
322
|
+
# ALSO boots the dummy app, defines the schema, and connects
|
|
323
|
+
# to the test DB.
|
|
324
|
+
require "rails"
|
|
325
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
326
|
+
require "#{engine_name}"
|
|
327
|
+
|
|
328
|
+
RSpec.configure do |config|
|
|
329
|
+
config.expect_with :rspec do |expectations|
|
|
330
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
config.mock_with :rspec do |mocks|
|
|
334
|
+
mocks.verify_partial_doubles = true
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
338
|
+
config.disable_monkey_patching!
|
|
339
|
+
config.order = :random
|
|
340
|
+
Kernel.srand config.seed
|
|
341
|
+
end
|
|
342
|
+
RB
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def rails_helper_rb
|
|
346
|
+
<<~RB
|
|
347
|
+
# frozen_string_literal: true
|
|
348
|
+
|
|
349
|
+
require_relative "spec_helper"
|
|
350
|
+
ENV["RAILS_ENV"] ||= "test"
|
|
351
|
+
|
|
352
|
+
# Ensure the per-engine Postgres test database exists before
|
|
353
|
+
# the dummy app boots and tries to connect to it. We connect
|
|
354
|
+
# to the maintenance "postgres" database first, CREATE DATABASE
|
|
355
|
+
# if missing, then let the dummy app pick up its own config.
|
|
356
|
+
require "active_record"
|
|
357
|
+
require "yaml"
|
|
358
|
+
require "erb"
|
|
359
|
+
dummy_db_yml = File.expand_path("dummy/config/database.yml", __dir__)
|
|
360
|
+
db_config = YAML.safe_load(ERB.new(File.read(dummy_db_yml)).result, aliases: true)["test"]
|
|
361
|
+
target_db = db_config["database"]
|
|
362
|
+
admin_config = db_config.merge("database" => "postgres")
|
|
363
|
+
ActiveRecord::Base.establish_connection(admin_config)
|
|
364
|
+
unless ActiveRecord::Base.connection.execute(
|
|
365
|
+
"SELECT 1 FROM pg_database WHERE datname = '\#{target_db}'"
|
|
366
|
+
).any?
|
|
367
|
+
ActiveRecord::Base.connection.execute(%(CREATE DATABASE "\#{target_db}"))
|
|
368
|
+
end
|
|
369
|
+
ActiveRecord::Base.remove_connection
|
|
370
|
+
|
|
371
|
+
require File.expand_path("dummy/config/environment", __dir__)
|
|
372
|
+
abort("Rails is in production mode!") if Rails.env.production?
|
|
373
|
+
|
|
374
|
+
require "rspec/rails"
|
|
375
|
+
|
|
376
|
+
# WebMock is optional — engines that stub outbound HTTP
|
|
377
|
+
# (billing's stub_stripe helpers, auth's OAuth adapter
|
|
378
|
+
# specs) bring in `webmock` via the host Gemfile. If
|
|
379
|
+
# available, require it so specs can call WebMock.stub_request
|
|
380
|
+
# without each one re-requiring it. We disable real HTTP
|
|
381
|
+
# connections to make missing stubs explicit instead of
|
|
382
|
+
# accidentally hitting the network.
|
|
383
|
+
begin
|
|
384
|
+
require "webmock/rspec"
|
|
385
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
|
386
|
+
rescue LoadError
|
|
387
|
+
# webmock isn't bundled — engines that don't stub HTTP
|
|
388
|
+
# don't need it.
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# FactoryBot is optional — engines that ship factories add
|
|
392
|
+
# `factory_bot_rails` to the host Gemfile. If it's loaded, wire
|
|
393
|
+
# the syntax methods + auto-discover the engine's
|
|
394
|
+
# spec/factories/*.rb (default search paths look in the host's
|
|
395
|
+
# spec/factories which doesn't exist when running engine specs
|
|
396
|
+
# from the host root).
|
|
397
|
+
if defined?(FactoryBot)
|
|
398
|
+
require "factory_bot_rails"
|
|
399
|
+
engine_factories = File.expand_path("factories", __dir__)
|
|
400
|
+
FactoryBot.definition_file_paths = [engine_factories]
|
|
401
|
+
FactoryBot.find_definitions if FactoryBot.factories.none?
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
ActiveRecord::Schema.verbose = false
|
|
405
|
+
# Drop and reload the schema for a clean slate every run.
|
|
406
|
+
ActiveRecord::Base.connection.tables.each do |t|
|
|
407
|
+
ActiveRecord::Base.connection.drop_table(t, force: :cascade)
|
|
408
|
+
end
|
|
409
|
+
load File.expand_path("dummy/db/schema.rb", __dir__)
|
|
410
|
+
|
|
411
|
+
RSpec.configure do |config|
|
|
412
|
+
config.use_transactional_fixtures = true
|
|
413
|
+
config.infer_spec_type_from_file_location!
|
|
414
|
+
config.filter_rails_from_backtrace!
|
|
415
|
+
|
|
416
|
+
if defined?(FactoryBot)
|
|
417
|
+
config.include FactoryBot::Syntax::Methods
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
RB
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Seams
|
|
4
|
+
module Generators
|
|
5
|
+
# Mixin that lets a canonical engine generator (auth, accounts,
|
|
6
|
+
# billing, core, notifications, teams) cooperate with the
|
|
7
|
+
# `bin/seams resolve --eject` CLI introduced in Wave 10 Phase 2B.
|
|
8
|
+
#
|
|
9
|
+
# The contract is one-way: ejection happens in the host's working
|
|
10
|
+
# tree (the CLI prepends a `# seams:ejected from <engine>.<path>`
|
|
11
|
+
# header to a file the host wants to own). This module gives the
|
|
12
|
+
# generator the matching skip behaviour — when the destination
|
|
13
|
+
# file already starts with that header, the generator's
|
|
14
|
+
# `template_unless_ejected` helper logs a yellow "skip" line and
|
|
15
|
+
# leaves the file untouched.
|
|
16
|
+
#
|
|
17
|
+
# ## Eject-eligibility rule
|
|
18
|
+
#
|
|
19
|
+
# The CLI and this helper agree on a single eligibility rule:
|
|
20
|
+
#
|
|
21
|
+
# - Eligible: every templated file under `app/` and `lib/` and
|
|
22
|
+
# `config/` (controllers, models, services, jobs, mailers,
|
|
23
|
+
# views, concerns, lib classes, configuration.rb, routes.rb).
|
|
24
|
+
# These are pieces a host might reasonably want to own outright
|
|
25
|
+
# — to override behaviour, change copy, swap a layout, etc.
|
|
26
|
+
# - Eligible: spec files (spec/) and factories. The host owns
|
|
27
|
+
# the regenerated specs the same way the host owns the engine
|
|
28
|
+
# code; ejecting a spec is rare but legal.
|
|
29
|
+
# - INELIGIBLE: migrations under `db/migrate/`. Migrations run
|
|
30
|
+
# exactly once on a host's timetable; ejecting a migration is
|
|
31
|
+
# meaningless (it's already been run) and re-running the
|
|
32
|
+
# generator never overwrites an existing migration anyway —
|
|
33
|
+
# each run produces a new timestamp.
|
|
34
|
+
# - INELIGIBLE: framework-managed engine boot files —
|
|
35
|
+
# `lib/<engine>/engine.rb`, `lib/<engine>/version.rb`, the
|
|
36
|
+
# engine's Gemfile and gemspec. These are the contract between
|
|
37
|
+
# the engine and the host's Rails boot; if the host wants
|
|
38
|
+
# different behaviour they extend via insertion points or a
|
|
39
|
+
# follow-up generator.
|
|
40
|
+
#
|
|
41
|
+
# The CLI enforces this rule at eject time. The
|
|
42
|
+
# `template_unless_ejected` helper is intentionally permissive —
|
|
43
|
+
# if a host has somehow stamped the eject header onto a
|
|
44
|
+
# framework-managed file, we skip rather than overwrite. The
|
|
45
|
+
# error path is loud at eject, not at generation.
|
|
46
|
+
#
|
|
47
|
+
# ## Usage
|
|
48
|
+
#
|
|
49
|
+
# In an engine generator:
|
|
50
|
+
#
|
|
51
|
+
# class AuthGenerator < Rails::Generators::Base
|
|
52
|
+
# include Seams::Generators::EjectAware
|
|
53
|
+
#
|
|
54
|
+
# def create_models
|
|
55
|
+
# template_unless_ejected "app/models/identity.rb.tt",
|
|
56
|
+
# engine_path("app/models/auth/identity.rb")
|
|
57
|
+
# end
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# `template_unless_ejected` accepts the same positional + keyword
|
|
61
|
+
# arguments as Thor's `template`, including `force: true`, so
|
|
62
|
+
# generators can adopt it incrementally without changing call sites.
|
|
63
|
+
module EjectAware
|
|
64
|
+
EJECT_HEADER_PREFIX = "# seams:ejected from"
|
|
65
|
+
|
|
66
|
+
# Drop-in replacement for Thor's `template`. If the destination
|
|
67
|
+
# exists and starts with the eject header, log a `skip` line and
|
|
68
|
+
# return without writing. Otherwise delegate to `template` —
|
|
69
|
+
# Thor handles `force: true` and conflict resolution as usual.
|
|
70
|
+
def template_unless_ejected(source, *args, **)
|
|
71
|
+
destination = args.first
|
|
72
|
+
if destination && File.exist?(destination) && ejected?(destination)
|
|
73
|
+
say_status :skip, ejected_say_message(destination), :yellow
|
|
74
|
+
return
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
template(source, *args, **)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# True when the file at `path` carries the eject header on its
|
|
81
|
+
# first line. Reads only the first 200 bytes — the header sits
|
|
82
|
+
# at the very top and we don't want to slurp megabytes for files
|
|
83
|
+
# under app/views/.
|
|
84
|
+
def ejected?(path)
|
|
85
|
+
return false unless File.exist?(path)
|
|
86
|
+
|
|
87
|
+
File.read(path, 200).to_s.start_with?(EJECT_HEADER_PREFIX)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def ejected_say_message(destination)
|
|
93
|
+
if respond_to?(:destination_root)
|
|
94
|
+
relative = destination.sub(%r{\A#{Regexp.escape(destination_root.to_s)}/?},
|
|
95
|
+
"")
|
|
96
|
+
end
|
|
97
|
+
relative ||= destination
|
|
98
|
+
"#{relative} (ejected — kept host version)"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "seams/generators/splicer"
|
|
5
|
+
|
|
6
|
+
module Seams
|
|
7
|
+
module Generators
|
|
8
|
+
# Base class for follow-up generators — net-new generators that
|
|
9
|
+
# extend an already-installed engine without re-templating the
|
|
10
|
+
# whole engine. Subclasses look like:
|
|
11
|
+
#
|
|
12
|
+
# module Seams
|
|
13
|
+
# module Generators
|
|
14
|
+
# class AddPasskeysGenerator < FollowUpGenerator
|
|
15
|
+
# engine_name "auth"
|
|
16
|
+
#
|
|
17
|
+
# def add_event
|
|
18
|
+
# splice(
|
|
19
|
+
# file: "lib/auth/engine.rb",
|
|
20
|
+
# marker: "auth.engine.events",
|
|
21
|
+
# content: <<~RUBY
|
|
22
|
+
# Seams::EventRegistry.register("identity.passkey_added.auth", emitted_by: "Auth")
|
|
23
|
+
# RUBY
|
|
24
|
+
# )
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# The base class supplies four primitives that every follow-up
|
|
31
|
+
# generator needs:
|
|
32
|
+
#
|
|
33
|
+
# - +engine_path(relative)+ — resolve a path inside the host's
|
|
34
|
+
# `engines/<this_engine>/` directory.
|
|
35
|
+
# - +splice(file:, marker:, content:, ...)+ — wrap
|
|
36
|
+
# +Splicer.splice_after_marker+ with a concise log line.
|
|
37
|
+
# - +assert_marker_exists!(file:, marker:)+ — fail fast with a
|
|
38
|
+
# clear "this engine wasn't generated" message when a follow-up
|
|
39
|
+
# generator runs against a host that hasn't installed the
|
|
40
|
+
# target engine.
|
|
41
|
+
# - +report_summary+ — template method subclasses override to
|
|
42
|
+
# print a "what just changed" message at the end.
|
|
43
|
+
class FollowUpGenerator < Rails::Generators::Base
|
|
44
|
+
class << self
|
|
45
|
+
# Sets the engine this follow-up generator targets. Used by
|
|
46
|
+
# +engine_path+ to resolve relative paths and by
|
|
47
|
+
# +assert_marker_exists!+ to build the recovery hint.
|
|
48
|
+
def engine_name(name = nil)
|
|
49
|
+
@engine_name = name if name
|
|
50
|
+
@engine_name || raise(ArgumentError,
|
|
51
|
+
"#{self.name} must declare `engine_name \"<engine>\"` " \
|
|
52
|
+
"(e.g. \"auth\") — the base class needs it to resolve " \
|
|
53
|
+
"engines/<engine>/ paths.")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The helpers below are wrapped in `no_tasks { ... }` so Thor
|
|
58
|
+
# treats them as plain instance methods, not generator
|
|
59
|
+
# commands. Without this wrapper, every public method becomes
|
|
60
|
+
# a task and `start` blows up trying to invoke `engine_path`
|
|
61
|
+
# with no arguments — and parent-class tasks invoke BEFORE
|
|
62
|
+
# subclass tasks (Thor's `all_commands` orders parents first),
|
|
63
|
+
# which would run `report_summary` before any of the
|
|
64
|
+
# subclass's actual work.
|
|
65
|
+
# The no_tasks block is naturally long — it groups every helper
|
|
66
|
+
# the FollowUpGenerator surface offers (engine_path, splice,
|
|
67
|
+
# assert_marker_exists!). Splitting them into separate blocks
|
|
68
|
+
# would obscure the "these are the public follow-up generator
|
|
69
|
+
# primitives" grouping. Disable BlockLength for this one block.
|
|
70
|
+
# rubocop:disable Metrics/BlockLength
|
|
71
|
+
no_tasks do
|
|
72
|
+
# Resolve a path inside the host's engines/<this_engine>/.
|
|
73
|
+
# Mirrors the canonical generators' +engine_path+ method but
|
|
74
|
+
# reads the engine name off the class accessor instead of
|
|
75
|
+
# hardcoding it per-generator.
|
|
76
|
+
def engine_path(relative)
|
|
77
|
+
File.join(destination_root, "engines", self.class.engine_name, relative)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Splice `content` after `marker` in `file` (a path relative
|
|
81
|
+
# to the engine root). Logs:
|
|
82
|
+
#
|
|
83
|
+
# splice engines/auth/lib/auth/engine.rb @ auth.engine.events
|
|
84
|
+
#
|
|
85
|
+
# When `before:` is true, splices BEFORE the marker line instead.
|
|
86
|
+
# Returns the underlying +Splicer::Result+ so subclasses can
|
|
87
|
+
# branch on `result.ok?` if they want.
|
|
88
|
+
def splice(file:, marker:, content:, before: false, indent: nil)
|
|
89
|
+
full_path = engine_path(file)
|
|
90
|
+
result =
|
|
91
|
+
if before
|
|
92
|
+
Splicer.splice_before_marker(file_path: full_path, marker: marker, content: content, indent: indent)
|
|
93
|
+
else
|
|
94
|
+
Splicer.splice_after_marker(file_path: full_path, marker: marker, content: content, indent: indent)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if result.ok?
|
|
98
|
+
status = result.lines_added.zero? ? "exists" : "splice"
|
|
99
|
+
say " #{status} engines/#{self.class.engine_name}/#{file} @ #{marker}", :green
|
|
100
|
+
else
|
|
101
|
+
say " error #{result.error}", :red
|
|
102
|
+
end
|
|
103
|
+
result
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Raise a clear, actionable error if the marker isn't present.
|
|
107
|
+
# The most common cause is the host hasn't generated the target
|
|
108
|
+
# engine yet (or generated it from a pre-Wave-10 version of seams
|
|
109
|
+
# that didn't ship insertion points). Both cases get a single
|
|
110
|
+
# recovery hint: re-run the canonical generator.
|
|
111
|
+
def assert_marker_exists!(file:, marker:)
|
|
112
|
+
full_path = engine_path(file)
|
|
113
|
+
return if Splicer.find_marker(file_path: full_path, marker: marker)
|
|
114
|
+
|
|
115
|
+
engine = self.class.engine_name
|
|
116
|
+
raise Seams::GeneratorError, <<~MSG.chomp
|
|
117
|
+
[seams] cannot run #{self.class.name}: insertion-point marker
|
|
118
|
+
"#{marker}"
|
|
119
|
+
was not found in
|
|
120
|
+
engines/#{engine}/#{file}
|
|
121
|
+
|
|
122
|
+
Either this engine wasn't generated, or it was generated by
|
|
123
|
+
a pre-Wave-10 version of seams that didn't ship insertion
|
|
124
|
+
points. Run:
|
|
125
|
+
|
|
126
|
+
bin/rails generate seams:#{engine}
|
|
127
|
+
|
|
128
|
+
to (re)generate the engine with the current marker set, then
|
|
129
|
+
re-run this follow-up generator.
|
|
130
|
+
MSG
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
# rubocop:enable Metrics/BlockLength
|
|
134
|
+
|
|
135
|
+
# Subclass override-point. Subclasses define their own public
|
|
136
|
+
# method named `report_summary` (or any other end-of-run name);
|
|
137
|
+
# Thor invokes it as the last task because public methods on the
|
|
138
|
+
# subclass come after the parent's `no_tasks`-wrapped helpers
|
|
139
|
+
# in declaration order. The base-class default is a no-op,
|
|
140
|
+
# wrapped in `no_tasks` so it doesn't pre-empt subclass output.
|
|
141
|
+
no_tasks do
|
|
142
|
+
def report_summary
|
|
143
|
+
# Override in subclasses.
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|