Module: DocOpsLab::Dev::Library::Cache
- Defined in:
- lib/docopslab/dev/library/cache.rb
Overview
Manages the host-wide library cache at ~/.cache/docopslab/dev/library/.
Cache layout:
current/ Active library used for sync and resolve operations.
previous/ Previous snapshot retained for fast rollback.
The XDG_CACHE_HOME env variable is respected; defaults to ~/.cache.
Constant Summary collapse
- CATALOG_JSON_KEYS =
%w[library_version library_ref generated_at files].freeze
Class Attribute Summary collapse
-
.root_override ⇒ Object
writeonly
Optional path override; set by Library.sync!/fetch! when manifest specifies ‘library.sync.cache_root`.
Class Method Summary collapse
-
.available? ⇒ Boolean
True if a current snapshot with a readable catalog is present.
-
.catalog ⇒ Object
Load and return the parsed catalog from the current snapshot.
-
.catalog_path ⇒ Object
Absolute path to the catalog inside the current snapshot.
-
.current_path ⇒ Object
Absolute path to the current library snapshot.
-
.fresh?(max_age_hours = 24) ⇒ Boolean
True if the cache exists and was generated within
max_age_hours. -
.previous_path ⇒ Object
Absolute path to the previous library snapshot (rollback target).
-
.rollback! ⇒ Object
Swap previous/ back to current/.
-
.root ⇒ Object
Absolute path to the cache root (~/.cache/docopslab/dev/library/).
-
.rotate! ⇒ Object
Rotate current/ to previous/, removing any prior previous/ snapshot.
-
.status ⇒ Object
Return a status hash describing the current snapshot.
-
.stored_head ⇒ Object
The remote HEAD SHA stored from the last successful fetch, or nil.
-
.with_root_override(path) ⇒ Object
Temporarily override the cache root for the duration of a block.
-
.write!(source_dir) ⇒ Object
Install a directory as the new current/ snapshot.
-
.write_head!(sha) ⇒ Object
Persist the remote HEAD SHA alongside the cache.
Class Attribute Details
.root_override=(value) ⇒ Object (writeonly)
Optional path override; set by Library.sync!/fetch! when manifest specifies ‘library.sync.cache_root`. Cleared after the operation.
22 23 24 |
# File 'lib/docopslab/dev/library/cache.rb', line 22 def root_override=(value) @root_override = value end |
Class Method Details
.available? ⇒ Boolean
True if a current snapshot with a readable catalog is present.
67 68 69 |
# File 'lib/docopslab/dev/library/cache.rb', line 67 def available? File.exist?(catalog_path) end |
.catalog ⇒ Object
Load and return the parsed catalog from the current snapshot. Returns nil if no cache is present or the catalog is unreadable.
60 61 62 63 64 |
# File 'lib/docopslab/dev/library/cache.rb', line 60 def catalog return nil unless File.exist?(catalog_path) load_catalog_json(catalog_path) end |
.catalog_path ⇒ Object
Absolute path to the catalog inside the current snapshot.
54 55 56 |
# File 'lib/docopslab/dev/library/cache.rb', line 54 def catalog_path File.join(current_path, 'catalog.json') end |
.current_path ⇒ Object
Absolute path to the current library snapshot.
44 45 46 |
# File 'lib/docopslab/dev/library/cache.rb', line 44 def current_path File.join(root, 'current') end |
.fresh?(max_age_hours = 24) ⇒ Boolean
True if the cache exists and was generated within max_age_hours.
88 89 90 91 92 93 94 95 96 97 |
# File 'lib/docopslab/dev/library/cache.rb', line 88 def fresh? max_age_hours=24 return false unless available? ts = catalog&.dig('generated_at') return false unless ts (Time.now.utc - Time.parse(ts)) < max_age_hours * 3600 rescue ArgumentError false end |
.previous_path ⇒ Object
Absolute path to the previous library snapshot (rollback target).
49 50 51 |
# File 'lib/docopslab/dev/library/cache.rb', line 49 def previous_path File.join(root, 'previous') end |
.rollback! ⇒ Object
Swap previous/ back to current/. Returns false if no previous/ snapshot exists.
123 124 125 126 127 128 129 |
# File 'lib/docopslab/dev/library/cache.rb', line 123 def rollback! return false unless Dir.exist?(previous_path) FileUtils.rm_rf(current_path) FileUtils.mv(previous_path, current_path) true end |
.root ⇒ Object
Absolute path to the cache root (~/.cache/docopslab/dev/library/). Respects root_override if set, then $XDG_CACHE_HOME, then ~/.cache.
26 27 28 29 30 31 |
# File 'lib/docopslab/dev/library/cache.rb', line 26 def root return File.(@root_override) if @root_override base = ENV.fetch('XDG_CACHE_HOME', File.join(Dir.home, '.cache')) File.join(base, Dev.xdg_cache_subpath) end |
.rotate! ⇒ Object
Rotate current/ to previous/, removing any prior previous/ snapshot. Returns true if a rotation was performed, false if current/ was absent.
101 102 103 104 105 106 107 |
# File 'lib/docopslab/dev/library/cache.rb', line 101 def rotate! return false unless Dir.exist?(current_path) FileUtils.rm_rf(previous_path) FileUtils.mv(current_path, previous_path) true end |
.status ⇒ Object
Return a status hash describing the current snapshot.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/docopslab/dev/library/cache.rb', line 132 def status if available? = catalog { available: true, version: &.dig('library_version'), ref: &.dig('library_ref'), generated_at: &.dig('generated_at'), cache_path: current_path, has_previous: Dir.exist?(previous_path) } else { available: false, cache_path: current_path } end end |
.stored_head ⇒ Object
The remote HEAD SHA stored from the last successful fetch, or nil.
72 73 74 75 76 77 78 79 |
# File 'lib/docopslab/dev/library/cache.rb', line 72 def stored_head return nil unless File.exist?(head_path) s = File.read(head_path).strip s.empty? ? nil : s rescue StandardError nil end |
.with_root_override(path) ⇒ Object
Temporarily override the cache root for the duration of a block. Restores the previous value even if the block raises.
35 36 37 38 39 40 41 |
# File 'lib/docopslab/dev/library/cache.rb', line 35 def with_root_override path previous = @root_override @root_override = path yield ensure @root_override = previous end |
.write!(source_dir) ⇒ Object
Install a directory as the new current/ snapshot. Rotates any existing current/ to previous/ first. source_dir must be an existing directory.
112 113 114 115 116 117 118 119 |
# File 'lib/docopslab/dev/library/cache.rb', line 112 def write! source_dir raise ArgumentError, "Source directory not found: #{source_dir}" unless Dir.exist?(source_dir) rotate! if Dir.exist?(current_path) FileUtils.mkdir_p(File.dirname(current_path)) FileUtils.cp_r(source_dir, current_path) true end |
.write_head!(sha) ⇒ Object
Persist the remote HEAD SHA alongside the cache.
82 83 84 85 |
# File 'lib/docopslab/dev/library/cache.rb', line 82 def write_head! sha FileUtils.mkdir_p(root) File.write(head_path, sha.to_s.strip) end |