class Carbon::Concrete::Index

A list of all of the “items” in the global context of Carbon. Items are mostly functions and data definitions. This keeps track of them and their dependencies, so that they can be built later. Indexes are also designed to be easily serialized if needed (which is likely the case, for libraries).

Constants

ITEMS

Used only for {#define}. This maps item “names” to the classes that represent them.

@api private @return [{::Symbol => Item::Base}]

Public Class Methods

new(data = {}) click to toggle source

Initialize the index with the given data.

@api public @param data [{::Symbol => ::Object}] The data to initialize with. @option data [::Hash] :items ({}) The definitions. @option data [::String] :id (id) The ID of the index. This is

completely derived from the index itself; or, rather, what's defined
on the index.  See {#id}.
# File lib/carbon/concrete/index.rb, line 41
def initialize(data = {})
  @items = ::Set.new.merge(data.fetch(:items, []))
  @links = ::Set.new
  @id = data.fetch(:id) { id }
end

Public Instance Methods

<<(item)
Alias for: add
add(item) click to toggle source

Adds a defined item to the index. The item just has to respond to the item API (see {Concrete::Item}). This clears the index's cache, such that the ID ({#id}) and item list ({#items}) are reset and have to be recalculated.

@api public @example Adding an item.

index << item
index.key?(item.intern) # => true

@param item [Concrete::Item::Base] The item to add. @return [self]

# File lib/carbon/concrete/index.rb, line 110
def add(item)
  @items << item
  clear!
end
Also aliased as: <<
build(item) click to toggle source

Yields the builds that need to be built for the given item. This uses the TSort module in order to determine all of the dependencies the given item has, and yields each; and finally, yields the last item. The item may not have any generics, even if they are fully formed generics (e.g. have a definition). If any items have a cyclic depdendency, it fails.

@api semipublic @example Build request.

request.intern # => "Main"
request.generics # => []
index.build(request).to_a # => [...]

@param item [Concrete::Type] The type to build. @return [::Enumerable] The build items, if no block is given. @return [void] If a block is given. @yield [dep] Multiple times, each with a dependency. @yieldparam dep [Request] A request.

# File lib/carbon/concrete/index.rb, line 204
def build(item)
  fail ArgumentError, "Passed item cannot be generic" if \
    item.generics.any?
  return to_enum(:build, item) unless block_given?

  build_from_request(item, &Proc.new)
end
define(data) { |opts| ... } click to toggle source

Defines a type. This is mostly a shorthand method. The purpose of this is to make it easy to define items on the index. The only parameter, data, is a hash. The key is the name of the type of the item; this corresponds to the keys in the {ITEMS} hash. The value is the {Concrete::Type} that is being defined. The method then yields a hash that is later used to define the item.

@api semipublic @example

index.class # => Carbon::Concrete::Index
index.define(internal: Carbon::Boolean) do |bool|
  bool[:size] = 1
  bool[:kind] = :integer
  bool[:implements] << Carbon::Type("Carbon::Numeric")
  bool[:implements] << Carbon::Type("Carbon::Boolean")
end
internal = index[Carbon::Boolean]
internal.name # => "Carbon::Boolean"

@param data [{::Symbol => Concrete::Type}] The name and

item type information.  There should only be one key
pair; any more will cause an error.

@yield [data] To construct the data. At the end of the block, the data

should contain all the information needed to create the item.

@yieldparam data [{::Symbol => ::Object}] The data to be passed to the

item's intializer.

@raise [ArgumentError] if more than one key-value pair is provided for

the `data` argument.

@return [void]

# File lib/carbon/concrete/index.rb, line 177
def define(data)
  fail ArgumentError, "Expected only one pair" if data.size > 1
  data.each do |itype, name|
    item = ITEMS.fetch(itype)
    opts = item.from(Carbon::Type(name))
    yield opts
    self << item.new(opts)
  end
end
fetch(type, default = @canary) { || ... } click to toggle source
# File lib/carbon/concrete/index.rb, line 48
def fetch(type, default = @canary)
  matching = items.map { |i| [i, i.type.match?(type)] }.select(&:last)
  fail TooManyItemsError, "Multiple items match #{type}" if
    matching.size > 1
  match = matching.first

  return match if match
  return yield if block_given?
  return default if default != @canary
  fail ItemNotFoundError, "Could not find an item named #{type}"
end
finalize() click to toggle source

Finalizes the index. This should only be used when all of the items defined on the index are defined. For libraries, this is the culmination of everything in the library.

@note

This freezes the item list for the index.  This prevents modification
for this instance of the index.

@return [self]

# File lib/carbon/concrete/index.rb, line 130
def finalize
  current = @items.dup
  items = ::Set.new(current.map { |item| item.define(self) })
  @items = items.freeze
  clear!
end
id() click to toggle source

The digest of the ID. This uses the SHA256 base58 digest of the items defined on the current index. This includes both defined and merged items.

@api public @example

index.items # => []
index.id # => "gjNT5GbSC-81KmUPncw-hzQAGV3GU-eAXafmkMP-Bw2GMHWM"

@return [::String]

# File lib/carbon/concrete/index.rb, line 92
def id
  @_id ||= begin
    body = (@items.map(&:type).map(&:to_s) + @links.map(&:id)).join("\n")
    Carbon.hash(body)
  end
end
item?(type) click to toggle source
# File lib/carbon/concrete/index.rb, line 60
def item?(type)
  items.any? { |item| item.type.match?(type) }
end
items() click to toggle source
# File lib/carbon/concrete/index.rb, line 64
def items
  @_current ||= begin
    current = @items.dup
    @links.each do |link|
      current.merge(link.items)
    end

    current.freeze
  end
end
to_json() click to toggle source
# File lib/carbon/concrete/index.rb, line 75
def to_json
  links, @links = @links, nil
  clear! && id
  value = Oj.dump(self, Concrete::OPTIONS)
  @links = links
  value
end

Private Instance Methods

build_from_request(request) { |first| ... } click to toggle source
# File lib/carbon/concrete/index.rb, line 219
def build_from_request(request)
  components = to_enum(:each_strongly_connected_component_from, request)
  components.each do |group|
    fail TSort::Cyclic, "cyclic dependencies: #{group.join(', ')}" if \
      group.size > 1
    yield group.first
  end
end
clear!() click to toggle source
# File lib/carbon/concrete/index.rb, line 214
def clear!
  @_id = @_current = nil
  self
end
tsort_each_child(node, &block) click to toggle source

Iterates over all of the “children” of a node. This iterates over all of the corrected dependencies of a node, allowing the TSort module to build a dependency map.

@api private @param node [#intern] The item. @yield [request] Yields each corrected dependency of the item. @yieldparam request [Request] @return [void]

# File lib/carbon/concrete/index.rb, line 237
def tsort_each_child(node, &block)
  fetch(node).first.corrected_dependencies(node, &block)
end