Class: Rails::Hyperdrive::LockFile

Inherits:
Object
  • Object
show all
Defined in:
lib/rails/hyperdrive/lock_file.rb

Overview

installed_at is volatile metadata, never an input to any comparison.

Defined Under Namespace

Classes: Entry

Constant Summary collapse

SCHEMA_VERSION =
1
STATE_PRESENT =
"present".freeze
STATE_REMOVED =
"removed-by-user".freeze
DISABLED_KEYS =
{ skill: "skills", guideline: "guidelines" }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ LockFile

Returns a new instance of LockFile.



30
31
32
33
34
35
36
37
# File 'lib/rails/hyperdrive/lock_file.rb', line 30

def initialize(path)
  @path = path.to_s
  @claude_md_state = nil # nil = no lock has been written yet
  @files = {}            # path(String) => Entry
  @document = {}         # raw parsed YAML, kept so unknown keys survive
  @disabled = empty_disabled
  @enabled = []
end

Instance Attribute Details

#claude_md_stateObject

Returns the value of attribute claude_md_state.



23
24
25
# File 'lib/rails/hyperdrive/lock_file.rb', line 23

def claude_md_state
  @claude_md_state
end

#pathObject (readonly)

Returns the value of attribute path.



22
23
24
# File 'lib/rails/hyperdrive/lock_file.rb', line 22

def path
  @path
end

Class Method Details

.load(path) ⇒ Object

Load existing lock state from disk (absent file → empty lock).



26
27
28
# File 'lib/rails/hyperdrive/lock_file.rb', line 26

def self.load(path)
  new(path).tap(&:read)
end

Instance Method Details

#carry(entry) ⇒ Object



101
102
103
104
# File 'lib/rails/hyperdrive/lock_file.rb', line 101

def carry(entry)
  return unless entry && entry.path
  @files[entry.path] = entry
end

#carry_settings(other) ⇒ Object

Adopt the state that is not derived from installed content, so rewriting the file preserves it.



83
84
85
86
87
88
# File 'lib/rails/hyperdrive/lock_file.rb', line 83

def carry_settings(other)
  @document = other.document.dup
  @disabled = other.disabled_lists.dup
  @enabled = other.enabled_gems.dup
  self
end

#disabled?(type, name) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/rails/hyperdrive/lock_file.rb', line 72

def disabled?(type, name)
  Array(@disabled[type.to_sym]).include?(name.to_s)
end

#each_entry(&block) ⇒ Object



68
69
70
# File 'lib/rails/hyperdrive/lock_file.rb', line 68

def each_entry(&block)
  @files.values.each(&block)
end

#enabled_gemsObject

Hand-editable list of gems the app opts into as companions.



77
78
79
# File 'lib/rails/hyperdrive/lock_file.rb', line 77

def enabled_gems
  @enabled
end

#entry(file_path) ⇒ Object



60
61
62
# File 'lib/rails/hyperdrive/lock_file.rb', line 60

def entry(file_path)
  @files[file_path.to_s]
end

#guideline_pathsObject



64
65
66
# File 'lib/rails/hyperdrive/lock_file.rb', line 64

def guideline_paths
  @files.values.select { |e| e.kind == "guideline" }.map(&:path)
end

#readObject



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rails/hyperdrive/lock_file.rb', line 39

def read
  return self unless File.exist?(@path)

  data = YAML.safe_load(File.read(@path))
  return self unless data.is_a?(Hash)

  @document = data
  claude_md = data["claude_md"]
  @claude_md_state = claude_md["state"] if claude_md.is_a?(Hash)
  @disabled = parse_disabled(data["disabled"])
  @enabled = parse_enabled(data["enabled"])
  Array(data["files"]).each do |raw|
    next unless raw.is_a?(Hash)
    entry = build_entry(raw)
    @files[entry.path] = entry if entry.path
  end
  self
rescue Psych::SyntaxError
  self
end

#to_yamlObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rails/hyperdrive/lock_file.rb', line 106

def to_yaml
  carried = @document["claude_md"]
  carried = {} unless carried.is_a?(Hash)

  document = @document.merge(
    "version"   => SCHEMA_VERSION,
    "claude_md" => carried.merge("state" => @claude_md_state),
    "disabled"  => DISABLED_KEYS.each_with_object({}) { |(type, key), h| h[key] = @disabled[type] },
    "enabled"   => @enabled,
    "files"     => @files.values.sort_by(&:path).map { |e| serialize_entry(e) }
  )
  # A nil state means no import line is being managed. Recording one anyway
  # would make the next run read the absent line as a deletion the user
  # made, and never add it back.
  document.delete("claude_md") if @claude_md_state.nil?
  document.to_yaml
end

#upsert(path:, kind:, source_gem:, source_version:, source_sha:, installed_at:) ⇒ Object



90
91
92
93
94
95
96
97
98
99
# File 'lib/rails/hyperdrive/lock_file.rb', line 90

def upsert(path:, kind:, source_gem:, source_version:, source_sha:, installed_at:)
  @files[path.to_s] = Entry.new(
    path: path.to_s,
    kind: kind.to_s,
    source_gem: source_gem.to_s,
    source_version: source_version.to_s,
    source_sha: source_sha.to_s,
    installed_at: installed_at.to_s
  )
end