Module: Capsium::Package::Composition

Included in:
Capsium::Package
Defined in:
lib/capsium/package/composition.rb,
sig/capsium/package/composition.rbs

Overview

Composite-package support (ARCHITECTURE.md section 4a), mixed into Package: metadata.dependencies resolution against a package store and load-time validation of dependency resource references.

Instance Method Summary collapse

Instance Method Details

#dependency_for(reference) ⇒ ResolvedDependency?

Parameters:

  • reference (String)

Returns:



64
65
66
67
68
# File 'lib/capsium/package/composition.rb', line 64

def dependency_for(reference)
  @resolved_dependencies
    .sort_by { |dependency| -dependency.guid.length }
    .find { |dependency| reference.start_with?("#{dependency.guid}/") }
end

#dependency_viewsArray[[String, MergedView]]

Returns:



70
71
72
73
74
# File 'lib/capsium/package/composition.rb', line 70

def dependency_views
  @resolved_dependencies.map do |dependency|
    [dependency.guid, dependency.package.merged_view(exported_only: true)]
  end
end

#resolve_dependencies(store, chain) ⇒ Array[ResolvedDependency]

Resolves metadata.dependencies against the package store (the store argument, else CAPSIUM_STORE). Each resolved dependency loads recursively with an extended circularity chain; its exported content becomes lower read-only layers of this package's merged view.

Parameters:

  • store (Store, String, nil)
  • chain (Array[String])

Returns:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/capsium/package/composition.rb', line 16

def resolve_dependencies(store, chain)
  declared = @metadata.dependencies
  return [] if declared.empty?

  resolver = DependencyResolver.new(store || Store.default)
  chain += [@metadata.guid]
  declared.map do |guid, range|
    cap_path = resolver.resolve_path(guid, range, chain: chain)
    ResolvedDependency.new(
      guid: guid, range: range, path: cap_path,
      package: Package.new(cap_path, store: resolver.store,
                                     dependency_chain: chain)
    )
  end
end

#validate_dependency_reference(route, dependency, reference) ⇒ void

This method returns an undefined value.

Parameters:

Raises:



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

def validate_dependency_reference(route, dependency, reference)
  inner = reference.delete_prefix("#{dependency.guid}/")
  return if dependency.package.merged_view(exported_only: true).resolve(inner)

  if dependency.package.merged_view.resolve(inner)
    raise DependencyVisibilityError,
          "route #{route.path} references a private resource of " \
          "dependency #{dependency.guid}: #{inner}"
  end
  raise DependencyError,
        "route #{route.path} references a resource missing from " \
        "dependency #{dependency.guid}: #{inner}"
end

#validate_dependency_references!void

This method returns an undefined value.

Every route resource given as a URI ("/") must address a resolved dependency and a resource that dependency exports; anything else is a load-time error (section 4a).



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/capsium/package/composition.rb', line 35

def validate_dependency_references!
  @routes.config.routes.each do |route|
    reference = route.resource
    next unless route.dependency_reference?

    dependency = dependency_for(reference)
    unless dependency
      raise DependencyError,
            "route #{route.path} references unknown dependency: #{reference}"
    end

    validate_dependency_reference(route, dependency, reference)
  end
end