Class: Textus::Store::Sentinel

Inherits:
Object
  • Object
show all
Defined in:
lib/textus/store/sentinel.rb

Overview

Value object for sentinel files written by Infra::Publisher and inspected by Doctor::Check::Sentinels. Owns the JSON shape (target, sha256, mode) and the on-disk path layout (<store_root>/sentinels/ <target-rel-to-repo>.textus-managed.json).

Repo-relative target/source on write so example trees can be committed without leaking the author’s absolute filesystem paths. Legacy absolute paths are still accepted on read.

Constant Summary collapse

SUFFIX =
".textus-managed.json".freeze
DIR =
"sentinels".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target:, source:, sha256:, mode:) ⇒ Sentinel

Returns a new instance of Sentinel.



74
75
76
77
78
79
# File 'lib/textus/store/sentinel.rb', line 74

def initialize(target:, source:, sha256:, mode:)
  @target = target
  @source = source
  @sha256 = sha256
  @mode = mode
end

Instance Attribute Details

#modeObject (readonly)

Returns the value of attribute mode.



19
20
21
# File 'lib/textus/store/sentinel.rb', line 19

def mode
  @mode
end

#sha256Object (readonly)

Returns the value of attribute sha256.



19
20
21
# File 'lib/textus/store/sentinel.rb', line 19

def sha256
  @sha256
end

#sourceObject (readonly)

Returns the value of attribute source.



19
20
21
# File 'lib/textus/store/sentinel.rb', line 19

def source
  @source
end

#targetObject (readonly)

Returns the value of attribute target.



19
20
21
# File 'lib/textus/store/sentinel.rb', line 19

def target
  @target
end

Class Method Details

.absolutize(path, repo_root) ⇒ Object



67
68
69
70
71
72
# File 'lib/textus/store/sentinel.rb', line 67

def self.absolutize(path, repo_root)
  return path if path.nil?
  return path if File.absolute_path?(path)

  File.expand_path(path, repo_root)
end

.legacy_path(target) ⇒ Object



51
52
53
# File 'lib/textus/store/sentinel.rb', line 51

def self.legacy_path(target)
  target + SUFFIX
end

.load(path, repo_root) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/textus/store/sentinel.rb', line 33

def self.load(path, repo_root)
  raw = JSON.parse(File.read(path))
  new(
    target: absolutize(raw["target"], repo_root),
    source: absolutize(raw["source"], repo_root),
    sha256: raw["sha256"],
    mode: raw["mode"],
  )
rescue JSON::ParserError, Errno::ENOENT
  nil
end

.rel_or_abs(path, repo_root) ⇒ Object



55
56
57
# File 'lib/textus/store/sentinel.rb', line 55

def self.rel_or_abs(path, repo_root)
  relative_to(path, repo_root) || File.expand_path(path)
end

.relative_to(path, repo_root) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/textus/store/sentinel.rb', line 59

def self.relative_to(path, repo_root)
  path = File.expand_path(path)
  base = File.expand_path(repo_root)
  return nil unless path.start_with?(base + File::SEPARATOR)

  path[(base.length + 1)..]
end

.sentinel_path(target, store_root) ⇒ Object



45
46
47
48
49
# File 'lib/textus/store/sentinel.rb', line 45

def self.sentinel_path(target, store_root)
  repo_root = File.dirname(store_root)
  rel = relative_to(target, repo_root) || File.basename(target)
  File.join(store_root, DIR, rel + SUFFIX)
end

.write!(target:, source:, store_root:) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/textus/store/sentinel.rb', line 21

def self.write!(target:, source:, store_root:)
  path = sentinel_path(target, store_root)
  FileUtils.mkdir_p(File.dirname(path))
  repo_root = File.dirname(store_root)
  File.write(path, JSON.generate(
                     "source" => rel_or_abs(source, repo_root),
                     "target" => rel_or_abs(target, repo_root),
                     "sha256" => Digest::SHA256.hexdigest(File.binread(target)),
                     "mode" => "copy",
                   ))
end

Instance Method Details

#drift?Boolean

Returns:

  • (Boolean)


85
86
87
88
89
90
# File 'lib/textus/store/sentinel.rb', line 85

def drift?
  return false if orphan?
  return false if @sha256.nil?

  Digest::SHA256.hexdigest(File.binread(@target)) != @sha256
end

#orphan?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/textus/store/sentinel.rb', line 81

def orphan?
  @target.nil? || !File.exist?(@target)
end