Class: StandardHealth::EnvSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/standard_health/env_spec.rb

Overview

DSL for declaring required and recommended environment variables.

Example:

StandardHealth::EnvSpec.define do
  mode_alias :deployed, %w[staging preview production]
  mode_alias :live, %w[production]

  required :SECRET_KEY_BASE
  required :APP_ENVIRONMENT, in: %w[staging production]
  recommended :SENTRY_DSN, description: "Error tracking DSN"

  group "Singpass / MyInfo" do
    required :MYINFO_CLIENT_ID
    required :MYINFO_PRIVATE_JWKS,
      in: :live,
      unless: -> { ENV["MYINFO_MOCK_MODE"].present? }
  end
end

Each entry has:

- `name` (Symbol)
- `level` (:required | :recommended)
- `in:` (Array<String>, Symbol, or nil) — when an Array, the entry only
  applies while `APP_ENVIRONMENT` matches one of these modes. A Symbol
  is resolved against `mode_alias` declarations at audit time. When
  omitted, the entry applies to every mode.
- `description:` (String, optional) — human-readable hint surfaced
  verbatim by `/diagnostics/env`.
- `consumed_by:` (String or Array<String>, optional) — pointer(s) to
  where the value is read in the host app. Surfaced verbatim.
- `if:` / `unless:` (Proc, optional) — predicates evaluated at audit
  time. When `unless:` returns truthy or `if:` returns falsy the entry
  is reported with `status: :not_applicable`.
- `group` (String, optional) — set implicitly by enclosing `group`
  block; surfaced verbatim.

Defined Under Namespace

Classes: Entry, UnknownModeAlias

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEnvSpec

Returns a new instance of EnvSpec.



70
71
72
73
74
# File 'lib/standard_health/env_spec.rb', line 70

def initialize
  @entries = []
  @mode_aliases = {}
  @group_stack = []
end

Instance Attribute Details

#entriesArray<Entry> (readonly)

Returns:



60
61
62
# File 'lib/standard_health/env_spec.rb', line 60

def entries
  @entries
end

#mode_aliasesHash{Symbol => Array<String>} (readonly)

Returns:

  • (Hash{Symbol => Array<String>})


63
64
65
# File 'lib/standard_health/env_spec.rb', line 63

def mode_aliases
  @mode_aliases
end

Class Method Details

.define(&block) ⇒ Object

Build a spec via the DSL.



66
67
68
# File 'lib/standard_health/env_spec.rb', line 66

def self.define(&block)
  new.tap { |spec| spec.instance_eval(&block) if block }
end

Instance Method Details

#audit(env_hash, mode:) ⇒ Array<Hash>

Run the audit against an env-like hash.

Parameters:

  • env_hash (Hash{String, Symbol => String})

    e.g. ENV.to_h

  • mode (String, Symbol)

    current APP_ENVIRONMENT value

Returns:

  • (Array<Hash>)

    one row per applicable entry. Each row has at least ‘name`, `level`, `status`, `mode`. When an entry is suppressed by an `if:`/`unless:` predicate, `status` is `:not_applicable` and a `reason` field explains why. `description`, `consumed_by`, and `group` are included when set on the entry.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/standard_health/env_spec.rb', line 127

def audit(env_hash, mode:)
  mode_str = mode.to_s
  env = stringify(env_hash)

  @entries.each_with_object([]) do |entry, out|
    next unless mode_applies?(entry, mode_str)

    row = base_row(entry, mode_str)

    suppression = predicate_suppression(entry)
    if suppression
      row[:status] = :not_applicable
      row[:reason] = suppression
    else
      value = env[entry.name.to_s]
      row[:status] = classify(entry, value)
    end

    out << row
  end
end

#group(label, &block) ⇒ Object

Group subsequent declarations under a label. Pure metadata —propagated to audit rows as ‘group:` and otherwise inert. Nested `group` blocks are supported; the innermost label is the one that propagates to enclosed entries.

Parameters:

  • label (String)


91
92
93
94
95
96
97
98
# File 'lib/standard_health/env_spec.rb', line 91

def group(label, &block)
  raise ArgumentError, "group requires a block" unless block

  @group_stack.push(label.to_s)
  block.call
ensure
  @group_stack.pop
end

#mode_alias(name, modes) ⇒ Object

Declare a mode alias usable in ‘in:`. Re-declaring overrides the previous value (last writer wins) so layered specs are easy to compose.

Parameters:

  • name (Symbol)
  • modes (Array<String>)


81
82
83
# File 'lib/standard_health/env_spec.rb', line 81

def mode_alias(name, modes)
  @mode_aliases[name.to_sym] = Array(modes).map(&:to_s)
end

Declare a recommended env var. A missing value never fails the audit; it surfaces as ‘:should_set`.



114
115
116
# File 'lib/standard_health/env_spec.rb', line 114

def recommended(name, **opts)
  add(:recommended, name, **opts)
end

#required(name, **opts) ⇒ Object

Declare a required env var.

Parameters:

  • name (Symbol, String)
  • in (Array<String>, Symbol, nil)

    limit applicability to these modes

  • description (String, nil)
  • consumed_by (String, Array<String>, nil)
  • if (Proc, nil)
  • unless (Proc, nil)


108
109
110
# File 'lib/standard_health/env_spec.rb', line 108

def required(name, **opts)
  add(:required, name, **opts)
end