Class: RailsInformant::Integration
- Inherits:
-
Object
- Object
- RailsInformant::Integration
- Defined in:
- lib/rails_informant/integration.rb
Overview
Classifies the host app's committed Claude Code integration by comparing its live .claude/ files against what the installed gem would generate now. The detection primitive behind the three drift channels (boot warning, doctor, hook nudge); Rails-boot-independent and unit-testable in isolation.
Internals may raise (missing gem spec, unreadable templates); each channel wraps a single top-level rescue so a drift check never breaks boot or CI. The one exception is #write_drift_flag, which is best-effort by nature and swallows its own IO failures.
Constant Summary collapse
- Content =
ClaudeIntegrationContent- DRIFT_FLAG =
"rails-informant-drift"- COMPONENTS =
Fixed order so the digest is deterministic across runs.
%w[hook mcp settings skill].freeze
Instance Method Summary collapse
- #drift_flag_path ⇒ Object
-
#gem_version ⇒ Object
Display only — the drift decision uses the digest, not the version.
-
#initialize(app_root: Rails.root) ⇒ Integration
constructor
A new instance of Integration.
- #installed? ⇒ Boolean
- #stale? ⇒ Boolean
-
#status ⇒ Object
:not_installed | :current | :stale | :error.
-
#write_drift_flag(stale:) ⇒ Object
Best-effort: the Ruby channels refresh this flag so the bash hook can read drift without loading Ruby.
Constructor Details
#initialize(app_root: Rails.root) ⇒ Integration
Returns a new instance of Integration.
25 26 27 |
# File 'lib/rails_informant/integration.rb', line 25 def initialize(app_root: Rails.root) @app_root = Pathname(app_root) end |
Instance Method Details
#drift_flag_path ⇒ Object
57 58 59 |
# File 'lib/rails_informant/integration.rb', line 57 def drift_flag_path @app_root.join "tmp", DRIFT_FLAG end |
#gem_version ⇒ Object
Display only — the drift decision uses the digest, not the version. Harmless that local path/git installs report 0.0.0.dev here; only published installs show a real number.
53 54 55 |
# File 'lib/rails_informant/integration.rb', line 53 def gem_version Gem.loaded_specs["rails-informant"]&.version&.to_s end |
#installed? ⇒ Boolean
42 43 44 |
# File 'lib/rails_informant/integration.rb', line 42 def installed? hook_script_present? || settings_informant_present? || mcp_informant_present? end |
#stale? ⇒ Boolean
46 47 48 |
# File 'lib/rails_informant/integration.rb', line 46 def stale? status == :stale end |
#status ⇒ Object
:not_installed | :current | :stale | :error
not_installed wins first so apps that use the gem only for error capture are never nagged. error (a present-but-unparseable settings.json/.mcp.json) is distinct from stale because re-running the generator skips unparseable files — it could never clear a stale reported on one.
35 36 37 38 39 40 |
# File 'lib/rails_informant/integration.rb', line 35 def status return :not_installed unless installed? return :error if json_error? live_digest == expected_digest ? :current : :stale end |
#write_drift_flag(stale:) ⇒ Object
Best-effort: the Ruby channels refresh this flag so the bash hook can read drift without loading Ruby. Never raises out — a read-only tmp/ must not break a dev boot or a doctor run.
64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rails_informant/integration.rb', line 64 def write_drift_flag(stale:) if stale FileUtils.mkdir_p drift_flag_path.dirname drift_flag_path.write "The Claude Code integration is out of date. " \ "Run `bin/rails g rails_informant:skill` to update it.\n" elsif drift_flag_path.exist? drift_flag_path.delete end rescue SystemCallError nil end |