Class: Capsium::Package::Store

Inherits:
Object
  • Object
show all
Defined in:
lib/capsium/package/store.rb,
sig/capsium/package/store.rbs

Overview

A package store directory (ARCHITECTURE.md section 4a): "-.cap" files plus an optional index.json mapping dependency GUID -> file name. Dependencies resolve to the newest version satisfying their semver range.

Defined Under Namespace

Classes: CatalogEntry

Constant Summary collapse

INDEX_FILE =

Returns:

  • (String)
"index.json"
CAP_GLOB =

Returns:

  • (String)
"*.cap"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir) ⇒ Store

Returns a new instance of Store.

Parameters:

  • dir (String)


30
31
32
33
34
35
36
# File 'lib/capsium/package/store.rb', line 30

def initialize(dir)
  unless File.directory?(dir)
    raise DependencyError, "Package store directory not found: #{dir}"
  end

  @dir = dir
end

Instance Attribute Details

#dirString (readonly)

Returns the value of attribute dir.

Returns:

  • (String)


21
22
23
# File 'lib/capsium/package/store.rb', line 21

def dir
  @dir
end

Class Method Details

.defaultStore?

The store configured via the CAPSIUM_STORE environment variable, or nil when unset.

Returns:



25
26
27
28
# File 'lib/capsium/package/store.rb', line 25

def self.default
  dir = ENV.fetch("CAPSIUM_STORE", nil)
  dir.nil? || dir.empty? ? nil : new(dir)
end

Instance Method Details

#catalogArray[CatalogEntry]

Every .cap in the store with its metadata identity (memoized).

Returns:



61
62
63
64
65
# File 'lib/capsium/package/store.rb', line 61

def catalog
  @catalog ||= Dir.glob(File.join(@dir, CAP_GLOB)).map do |path|
    catalog_entry(path)
  end
end

#find(guid, range_string) ⇒ String

The newest .cap providing the dependency GUID whose version satisfies the range string. Raises DependencyNotFoundError or UnsatisfiableDependencyError when it cannot.

Parameters:

  • guid (String)
  • range_string (String)

Returns:

  • (String)


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

def find(guid, range_string)
  range = VersionRange.parse(range_string)
  indexed = indexed_path(guid)
  return indexed_satisfying(indexed, guid, range_string, range) if indexed

  candidates = catalog.select { |entry| entry.guid == guid }
  if candidates.empty?
    raise DependencyNotFoundError,
          "no package for dependency #{guid} in store #{@dir}"
  end

  satisfying = candidates.select { |entry| range.satisfied_by?(entry.version) }
  return satisfying.max_by(&:version).path unless satisfying.empty?

  versions = candidates.map { |entry| entry.version.to_s }.sort.join(", ")
  raise UnsatisfiableDependencyError,
        "no version of #{guid} satisfies '#{range_string}' " \
        "(store has: #{versions})"
end

#install(source_path, guid:, file_name:) ⇒ Object

Installs the .cap at source_path into the store as file_name, records guid -> file_name in index.json (atomically) and returns the installed path.



72
73
74
75
76
77
78
79
# File 'lib/capsium/package/store.rb', line 72

def install(source_path, guid:, file_name:)
  destination = File.join(@dir, file_name)
  tmp = "#{destination}.tmp-#{Process.pid}"
  FileUtils.cp(source_path, tmp)
  File.rename(tmp, destination)
  record_index(guid, file_name)
  destination
end