standard_health 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 298b48147224460e942fb5dd9440596ef128f0967dc7212a507756b9b45ae1b5
4
- data.tar.gz: 5008923ddf09d5637f9e6c07b95bf9073b15d0e79b2d0e00646170478e5d7aee
3
+ metadata.gz: 8542efb5b2511d31b4c9b3ef2a10f650ed342c1eeadc4aecaadef1476b82bc59
4
+ data.tar.gz: 8996c9e8f67904d842f589e60d880127680afa55365cb3be835260db9d36b3c1
5
5
  SHA512:
6
- metadata.gz: 61f79addc2cba3dc84019036dd38b0917b5c812193b6b8af551b4efdd8578f93499a8efaed5f95b38efa07d71bf5d3bfe19495ff7f54eea145895169289aa7e5
7
- data.tar.gz: ff348b35a931bed97044118e15b275ac1daed5fae93929c1f2976bc1db1947dbbeb8a05fe8029bc277311e6174e73e8be94c3afc060c880282a1fa1906406d63
6
+ metadata.gz: b528859586bf219cab6b76902a230f3e660d36e4c108db6489ad1c4643fc19455c868bfa1fbe2b5fdc5291173aa04bfaa516ff322f44cecea41cd3cf5381b474
7
+ data.tar.gz: fe062a3ab494d205e3d436037a4ea42fbe3802043a7d8b3ee873ff4d5b6dd1ae7d1a7bd4659a3e4c533d92cfc035fa47d7c582801a418e4ba24b8943163ae5d5
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-04-29
11
+
12
+ ### Added
13
+
14
+ - `c.diagnostics_parent_controller` config option. When set, only `StandardHealth::DiagnosticsController` inherits from it; `HealthController` still uses `parent_controller`. Lets consuming apps put auth (e.g. HTTP Basic) on the diagnostics endpoint without needing `raise_on_missing_callback_actions = false` — the workaround three rarebit-one web apps applied when adopting v0.1.0.
15
+
16
+ ### Changed
17
+
18
+ - `StandardHealth::DiagnosticsController` now inherits from a new `StandardHealth::DiagnosticsApplicationController` that resolves to `diagnostics_parent_controller || parent_controller`. Backward-compatible — when the new option isn't set, behavior is identical to v0.1.0.
19
+
10
20
  ## [0.1.0] - 2026-04-28
11
21
 
12
22
  ### Added
data/README.md CHANGED
@@ -125,8 +125,49 @@ StandardHealth.configure do |c|
125
125
  end
126
126
  ```
127
127
 
128
+ > **Note (Rails 7.1+):** the `only: :env` filter above raises `AbstractController::ActionNotFound` because `HealthController` (alive/ready) shares this parent and has no `:env` action. Use [Splitting auth between health and diagnostics](#splitting-auth-between-health-and-diagnostics) instead — that's why v0.2.0 added `diagnostics_parent_controller`.
129
+
128
130
  For a more granular setup, mount the engine inside an authenticated route block in your host app's `routes.rb`.
129
131
 
132
+ ### Splitting auth between health and diagnostics
133
+
134
+ The pattern above hits a snag on Rails 7.1+ when you want to protect *only* `/diagnostics/env`. Both `HealthController` and `DiagnosticsController` inherit from `parent_controller`, so a `before_action :authenticate, only: :env` on that single parent applies to both — and Rails raises `AbstractController::ActionNotFound` because `:env` doesn't exist on `HealthController`.
135
+
136
+ Pre-v0.2.0 the workaround was to disable the check on the host controller:
137
+
138
+ ```ruby
139
+ class StandardHealthHostController < ActionController::API
140
+ self.raise_on_missing_callback_actions = false # workaround
141
+ http_basic_authenticate_with(name: ..., password: ..., only: :env)
142
+ end
143
+ ```
144
+
145
+ From v0.2.0 onwards, point `diagnostics_parent_controller` at a separate base class instead. Only `DiagnosticsController` inherits from it, so the `only: :env` callback no longer leaks onto `HealthController`:
146
+
147
+ ```ruby
148
+ # app/controllers/health_base_controller.rb
149
+ class HealthBaseController < ActionController::API
150
+ end
151
+
152
+ # app/controllers/diagnostics_base_controller.rb
153
+ class DiagnosticsBaseController < ActionController::API
154
+ http_basic_authenticate_with(
155
+ name: ENV.fetch("HEALTH_USER"),
156
+ password: ENV.fetch("HEALTH_PASS")
157
+ )
158
+ end
159
+
160
+ # config/initializers/standard_health.rb
161
+ StandardHealth.configure do |c|
162
+ c.parent_controller = "HealthBaseController"
163
+ c.diagnostics_parent_controller = "DiagnosticsBaseController"
164
+ end
165
+ ```
166
+
167
+ Now `/health/alive` and `/health/ready` are unauthenticated (probe-friendly) while `/health/diagnostics/env` requires HTTP Basic — no `raise_on_missing_callback_actions` flag needed.
168
+
169
+ When `diagnostics_parent_controller` is unset, `DiagnosticsController` falls back to `parent_controller`, matching v0.1.0 behavior exactly.
170
+
130
171
  ## Status semantics
131
172
 
132
173
  `/ready` returns:
@@ -3,26 +3,16 @@
3
3
  module StandardHealth
4
4
  # Base controller for all StandardHealth endpoints.
5
5
  #
6
- # Lazily inherits from `StandardHealth.config.parent_controller` so host
6
+ # Lazily inherits from `StandardHealth.config.parent_controller` (resolved
7
+ # by `StandardHealth.parent_controller` in lib/standard_health.rb) so host
7
8
  # apps can wire their own auth/rate-limiting/before_actions in once and
8
- # have them apply to /alive, /ready, and /diagnostics/env. The default
9
- # is `ActionController::API` so the engine works in API-only host apps
9
+ # have them apply to /alive and /ready. The default is
10
+ # `ActionController::API` so the engine works in API-only host apps
10
11
  # without configuration.
11
12
  #
12
- # Resolution happens at the moment this class is first referenced, which
13
- # is request time by then the host app's controllers are loaded and
14
- # the constant resolves cleanly. Caching the resolved class on the
15
- # singleton avoids re-running `constantize` per request.
16
- def self.parent_controller
17
- @parent_controller ||= config.parent_controller.constantize
18
- end
19
-
20
- # Allow tests/host apps to bust the cached parent controller (e.g. when
21
- # toggling config between examples).
22
- def self.reset_parent_controller!
23
- @parent_controller = nil
24
- end
25
-
13
+ # `DiagnosticsController` has its own base class
14
+ # (`DiagnosticsApplicationController`) with its own resolver see
15
+ # `config.diagnostics_parent_controller`.
26
16
  class ApplicationController < parent_controller
27
17
  end
28
18
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StandardHealth
4
+ # Separate base class for `DiagnosticsController` so host apps can target
5
+ # diagnostics-only auth at this class via `diagnostics_parent_controller`.
6
+ # When `diagnostics_parent_controller` is unset, this resolves to the same
7
+ # class as `parent_controller`, so the inheritance chain is behaviorally
8
+ # identical to v0.1.0.
9
+ class DiagnosticsApplicationController < diagnostics_parent_controller
10
+ end
11
+ end
@@ -3,9 +3,15 @@
3
3
  module StandardHealth
4
4
  # Diagnostics endpoints. Output here is potentially sensitive (it
5
5
  # enumerates which env vars are missing), so host apps are responsible
6
- # for wrapping these routes with authentication via `parent_controller`
7
- # — typically a basic-auth `before_action` on `ApplicationController`.
8
- class DiagnosticsController < ApplicationController
6
+ # for wrapping these routes with authentication.
7
+ #
8
+ # Inherits from `DiagnosticsApplicationController`, which resolves to
9
+ # `config.diagnostics_parent_controller || config.parent_controller`.
10
+ # This lets host apps put auth on diagnostics only — e.g. an HTTP Basic
11
+ # `before_action :auth, only: :env` on a dedicated diagnostics parent —
12
+ # without that callback leaking onto `HealthController` and tripping
13
+ # Rails 7.1's `raise_on_missing_callback_actions`.
14
+ class DiagnosticsController < DiagnosticsApplicationController
9
15
  # Audits the configured EnvSpec against the current process ENV and
10
16
  # returns the result as JSON. When no EnvSpec is configured the
11
17
  # endpoint returns an empty audit rather than a 404 so callers don't
@@ -25,6 +25,18 @@ module StandardHealth
25
25
  # engine works in API-only host apps without configuration.
26
26
  attr_accessor :parent_controller
27
27
 
28
+ # Optional class name of a controller that ONLY `DiagnosticsController`
29
+ # should inherit from. When set, `HealthController` continues to use
30
+ # `parent_controller` while `DiagnosticsController` uses this one. Lets
31
+ # host apps put auth (e.g. HTTP Basic) on the diagnostics endpoint
32
+ # without needing to set `raise_on_missing_callback_actions = false`
33
+ # to suppress Rails 7.1's missing-action error caused by a single
34
+ # parent declaring `before_action :auth, only: :env` for both controllers.
35
+ #
36
+ # When unset (the default), `DiagnosticsController` falls back to
37
+ # `parent_controller` — fully backward-compatible with v0.1.0.
38
+ attr_accessor :diagnostics_parent_controller
39
+
28
40
  # An optional `StandardHealth::EnvSpec` instance describing required and
29
41
  # recommended environment variables for the host app. Audited via the
30
42
  # /diagnostics/env endpoint.
@@ -32,6 +44,7 @@ module StandardHealth
32
44
 
33
45
  def initialize
34
46
  @parent_controller = "ActionController::API"
47
+ @diagnostics_parent_controller = nil
35
48
  @env_spec = nil
36
49
  @checks = []
37
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StandardHealth
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -32,5 +32,41 @@ module StandardHealth
32
32
  def reset_config!
33
33
  @config = Configuration.new
34
34
  end
35
+
36
+ # Resolves `config.parent_controller` to an actual class lazily — at
37
+ # the moment the engine's `ApplicationController` is first referenced
38
+ # (request time), by which point the host app's controllers are
39
+ # loaded. Caching avoids re-running `constantize` per request, and
40
+ # the name-mismatch reset lets tests swap config between examples.
41
+ def parent_controller
42
+ expected = config.parent_controller
43
+ if @parent_controller && @parent_controller.name != expected
44
+ @parent_controller = nil
45
+ end
46
+ @parent_controller ||= expected.constantize
47
+ end
48
+
49
+ def reset_parent_controller!
50
+ @parent_controller = nil
51
+ end
52
+
53
+ # Resolves the parent class for `DiagnosticsController` only. Falls
54
+ # back to `config.parent_controller` when no diagnostics-specific
55
+ # parent is configured, preserving v0.1.0 behavior. When set, lets
56
+ # host apps apply `before_action :auth, only: :env` to the diagnostics
57
+ # endpoint without that callback leaking onto `HealthController`
58
+ # (which would otherwise trip Rails 7.1's
59
+ # `raise_on_missing_callback_actions`).
60
+ def diagnostics_parent_controller
61
+ expected = config.diagnostics_parent_controller || config.parent_controller
62
+ if @diagnostics_parent_controller && @diagnostics_parent_controller.name != expected
63
+ @diagnostics_parent_controller = nil
64
+ end
65
+ @diagnostics_parent_controller ||= expected.constantize
66
+ end
67
+
68
+ def reset_diagnostics_parent_controller!
69
+ @diagnostics_parent_controller = nil
70
+ end
35
71
  end
36
72
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_health
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaryl Sim
@@ -37,6 +37,7 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - app/controllers/standard_health/application_controller.rb
40
+ - app/controllers/standard_health/diagnostics_application_controller.rb
40
41
  - app/controllers/standard_health/diagnostics_controller.rb
41
42
  - app/controllers/standard_health/health_controller.rb
42
43
  - config/routes.rb