class ActiveFacts::Compositions::Compositor

Attributes

composition[R]
name[R]
options[R]

Public Class Methods

new(constellation, name, options = {}) click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 28
def initialize constellation, name, options = {}, compositor_name
  @constellation = constellation
  @name = name
  @compositor_name = compositor_name
  @options = options
  # @option_source = options.delete('source')
  # @option_target = options.delete('target')
  # @option_transform = options.delete('transform')
end
options() click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 23
def self.options
  {
  }
end

Public Instance Methods

generate() click to toggle source

Generate all Mappings into @binary_mappings for a binary composition of all ObjectTypes in this constellation

# File lib/activefacts/compositions/compositor.rb, line 39
def generate
  # Retract an existing composition by this name
  if existing = @constellation.Name[[@name]] and
      @composition = existing.composition
    @composition.all_composite.to_a.each{|composite| composite.retract}
    @composition.retract
  end

  @composition = @constellation.Composition(:new, :name => @name, :compositor_name => @compositor_name)
  preload_preferred_identifiers
  populate_references
end
populate_reference(role) click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 52
def populate_reference role
  component = (@reference_populated ||= {})[role]
  return component if component
  @reference_populated[role] = true

  parent = @binary_mappings[role.object_type]
  return nil if role.fact_type.all_role.size > 2
  if role.fact_type.all_role.size != 1
    counterpart = role.counterpart
    rt = role_type(counterpart)
    if rt == :many_many
      raise "Fact type must be objectified: #{role.fact_type.default_reading}"
    end

    component = @constellation.Absorption(
        :new,
        name: role_name(counterpart),
        parent: parent,
        object_type: counterpart.object_type,
        parent_role: role,
        child_role: counterpart
      )
    # Populate the absorption/reverse_mapping (putting the "many" or optional side as reverse)
    if r = @component_by_fact[role.fact_type]
      # Second occurrence of this fact type, set the direction:
      if component.is_preferred_direction
        component.reverse_mapping = r
      else  # Set this as the reverse absorption
        component.forward_mapping = r
      end
    else
      # First occurrence of this fact type
      @component_by_fact[role.fact_type] = component
    end
  else    # It's an indicator
    component = @constellation.Indicator(
        :new,
        name: role.name,
        parent: parent,
        role: role
      )
    @component_by_fact[role.fact_type] = component  # For completeness, in case a subclass uses it
  end
  @reference_populated[role] = component
  trace :binarize, "Populated #{component.inspect}"
  component
end
show_references() click to toggle source

Display the primitive binary mapping:

# File lib/activefacts/compositions/compositor.rb, line 208
def show_references
  trace :composition, "Displaying the mappings:" do
    @binary_mappings.keys.sort_by(&:name).each do |object_type|
      mapping = @binary_mappings[object_type]
      trace :composition, "#{object_type.name}" do
        mapping.all_member.each do |component|
          trace :composition, component.inspect
        end
      end
    end
  end
end

Private Instance Methods

populate_references() click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 108
def populate_references
  # A table of Mappings by object type, with a default Mapping for each:
  @binary_mappings = Hash.new do |h, object_type|
    h[object_type] = @constellation.Mapping(
        :new,
        name: object_type.name,
        object_type: object_type
      )
  end
  @component_by_fact = {}

  trace :binarize, "Build binary bindings for full schema" do
    @constellation.ObjectType.each do |key, object_type|
      @binary_mappings[object_type]  # Ensure we create the top Mapping even if it has no references
    end
  end

  @binary_mappings.each do |object_type, mapping|
    trace :binarize, "Populating possible absorptions for #{object_type.name}" do

      object_type.all_role.each do |role|
        # Exclude base roles in objectified fact types (unless unary); just use link fact types
        next if role.fact_type.entity_type && role.fact_type.all_role.size != 1
        next if role.variable   # REVISIT: Continue to ignore roles in derived fact types?
        populate_reference role
      end
      if object_type.is_a?(ActiveFacts::Metamodel::ValueType)
        # This requires a change in the metamodel to use TypeInheritance for ValueTypes
        if object_type.supertype
          trace :binarize, "REVISIT: Eliding supertype #{object_type.supertype.name} for #{object_type.name}"
        end
        object_type.all_value_type_as_supertype.each do |subtype|
          trace :binarize, "REVISIT: Eliding subtype #{subtype.name} for #{object_type.name}"
        end
      end
    end
  end
end
preload_preferred_identifiers() click to toggle source

Preferred identifiers are cached, but the process produces trace output that appears in the “tutti” mode used in testing. This precludes that.

# File lib/activefacts/compositions/compositor.rb, line 104
def preload_preferred_identifiers
  @constellation.EntityType.map{|k, et| et.preferred_identifier }
end
role_name(role) click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 147
      def role_name role
#       if role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) && role == role.fact_type.subtype_role
#         return "Is "+role.object_type.name
#       end
        role = role.base_role unless role.base_role.fact_type.all_role.size == 1
        String::Words.new(role.preferred_reference.role_name(nil)).capwords*' '
      end
role_type(role) click to toggle source
# File lib/activefacts/compositions/compositor.rb, line 155
def role_type role
  fact_type = role.fact_type
  if fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
    return role.object_type == fact_type.supertype ? :supertype : :subtype
  end

  return :unary if fact_type.all_role.size == 1

  if fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
    # Prevent an unnecessary from-1 search:
    from_1 = true
    # Change the to_1 search to detect a one-to-one:
    role = fact_type.implying_role
    fact_type = role.fact_type
  end

  # List the UCs on this fact type:
  all_uniqueness_constraints =
    fact_type.all_role.map do |fact_role|
      fact_role.all_role_ref.map do |rr|
        rr.role_sequence.all_presence_constraint.select do |pc|
          pc.max_frequency == 1
        end
      end
    end.flatten.uniq

  # It's to-1 if a UC exists over exactly this role:
  to_1 =
    all_uniqueness_constraints.
      detect do |c|
          (rr = c.role_sequence.all_role_ref.single) and
          rr.role == role
      end

  if from_1 || fact_type.entity_type
    # This is a role in an objectified fact type
    from_1 = true
  else
    # It's from-1 if a UC exists over roles of this FT that doesn't cover this role:
    from_1 = all_uniqueness_constraints.detect{|uc|
      !uc.role_sequence.all_role_ref.detect{|rr| rr.role == role || rr.role.fact_type != fact_type}
    }
  end

  if from_1
    return to_1 ? :one_one : :one_many
  else
    return to_1 ? :many_one : :many_many
  end
end