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,5 @@
|
|
|
1
|
+
<%%# Default billing/subscription_started template, used by BillingSubscriber %>
|
|
2
|
+
<%%# when it consumes subscription.created.billing. Override by dropping a %>
|
|
3
|
+
<%%# file at app/views/notifications/templates/billing/subscription_started.erb %>
|
|
4
|
+
<%%# in your host. %>
|
|
5
|
+
Your subscription is active. Welcome aboard.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%%# Default HTML template. notification is the local. Override at %>
|
|
2
|
+
<%%# app/views/notifications/templates/<name>.html.erb in the host. %>
|
|
3
|
+
<p>Hello,</p>
|
|
4
|
+
|
|
5
|
+
<p>You have a notification.</p>
|
|
6
|
+
|
|
7
|
+
<p>—<br>
|
|
8
|
+
This is the default Seams notification template. Override it by creating
|
|
9
|
+
<code>app/views/notifications/templates/<your-template>.html.erb</code>
|
|
10
|
+
in your host app.</p>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%%# Default ERB template. The notification model exposes itself via %>
|
|
2
|
+
<%%# the local variable +notification+. Override by dropping a file %>
|
|
3
|
+
<%%# at app/views/notifications/templates/<name>.erb in your host. %>
|
|
4
|
+
Hello,
|
|
5
|
+
|
|
6
|
+
You have a notification.
|
|
7
|
+
|
|
8
|
+
—
|
|
9
|
+
This is the default Seams notification template. Override it by
|
|
10
|
+
creating app/views/notifications/templates/<your-template>.erb in
|
|
11
|
+
your host app.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%%# Default welcome HTML template, used by AuthSubscriber when it %>
|
|
2
|
+
<%%# consumes identity.signed_up.auth. Override by dropping a file at %>
|
|
3
|
+
<%%# app/views/notifications/templates/welcome.html.erb in your host. %>
|
|
4
|
+
<p>Welcome<%%= ", #{notification.owner.respond_to?(:name) ? notification.owner.name : ''}".rstrip %>!</p>
|
|
5
|
+
|
|
6
|
+
<p>Thanks for signing up. Reply to this email if you need anything.</p>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%%# Default welcome template, used by AuthSubscriber when it consumes %>
|
|
2
|
+
<%%# identity.signed_up.auth. Override by dropping a file at %>
|
|
3
|
+
<%%# app/views/notifications/templates/welcome.erb in your host. %>
|
|
4
|
+
Welcome<%%= ", #{notification.owner.respond_to?(:name) ? notification.owner.name : ''}".rstrip %>!
|
|
5
|
+
|
|
6
|
+
Thanks for signing up. Reply to this email if you need anything.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Configuration for the Notifications engine.
|
|
4
|
+
#
|
|
5
|
+
# Adapters
|
|
6
|
+
# --------
|
|
7
|
+
# Email defaults to Notifications::Adapters::ActionMailer (delegates to
|
|
8
|
+
# the engine's NotificationMailer). SMS defaults to a no-op NullSms
|
|
9
|
+
# adapter that logs and drops. Swap either by assigning a class name
|
|
10
|
+
# (a string — resolved via constantize at delivery time so the host
|
|
11
|
+
# doesn't have to require the adapter at boot):
|
|
12
|
+
#
|
|
13
|
+
# Notifications.configure do |config|
|
|
14
|
+
# config.email_adapter = "MyApp::MailgunAdapter"
|
|
15
|
+
# config.sms_adapter = "MyApp::TwilioAdapter"
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# Optional: Notifiable concern
|
|
19
|
+
# ----------------------------
|
|
20
|
+
# Notifications work without the concern — every Notification has a
|
|
21
|
+
# polymorphic `owner`, and `Notifications::Notification.create!(owner: anything, ...)`
|
|
22
|
+
# is the always-supported path.
|
|
23
|
+
#
|
|
24
|
+
# The concern is OPTIONAL sugar: it adds a `notifications` has_many
|
|
25
|
+
# association and a `#notify(strategy:, template:)` helper to whatever
|
|
26
|
+
# class includes it. Pick one of the patterns below if you want that
|
|
27
|
+
# convenience.
|
|
28
|
+
#
|
|
29
|
+
# Pattern A — wire onto Auth::Identity (canonical post-Wave-9 host):
|
|
30
|
+
#
|
|
31
|
+
# Rails.application.config.to_prepare do
|
|
32
|
+
# Auth::Identity.include(Notifications::Notifiable)
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# Pattern B — wire onto a host User class (hosts that keep their own
|
|
36
|
+
# domain User alongside Auth::Identity):
|
|
37
|
+
#
|
|
38
|
+
# class User < ApplicationRecord
|
|
39
|
+
# include Notifications::Notifiable
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# Pattern C — don't include the concern at all. Use
|
|
43
|
+
# `Notifications::Notification.create!(owner: ..., template: ...)` or
|
|
44
|
+
# the per-strategy classes directly.
|
|
45
|
+
#
|
|
46
|
+
# Default subscriber
|
|
47
|
+
# ------------------
|
|
48
|
+
# Notifications::AuthSubscriber listens for `identity.signed_up.auth`
|
|
49
|
+
# and creates an InApp + Email welcome notification owned by the
|
|
50
|
+
# Auth::Identity. To redirect the welcome notification at a different
|
|
51
|
+
# owner (an Account, a Membership, a host User), copy
|
|
52
|
+
# `engines/notifications/app/subscribers/notifications/auth_subscriber.rb`
|
|
53
|
+
# into your host's app/subscribers/, change `OWNER_CLASS_NAME` and the
|
|
54
|
+
# resolution rule, then re-attach in this initializer:
|
|
55
|
+
#
|
|
56
|
+
# Rails.application.config.to_prepare do
|
|
57
|
+
# Notifications::AuthSubscriber.attach!
|
|
58
|
+
# end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Notifications::Engine.routes.draw do
|
|
4
|
+
resources :notifications, only: %i[index show] do
|
|
5
|
+
member do
|
|
6
|
+
patch :mark_as_read
|
|
7
|
+
end
|
|
8
|
+
collection do
|
|
9
|
+
patch :mark_all_as_read
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
resource :preferences, only: %i[show update]
|
|
14
|
+
|
|
15
|
+
# Follow-up generators that add admin-side notification routes (digest scheduling, channel-specific opt-outs) splice their resources here.
|
|
16
|
+
# seams:insertion-point notifications.routes.after_preferences
|
|
17
|
+
end
|
data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates notification_deliveries — one row per successful send.
|
|
4
|
+
# Why: audit trail for support and ops ("when did we last send X to
|
|
5
|
+
# user Y?") without round-tripping the upstream gateway.
|
|
6
|
+
# Risk: append-only writes from Notification#send!. Indexed by
|
|
7
|
+
# notification_id for join queries from the bell view.
|
|
8
|
+
class CreateNotificationDeliveries < ActiveRecord::Migration[7.1]
|
|
9
|
+
def change
|
|
10
|
+
create_table :notification_deliveries do |t|
|
|
11
|
+
t.references :notification, null: false, foreign_key: true, index: true
|
|
12
|
+
t.datetime :sent_at, null: false
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates notification_preferences for per-Identity opt-outs.
|
|
4
|
+
# Why: hosts need a place for "email me when X" toggles. Channel
|
|
5
|
+
# preferences live with the human (Auth::Identity), not with
|
|
6
|
+
# the polymorphic Notification owner — a host wiring
|
|
7
|
+
# notifications onto an Account or Membership still resolves
|
|
8
|
+
# toggles via the Identity behind the actor. Single row per
|
|
9
|
+
# (identity, channel, notification_type) — absence means
|
|
10
|
+
# use defaults.
|
|
11
|
+
# Risk: tiny table, indexed for fast lookup.
|
|
12
|
+
class CreateNotificationPreferences < ActiveRecord::Migration[7.1]
|
|
13
|
+
def change
|
|
14
|
+
create_table :notification_preferences do |t|
|
|
15
|
+
t.bigint :identity_id, null: false
|
|
16
|
+
t.string :channel, null: false # "in_app" | "email" | "sms"
|
|
17
|
+
t.string :notification_type
|
|
18
|
+
t.boolean :enabled, null: false, default: true
|
|
19
|
+
t.timestamps
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
add_index :notification_preferences, %i[identity_id channel notification_type], unique: true,
|
|
23
|
+
name: "index_notification_prefs_unique"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates the notifications table for the STI Notification model.
|
|
4
|
+
# Why: every channel (in-app, email, SMS) shares the same scheduling +
|
|
5
|
+
# audit semantics. STI keeps that DRY: one table, one indexed
|
|
6
|
+
# next_delivery_at, three subclasses with their own dispatch!.
|
|
7
|
+
# Risk: append + per-row updates (read_at flips, next_delivery_at
|
|
8
|
+
# advances). Indexed by (next_delivery_at) for the due sweeper
|
|
9
|
+
# and (owner) for the bell view.
|
|
10
|
+
class CreateNotifications < ActiveRecord::Migration[7.1]
|
|
11
|
+
def change
|
|
12
|
+
create_table :notifications do |t|
|
|
13
|
+
t.string :type, null: false # STI discriminator
|
|
14
|
+
# Polymorphic owner: can be Auth::Identity (bigint), an
|
|
15
|
+
# Accounts::Account (UUID post-Wave-9), or any other host model.
|
|
16
|
+
# `owner_id` is stored as a string so the column accommodates
|
|
17
|
+
# both PK types without needing two separate FKs. Rails handles
|
|
18
|
+
# the type coercion in the polymorphic association — the lookup
|
|
19
|
+
# uses `WHERE owner_type = ? AND owner_id = ?` and AR casts the
|
|
20
|
+
# parameter to text in the prepared statement.
|
|
21
|
+
t.string :owner_type, null: false
|
|
22
|
+
t.string :owner_id, null: false
|
|
23
|
+
t.string :recipient # destination address (override of owner default)
|
|
24
|
+
t.string :template, null: false
|
|
25
|
+
t.jsonb :schedule_data # IceCube::Schedule#to_hash
|
|
26
|
+
t.datetime :next_delivery_at
|
|
27
|
+
t.datetime :read_at # InApp only
|
|
28
|
+
t.timestamps
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
add_index :notifications, %i[owner_type owner_id]
|
|
32
|
+
add_index :notifications, :next_delivery_at
|
|
33
|
+
add_index :notifications, %i[type next_delivery_at]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Notifications
|
|
4
|
+
module Adapters
|
|
5
|
+
# Contract every notification adapter must implement. Subclass
|
|
6
|
+
# this in your host application to wire Mailgun, SendGrid, Twilio,
|
|
7
|
+
# etc., then point the relevant +Notifications.configuration+
|
|
8
|
+
# knob at the subclass name.
|
|
9
|
+
#
|
|
10
|
+
# Each adapter receives the full Notification object so it can
|
|
11
|
+
# read +recipient+, +template+, +rendered_content+, +owner+ —
|
|
12
|
+
# whatever the gateway needs. Returns a Hash that includes at
|
|
13
|
+
# least { ok: true|false, provider: <string> }.
|
|
14
|
+
class Abstract
|
|
15
|
+
def deliver(notification:)
|
|
16
|
+
raise NotImplementedError, "#{self.class} must implement #deliver"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "notifications/adapters/abstract"
|
|
4
|
+
|
|
5
|
+
module Notifications
|
|
6
|
+
module Adapters
|
|
7
|
+
# Default email adapter — delegates to the engine's
|
|
8
|
+
# NotificationMailer which renders the notification's template
|
|
9
|
+
# and dispatches via ActionMailer.
|
|
10
|
+
class ActionMailer < Abstract
|
|
11
|
+
def deliver(notification:)
|
|
12
|
+
Notifications::NotificationMailer.notify(notification).deliver_later
|
|
13
|
+
{ ok: true, provider: "action_mailer" }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "notifications/adapters/abstract"
|
|
4
|
+
|
|
5
|
+
module Notifications
|
|
6
|
+
module Adapters
|
|
7
|
+
# No-op SMS adapter. Logs the delivery via Seams::Observability
|
|
8
|
+
# and returns success without dispatching. Useful in development
|
|
9
|
+
# and tests so SMS calls succeed without burning vendor quota.
|
|
10
|
+
class NullSms < Abstract
|
|
11
|
+
def deliver(notification:)
|
|
12
|
+
Seams::Observability.adapter.info(
|
|
13
|
+
"notifications.null_sms.deliver",
|
|
14
|
+
engine: "notifications",
|
|
15
|
+
to: notification.recipient,
|
|
16
|
+
template: notification.template,
|
|
17
|
+
length: notification.rendered_content.to_s.length
|
|
18
|
+
)
|
|
19
|
+
{ ok: true, provider: "null_sms" }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Notifications
|
|
6
|
+
# OPTIONAL convenience concern. Adds a +notifications+ has_many
|
|
7
|
+
# association + a +#notify+ helper to whatever model includes it.
|
|
8
|
+
# Notifications themselves are polymorphic — every Notification has
|
|
9
|
+
# an +owner+, which can be ANY ActiveRecord model. You don't need
|
|
10
|
+
# this concern to attach notifications to a model:
|
|
11
|
+
#
|
|
12
|
+
# Notifications::Notification.create!(owner: anything, template: ...)
|
|
13
|
+
#
|
|
14
|
+
# The concern is just sugar for the receiving side.
|
|
15
|
+
#
|
|
16
|
+
# Three include patterns hosts can pick from:
|
|
17
|
+
#
|
|
18
|
+
# 1. Wire into Auth::Identity (most common for canonical seams hosts
|
|
19
|
+
# after Wave 9 — the "human" is Auth::Identity, not a host User):
|
|
20
|
+
#
|
|
21
|
+
# # config/initializers/notifications.rb
|
|
22
|
+
# Rails.application.config.to_prepare do
|
|
23
|
+
# Auth::Identity.include(Notifications::Notifiable)
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# 2. Wire into a host-defined User class (older hosts that still
|
|
27
|
+
# keep their own User model alongside Auth::Identity):
|
|
28
|
+
#
|
|
29
|
+
# class User < ApplicationRecord
|
|
30
|
+
# include Notifications::Notifiable
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# 3. Don't include the concern at all. Create notifications via
|
|
34
|
+
# +Notifications::Notification.create!(owner: ..., template: ...)+
|
|
35
|
+
# or the per-strategy classes directly. The polymorphic +owner+
|
|
36
|
+
# column doesn't care.
|
|
37
|
+
#
|
|
38
|
+
# Once included:
|
|
39
|
+
#
|
|
40
|
+
# target.notify(strategy: :email, template: "welcome")
|
|
41
|
+
# target.notify(strategy: :sms, template: "alert", schedule_config: { starts_at: 1.day.from_now, frequency: "once" })
|
|
42
|
+
# target.notify(strategy: :in_app, template: "system")
|
|
43
|
+
module Notifiable
|
|
44
|
+
extend ActiveSupport::Concern
|
|
45
|
+
|
|
46
|
+
STRATEGY_CLASSES = {
|
|
47
|
+
<%- if channels.include?("email") -%>
|
|
48
|
+
email: "Notifications::Strategies::Email",
|
|
49
|
+
<%- end -%>
|
|
50
|
+
<%- if channels.include?("sms") -%>
|
|
51
|
+
sms: "Notifications::Strategies::Sms",
|
|
52
|
+
<%- end -%>
|
|
53
|
+
<%- if channels.include?("in_app") -%>
|
|
54
|
+
in_app: "Notifications::Strategies::InApp",
|
|
55
|
+
<%- end -%>
|
|
56
|
+
# Follow-up generators that ship new delivery strategies (push, webhook, slack) splice their `key: "Notifications::Strategies::ClassName",` entry here. Trailing commas on every entry keep the hash valid both before and after a splice.
|
|
57
|
+
# seams:insertion-point notifications.notifiable.strategies
|
|
58
|
+
}.freeze
|
|
59
|
+
|
|
60
|
+
included do
|
|
61
|
+
has_many :notifications, class_name: "Notifications::Notification",
|
|
62
|
+
as: :owner, dependent: :destroy
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def notify(strategy:, template:, schedule_config: nil, recipient: nil)
|
|
66
|
+
notif = notifications.build(
|
|
67
|
+
type: STRATEGY_CLASSES.fetch(strategy.to_sym),
|
|
68
|
+
recipient: recipient,
|
|
69
|
+
template: template
|
|
70
|
+
)
|
|
71
|
+
assign_schedule!(notif, schedule_config)
|
|
72
|
+
notif.save!
|
|
73
|
+
notif.send_async if notif.due?
|
|
74
|
+
notif
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Looks up a registered Notifications::TypeRegistry::Type by name
|
|
78
|
+
# and creates one Notification per channel the type supports
|
|
79
|
+
# (subject to the host's NotificationPreference rows). Returns the
|
|
80
|
+
# array of created notifications.
|
|
81
|
+
#
|
|
82
|
+
# Preference lookup keys off `identity_id` — if the including
|
|
83
|
+
# model is an Auth::Identity, that's just `id`. Hosts that include
|
|
84
|
+
# the concern on a non-Identity model (a host User keyed by a
|
|
85
|
+
# different identity_id, an Account, etc.) should override
|
|
86
|
+
# `notification_preference_identity_id` to return the right id.
|
|
87
|
+
def notify_typed(type:, schedule_config: nil, recipient: nil)
|
|
88
|
+
record = Notifications::TypeRegistry.fetch(type)
|
|
89
|
+
preference_id = notification_preference_identity_id
|
|
90
|
+
record.channels.filter_map do |channel|
|
|
91
|
+
next unless STRATEGY_CLASSES.key?(channel.to_sym)
|
|
92
|
+
next unless Notifications::NotificationPreference.enabled?(
|
|
93
|
+
identity_id: preference_id, channel: channel.to_s, notification_type: record.name
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
notify(strategy: channel, template: record.template,
|
|
97
|
+
schedule_config: schedule_config, recipient: recipient)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Override on a non-Identity host class to point preference lookup
|
|
102
|
+
# at the right identity_id. Default: the receiver's own id (works
|
|
103
|
+
# when Notifiable is included on Auth::Identity directly).
|
|
104
|
+
def notification_preference_identity_id
|
|
105
|
+
id
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Override per-host if the receiver exposes its email address under
|
|
109
|
+
# a different name (e.g. a host User that keeps `primary_email_id`).
|
|
110
|
+
# Default falls back to `email_address` (the Rails 8 has_secure_password
|
|
111
|
+
# convention some hosts keep) and then `email` (Auth::Identity + most
|
|
112
|
+
# host User models).
|
|
113
|
+
def email_notification_recipient
|
|
114
|
+
try(:email_address) || try(:email)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def sms_notification_recipient
|
|
118
|
+
try(:phone)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def unread_in_app_notifications
|
|
122
|
+
notifications.where(type: "Notifications::Strategies::InApp").unread
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def assign_schedule!(notif, schedule_config)
|
|
128
|
+
if schedule_config
|
|
129
|
+
notif.schedule_config = schedule_config
|
|
130
|
+
else
|
|
131
|
+
notif.schedule = IceCube::Schedule.new(Time.current)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Notifications
|
|
4
|
+
# Engine-scoped configuration. Override in
|
|
5
|
+
# config/initializers/notifications.rb of the host application:
|
|
6
|
+
#
|
|
7
|
+
# Notifications.configure do |c|
|
|
8
|
+
# c.email_adapter = "Notifications::Adapters::Mailgun"
|
|
9
|
+
# c.sms_adapter = "Notifications::Adapters::Twilio"
|
|
10
|
+
# end
|
|
11
|
+
class Configuration
|
|
12
|
+
attr_accessor :email_adapter, :sms_adapter, :default_from
|
|
13
|
+
# Follow-up generators that add knobs (push adapter, webhook adapter, in-app retention) declare their attr_accessor here.
|
|
14
|
+
# seams:insertion-point notifications.configuration.attributes
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@email_adapter = "Notifications::Adapters::ActionMailer"
|
|
18
|
+
@sms_adapter = "Notifications::Adapters::NullSms"
|
|
19
|
+
@default_from = "no-reply@example.com"
|
|
20
|
+
# Follow-up generators that add defaults for new attributes (matching notifications.configuration.attributes) splice them here.
|
|
21
|
+
# seams:insertion-point notifications.configuration.defaults
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Notifications
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace Notifications
|
|
6
|
+
|
|
7
|
+
config.generators do |g|
|
|
8
|
+
g.test_framework :rspec
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
initializer "notifications.register_events" do
|
|
12
|
+
Seams::EventRegistry.register("notification.queued.notifications", emitted_by: "Notifications")
|
|
13
|
+
Seams::EventRegistry.register("notification.delivered.notifications", emitted_by: "Notifications")
|
|
14
|
+
Seams::EventRegistry.register("notification.failed.notifications", emitted_by: "Notifications")
|
|
15
|
+
# Follow-up generators that ship new delivery semantics (notification.bounced.notifications, etc.) register them here.
|
|
16
|
+
# seams:insertion-point notifications.engine.events
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
initializer "notifications.append_migrations" do |app|
|
|
20
|
+
unless app.root == root
|
|
21
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
22
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
config.after_initialize do
|
|
28
|
+
require "notifications/concerns/notifiable"
|
|
29
|
+
Notifications::AuthSubscriber.attach!
|
|
30
|
+
# Follow-up generators that ship subscribers for new cross-engine events (Notifications::TeamsSubscriber, etc.) splice their attach! calls here.
|
|
31
|
+
# seams:insertion-point notifications.engine.subscribers
|
|
32
|
+
Notifications::BillingSubscriber.attach! if defined?(Billing::Engine)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "notifications/version"
|
|
4
|
+
require "notifications/configuration"
|
|
5
|
+
require "notifications/type_registry"
|
|
6
|
+
require "notifications/engine"
|
|
7
|
+
require "notifications/concerns/notifiable"
|
|
8
|
+
require "notifications/adapters/abstract"
|
|
9
|
+
require "notifications/adapters/action_mailer"
|
|
10
|
+
require "notifications/adapters/null_sms"
|
|
11
|
+
|
|
12
|
+
module Notifications
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
class AdapterError < Error; end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def configuration
|
|
18
|
+
@configuration ||= Configuration.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def configure
|
|
22
|
+
yield configuration
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Seed the TypeRegistry with the default cross-engine notification
|
|
26
|
+
# types other engines emit (auth.signed_up, billing.invoice_paid,
|
|
27
|
+
# etc). Hosts call this from their initializer; subsequent
|
|
28
|
+
# +TypeRegistry.register+ calls override the defaults or extend the
|
|
29
|
+
# set with host-specific types.
|
|
30
|
+
def seed_default_types!
|
|
31
|
+
TypeRegistry.register("welcome",
|
|
32
|
+
template: "welcome",
|
|
33
|
+
channels: %i[in_app email],
|
|
34
|
+
display: "Welcome")
|
|
35
|
+
TypeRegistry.register("billing.invoice_paid",
|
|
36
|
+
template: "billing/invoice_paid",
|
|
37
|
+
channels: %i[in_app email],
|
|
38
|
+
display: "Invoice paid")
|
|
39
|
+
TypeRegistry.register("billing.invoice_failed",
|
|
40
|
+
template: "billing/invoice_failed",
|
|
41
|
+
channels: %i[in_app email],
|
|
42
|
+
display: "Invoice payment failed")
|
|
43
|
+
TypeRegistry.register("billing.subscription_started",
|
|
44
|
+
template: "billing/subscription_started",
|
|
45
|
+
channels: %i[in_app email],
|
|
46
|
+
display: "Subscription started")
|
|
47
|
+
TypeRegistry.register("billing.subscription_canceled",
|
|
48
|
+
template: "billing/subscription_canceled",
|
|
49
|
+
channels: %i[in_app email],
|
|
50
|
+
display: "Subscription canceled")
|
|
51
|
+
TypeRegistry.register("billing.lifetime_purchased",
|
|
52
|
+
template: "billing/lifetime_purchased",
|
|
53
|
+
channels: %i[in_app email],
|
|
54
|
+
display: "Lifetime access purchased")
|
|
55
|
+
# Follow-up generators that ship new cross-engine notification types (teams.invitation_received, auth.password_reset) splice their TypeRegistry.register(...) call here.
|
|
56
|
+
# seams:insertion-point notifications.type_registry.defaults
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def email_adapter
|
|
60
|
+
configuration.email_adapter.constantize.new
|
|
61
|
+
rescue NameError => e
|
|
62
|
+
raise Notifications::AdapterError,
|
|
63
|
+
"Notifications.configuration.email_adapter is set to " \
|
|
64
|
+
"#{configuration.email_adapter.inspect}, which could not be resolved: #{e.message}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def sms_adapter
|
|
68
|
+
configuration.sms_adapter.constantize.new
|
|
69
|
+
rescue NameError => e
|
|
70
|
+
raise Notifications::AdapterError,
|
|
71
|
+
"Notifications.configuration.sms_adapter is set to " \
|
|
72
|
+
"#{configuration.sms_adapter.inspect}, which could not be resolved: #{e.message}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Notifications
|
|
4
|
+
# Registry of typed notification semantics — distinct from the STI
|
|
5
|
+
# strategy classes (InApp / Email / Sms), which are *delivery
|
|
6
|
+
# channels*, not *what the notification is about*.
|
|
7
|
+
#
|
|
8
|
+
# A NotificationType pairs a stable name (e.g. "billing.invoice_paid")
|
|
9
|
+
# with a template path, the channels it can fan out to, and a human
|
|
10
|
+
# display name. Hosts register types up front in an initializer so
|
|
11
|
+
# the host has a closed-set list to power preference UIs and audit
|
|
12
|
+
# queries.
|
|
13
|
+
#
|
|
14
|
+
# # config/initializers/notifications.rb
|
|
15
|
+
# Notifications::TypeRegistry.register("billing.invoice_paid",
|
|
16
|
+
# template: "billing/invoice_paid",
|
|
17
|
+
# channels: %i[in_app email],
|
|
18
|
+
# display: "Invoice paid")
|
|
19
|
+
#
|
|
20
|
+
# user.notify_typed(type: "billing.invoice_paid")
|
|
21
|
+
#
|
|
22
|
+
# The engine seeds a small default set on boot (see
|
|
23
|
+
# +Notifications.seed_default_types!+); hosts override or extend
|
|
24
|
+
# in their initializer.
|
|
25
|
+
module TypeRegistry
|
|
26
|
+
Type = Struct.new(:name, :template, :channels, :display, keyword_init: true) do
|
|
27
|
+
def supports_channel?(channel)
|
|
28
|
+
channels.map(&:to_sym).include?(channel.to_sym)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
name
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
UnknownType = Class.new(StandardError)
|
|
37
|
+
|
|
38
|
+
@types = {}
|
|
39
|
+
@mutex = Mutex.new
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def register(name, template:, channels:, display: nil)
|
|
44
|
+
@mutex.synchronize do
|
|
45
|
+
@types[name.to_s] = Type.new(
|
|
46
|
+
name: name.to_s,
|
|
47
|
+
template: template.to_s,
|
|
48
|
+
channels: Array(channels).map(&:to_sym),
|
|
49
|
+
display: display || name.to_s.tr("._", " ").capitalize
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def find(name)
|
|
55
|
+
@types[name.to_s]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def fetch(name)
|
|
59
|
+
find(name) || raise(UnknownType, "no notification type registered as #{name.inspect}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def all
|
|
63
|
+
@types.values
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def names
|
|
67
|
+
@types.keys
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def reset!
|
|
71
|
+
@mutex.synchronize { @types = {} }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|