Class: Capsium::Package::Bundle

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

Overview

Bundled dependencies of an encapsulated (self-contained) package: dependency .cap files embedded under packages/ with a packages/index.json manifest mapping each dependency GUID to its file, version and SHA-256. Bundled dependencies resolve first at load time, before the store -> registry chain.

Defined Under Namespace

Classes: Entry

Constant Summary collapse

DIRECTORY =

Returns:

  • (String)
"packages"
INDEX_FILE =

Returns:

  • (String)
"packages/index.json"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(package_path, entries = nil) ⇒ Bundle

Returns a new instance of Bundle.

Parameters:

  • package_path (String, nil)
  • entries (Hash[String, Entry], nil) (defaults to: nil)


42
43
44
# File 'lib/capsium/package/bundle.rb', line 42

def initialize(package_path, entries = nil)
  @entries = entries || load_entries(package_path)
end

Instance Attribute Details

#entriesHash[String, Entry] (readonly)

Returns the value of attribute entries.

Returns:



40
41
42
# File 'lib/capsium/package/bundle.rb', line 40

def entries
  @entries
end

Class Method Details

.write(package_path, resolutions) ⇒ Hash[String, Hash[String, String]]

Embeds the resolved dependency .cap files (guid => .cap path pairs) into the package directory at package_path and writes the packages/index.json manifest. Returns the manifest hash.

Parameters:

  • package_path (String)
  • resolutions (Array[[String, String]])

Returns:

  • (Hash[String, Hash[String, String]])


50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/capsium/package/bundle.rb', line 50

def self.write(package_path, resolutions)
  FileUtils.mkdir_p(File.join(package_path, DIRECTORY))
  index = resolutions.to_h do |guid, cap_path|
    file = File.join(DIRECTORY, File.basename(cap_path))
    FileUtils.cp(cap_path, File.join(package_path, file))
    [guid, { "file" => file,
             "version" => cap_version(cap_path),
             "sha256" => Digest::SHA256.file(cap_path).hexdigest }]
  end
  File.write(File.join(package_path, INDEX_FILE), JSON.pretty_generate(index))
  index
end

Instance Method Details

#merged_with(other) ⇒ Bundle

Merges an inherited (ancestor) bundle underneath this one; own entries win on GUID conflicts.

Parameters:

Returns:



79
80
81
82
83
84
# File 'lib/capsium/package/bundle.rb', line 79

def merged_with(other)
  return self if other.nil? || !other.present?
  return other unless present?

  self.class.new(nil, other.entries.merge(@entries))
end

#present?Boolean

Whether this bundle embeds any dependency.

Returns:

  • (Boolean)


74
# File 'lib/capsium/package/bundle.rb', line 74

def present? = !@entries.empty?

#resolve_path(guid, range, chain:) ⇒ String?

The absolute .cap path for a bundled dependency, or nil when the GUID is not bundled.

Parameters:

  • guid (String)
  • range (String)
  • chain: (Array[String] chain)

Returns:

  • (String, nil)


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/capsium/package/bundle.rb', line 93

def resolve_path(guid, range, chain:)
  entry = @entries[guid]
  return nil if entry.nil?

  if chain.include?(guid)
    raise CircularDependencyError,
          "circular dependency: #{(chain + [guid]).join(' -> ')}"
  end

  unless VersionRange.parse(range).satisfied_by?(Version.parse(entry.version))
    raise UnsatisfiableDependencyError,
          "bundled #{guid} #{entry.version} does not satisfy '#{range}'"
  end

  verify_sha256(guid, entry)
  entry.file
end