Exception: CLI::Kit::Config::ConfigWriteError

Inherits:
SystemCallError
  • Object
show all
Defined in:
lib/cli/kit/config.rb

Overview

Raised when the config file cannot be written (e.g. read-only directory, read-only filesystem, nix/home-manager-managed symlink pointing into /nix/store).

Inherits from SystemCallError so existing rescue SystemCallError handlers around Config#set continue to match. Diagnostics contain only changed section/key names, never their values. Config contents are not retained on the exception, to keep them out of error reports.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_path, old_config, new_config, cause) ⇒ ConfigWriteError

rubocop:disable Lint/MissingSuper SystemCallError#initialize has a factory-style signature that cannot be invoked from a plain subclass (it expects a matching Errno constant on the class). We bypass it deliberately and initialize via Exception so we just get a message-only exception that rescue SystemCallError still catches via inheritance. super would not work here. : (String config_path, Hash[String, Hash[String, String]] old_config, Hash[String, Hash[String, String]] new_config, SystemCallError cause) -> void



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/cli/kit/config.rb', line 64

def initialize(config_path, old_config, new_config, cause)
  @config_path = config_path
  @diff = build_diff(old_config, new_config)
  @wrapped_errno = cause.errno
  message = "    Could not write to \#{config_path}: \#{cause.message}\n\n    Attempted changes (values and unchanged keys omitted):\n    \#{diff}\n  MSG\n  Exception.instance_method(:initialize).bind(self).call(message)\nend\n".rstrip

Instance Attribute Details

#config_path ⇒ Object (readonly)

: String



51
52
53
# File 'lib/cli/kit/config.rb', line 51

def config_path
  @config_path
end

#diff ⇒ Object (readonly)

: String



54
55
56
# File 'lib/cli/kit/config.rb', line 54

def diff
  @diff
end

Class Method Details

.===(other) ⇒ Object

Ruby's SystemCallError.=== uses errno-based matching, which means rescue ConfigWriteError would otherwise fail to catch a ConfigWriteError instance (it would match Errno::EACCES or Errno::EPERM instead, based on the wrapped errno). Override with plain class-hierarchy matching so rescue ConfigWriteError works as callers expect. rescue SystemCallError still matches via normal inheritance. : (untyped other) -> bool



31
32
33
# File 'lib/cli/kit/config.rb', line 31

def ===(other)
  other.is_a?(self)
end

.new(config_path, old_config, new_config, cause) ⇒ Object

SystemCallError.new has a factory-style signature defined in Sorbet's stdlib RBI (+(String | Integer?) -> SystemCallError+) which doesn't match this subclass's four-argument constructor. Override .new here so type checkers and callers see the real signature, and allocate the instance manually to bypass +SystemCallError+'s factory behaviour. : (String config_path, Hash[String, Hash[String, String]] old_config, Hash[String, Hash[String, String]] new_config, SystemCallError cause) -> ConfigWriteError



43
44
45
46
47
# File 'lib/cli/kit/config.rb', line 43

def new(config_path, old_config, new_config, cause)
  instance = allocate
  instance.__send__(:initialize, config_path, old_config, new_config, cause)
  instance
end

Instance Method Details

#errno ⇒ Object

Preserve the original errno from the wrapped Errno so callers that inspect errno continue to see the real underlying code. : -> Integer?



81
82
83
# File 'lib/cli/kit/config.rb', line 81

def errno
  @wrapped_errno
end