Module: OpenLoam::Lifecycle
- Defined in:
- lib/open_loam/lifecycle.rb
Overview
Tenant lifecycle: what a brand-new tenant gets for free.
An app declares seeding once, in config/initializers/open_loam.rb:
OpenLoam.on_tenant_created do |tenant|
OpenLoam::FieldDefinition.find_or_create_by!(entity_type: "Equipment", name: "asset_tag") { ... }
end
The block runs inside OpenLoam.as_tenant(tenant), so tenant-scoped writes
need no extra ceremony.
THE CONTRACT: callbacks MUST be idempotent. They fire once when a tenant is
created, and again for EVERY existing tenant whenever bin/rails open_loam:sync
runs — which is how a role/field/default added by a later release reaches
tenants that already exist. Write find_or_create_by!, never create!.
Class Method Summary collapse
-
.broadcast_events ⇒ Object
Event-name patterns (OpenLoam::Events.pattern_matches?) whose events may be pushed to the browser over SSE (OpenLoam::EventStream).
- .broadcast_events=(patterns) ⇒ Object
-
.config_defaults ⇒ Object
App-wide setting defaults:
{ "billing.currency" => "USD" }, declared in the initializer and read by OpenLoam::Configs as the baseline a key resolves to when no global row and no tenant override exist. - .config_defaults=(defaults) ⇒ Object
- .default_locale ⇒ Object
-
.default_roles ⇒ Object
Role names this app expects every tenant to have — declared in the initializer (
OpenLoam.default_roles = %w[manager employee]) and read by whatever seeds memberships. - .default_roles=(roles) ⇒ Object
-
.event_log_retention ⇒ Object
How long a captured event is kept before OpenLoam::EventLogPruneJob deletes it.
- .event_log_retention=(duration) ⇒ Object
-
.feature_defaults ⇒ Object
Known feature flags:
{ "beta_dashboard" => { default: false, description: "..." } }, declared in the initializer and read by OpenLoam::Features. -
.feature_defaults=(defaults) ⇒ Object
Normalizes both levels: outer keys to strings, and each flag's own hash to symbol keys, so
{ "x" => { "default" => true } }and{ x: { default: true } }behave identically. -
.locale ⇒ Object
The current request/job locale — content reads overlay onto it.
- .locale=(code) ⇒ Object
-
.locales ⇒ Object
The locales content translations (OpenLoam::Translatable) may be authored in — declared in the initializer (
OpenLoam.locales = %w[en de pl]), so the admin knows which languages to offer. - .locales=(codes) ⇒ Object
- .on_tenant_created(&block) ⇒ Object
-
.run_tenant_created(tenant) ⇒ Object
The single execution path for a tenant's callbacks — used both by OpenLoam::Tenant's after_create_commit and by sync_tenants!, so "runs inside as_tenant, in declaration order" can never drift between the two.
-
.schedulable_jobs ⇒ Object
Job classes an app EXPLICITLY allows the scheduler to run, beyond the ones it registers via OpenLoam::Scheduler.register.
- .schedulable_jobs=(names) ⇒ Object
-
.sync_tenants! ⇒ Object
Re-runs every on_tenant_created callback for every existing tenant.
-
.tenant_created_callbacks ⇒ Object
Registered blocks, in declaration order.
-
.uncaptured_events ⇒ Object
Event-name patterns EXCLUDED from the event log (OpenLoam::EventLog), which captures everything else.
- .uncaptured_events=(patterns) ⇒ Object
Class Method Details
.broadcast_events ⇒ Object
Event-name patterns (OpenLoam::Events.pattern_matches?) whose events may be pushed to the browser over SSE (OpenLoam::EventStream). DEFAULT OFF — an empty list means nothing reaches a browser; an app opts in explicitly, so a stray event never leaks by default.
82 83 84 |
# File 'lib/open_loam/lifecycle.rb', line 82 def self.broadcast_events @broadcast_events ||= [] end |
.broadcast_events=(patterns) ⇒ Object
86 87 88 |
# File 'lib/open_loam/lifecycle.rb', line 86 def self.broadcast_events=(patterns) @broadcast_events = Array(patterns).map(&:to_s) end |
.config_defaults ⇒ Object
App-wide setting defaults: { "billing.currency" => "USD" }, declared in
the initializer and read by OpenLoam::Configs as the baseline a key resolves
to when no global row and no tenant override exist. A registry, like
default_roles — declaring a default here needs no migration and no row.
70 71 72 |
# File 'lib/open_loam/lifecycle.rb', line 70 def self.config_defaults @config_defaults ||= {} end |
.config_defaults=(defaults) ⇒ Object
74 75 76 |
# File 'lib/open_loam/lifecycle.rb', line 74 def self.config_defaults=(defaults) @config_defaults = defaults.to_h.transform_keys(&:to_s) end |
.default_locale ⇒ Object
135 136 137 |
# File 'lib/open_loam/lifecycle.rb', line 135 def self.default_locale (defined?(I18n) ? I18n.default_locale : :en).to_s end |
.default_roles ⇒ Object
Role names this app expects every tenant to have — declared in the
initializer (OpenLoam.default_roles = %w[manager employee]) and read by
whatever seeds memberships. A registry, not a mechanism: OpenLoam does not
create roles for you, because who gets which role is business logic.
58 59 60 |
# File 'lib/open_loam/lifecycle.rb', line 58 def self.default_roles @default_roles ||= [] end |
.default_roles=(roles) ⇒ Object
62 63 64 |
# File 'lib/open_loam/lifecycle.rb', line 62 def self.default_roles=(roles) @default_roles = Array(roles).map(&:to_s) end |
.event_log_retention ⇒ Object
How long a captured event is kept before OpenLoam::EventLogPruneJob deletes it. nil disables pruning, leaving an unbounded log.
103 104 105 |
# File 'lib/open_loam/lifecycle.rb', line 103 def self.event_log_retention defined?(@event_log_retention) ? @event_log_retention : 90.days end |
.event_log_retention=(duration) ⇒ Object
107 108 109 |
# File 'lib/open_loam/lifecycle.rb', line 107 def self.event_log_retention=(duration) @event_log_retention = duration end |
.feature_defaults ⇒ Object
Known feature flags: { "beta_dashboard" => { default: false, description: "..." } }, declared in the initializer and read by OpenLoam::Features. A
registry like the others — a flag with no row resolves to its declared
default, and the admin can list EVERY known flag, not just toggled ones.
153 154 155 |
# File 'lib/open_loam/lifecycle.rb', line 153 def self.feature_defaults @feature_defaults ||= {} end |
.feature_defaults=(defaults) ⇒ Object
Normalizes both levels: outer keys to strings, and each flag's own hash to
symbol keys, so { "x" => { "default" => true } } and
{ x: { default: true } } behave identically.
160 161 162 163 164 |
# File 'lib/open_loam/lifecycle.rb', line 160 def self.feature_defaults=(defaults) @feature_defaults = defaults.to_h.each_with_object({}) do |(name, spec), out| out[name.to_s] = spec.to_h.transform_keys(&:to_sym) end end |
.locale ⇒ Object
The current request/job locale — content reads overlay onto it. Request state like the tenant (set in a before_action, reset with OpenLoam::Current).
141 142 143 |
# File 'lib/open_loam/lifecycle.rb', line 141 def self.locale (OpenLoam::Current.locale || default_locale).to_s end |
.locale=(code) ⇒ Object
145 146 147 |
# File 'lib/open_loam/lifecycle.rb', line 145 def self.locale=(code) OpenLoam::Current.locale = code&.to_s end |
.locales ⇒ Object
The locales content translations (OpenLoam::Translatable) may be authored in —
declared in the initializer (OpenLoam.locales = %w[en de pl]), so the admin
knows which languages to offer. A registry like the others; defaults to the
single default locale.
115 116 117 |
# File 'lib/open_loam/lifecycle.rb', line 115 def self.locales @locales ||= [ default_locale ] end |
.locales=(codes) ⇒ Object
119 120 121 |
# File 'lib/open_loam/lifecycle.rb', line 119 def self.locales=(codes) @locales = Array(codes).map(&:to_s) end |
.on_tenant_created(&block) ⇒ Object
24 25 26 27 |
# File 'lib/open_loam/lifecycle.rb', line 24 def self.on_tenant_created(&block) tenant_created_callbacks << block block end |
.run_tenant_created(tenant) ⇒ Object
The single execution path for a tenant's callbacks — used both by OpenLoam::Tenant's after_create_commit and by sync_tenants!, so "runs inside as_tenant, in declaration order" can never drift between the two.
Exceptions propagate: a failing callback fails the tenant creation (Rails re-raises from after_commit) or the sync run, loudly, like every other OpenLoam guardrail.
36 37 38 39 40 |
# File 'lib/open_loam/lifecycle.rb', line 36 def self.run_tenant_created(tenant) OpenLoam.as_tenant(tenant) do tenant_created_callbacks.each { |callback| callback.call(tenant) } end end |
.schedulable_jobs ⇒ Object
Job classes an app EXPLICITLY allows the scheduler to run, beyond the ones
it registers via OpenLoam::Scheduler.register. An allowlist (not "any
ActiveJob") so a tenant admin can't schedule ActiveStorage::PurgeJob or a
mailer. Declared in the initializer: OpenLoam.schedulable_jobs = %w[DigestJob].
127 128 129 |
# File 'lib/open_loam/lifecycle.rb', line 127 def self.schedulable_jobs @schedulable_jobs ||= [] end |
.schedulable_jobs=(names) ⇒ Object
131 132 133 |
# File 'lib/open_loam/lifecycle.rb', line 131 def self.schedulable_jobs=(names) @schedulable_jobs = Array(names).map(&:to_s) end |
.sync_tenants! ⇒ Object
Re-runs every on_tenant_created callback for every existing tenant. Idempotent by contract (see above) — safe to run on every deploy. Returns the number of tenants synced.
45 46 47 48 49 50 51 52 |
# File 'lib/open_loam/lifecycle.rb', line 45 def self.sync_tenants! count = 0 OpenLoam::Tenant.find_each do |tenant| run_tenant_created(tenant) count += 1 end count end |
.tenant_created_callbacks ⇒ Object
Registered blocks, in declaration order. Registration returns the block itself so a caller (typically a test) can deregister it again.
20 21 22 |
# File 'lib/open_loam/lifecycle.rb', line 20 def self.tenant_created_callbacks @tenant_created_callbacks ||= [] end |
.uncaptured_events ⇒ Object
Event-name patterns EXCLUDED from the event log (OpenLoam::EventLog), which captures everything else. An empty list captures everything; there is no opt-in spelling. Progress ticks fire once per row during a bulk import.
93 94 95 |
# File 'lib/open_loam/lifecycle.rb', line 93 def self.uncaptured_events @uncaptured_events ||= [ "open_loam.progress." ] end |
.uncaptured_events=(patterns) ⇒ Object
97 98 99 |
# File 'lib/open_loam/lifecycle.rb', line 97 def self.uncaptured_events=(patterns) @uncaptured_events = Array(patterns).map(&:to_s) end |