class ActiveFacts::Generators::Population

Constants

MM

Public Class Methods

compatibility() click to toggle source
# File lib/activefacts/generator/population.rb, line 32
def self.compatibility
  [1, %i{relational}]   # one relational composition
end
new(constellation, composition, options = {}) click to toggle source
# File lib/activefacts/generator/population.rb, line 36
def initialize constellation, composition, options = {}
  @constellation = constellation
  @composition = composition
  @options = options

  @trait = ActiveFacts::Generators::Traits::SQL
  if @dialect = options.delete("dialect")
    require 'activefacts/generator/traits/sql/'+@dialect
    trait_name = ActiveFacts::Generators::Traits::SQL.constants.detect{|c| c.to_s =~ %r{#{@dialect}}i}
    @trait = @trait.const_get(trait_name)
  end
  self.class.include @trait
  self.class.extend @trait
  extend @trait
  @population_name = options.delete("population") || ''

  process_options options
end
options() click to toggle source
# File lib/activefacts/generator/population.rb, line 19
def self.options
  # REVISIT: There's no way to support SQL dialect options here
  sql_trait = ActiveFacts::Generators::Traits::SQL
  Class.new.extend(sql_trait).  # Anonymous class to enable access to traits module instance methods
  options.
  merge(
    {
      dialect: [String, "SQL Dialect to use"],
      population: [String, "Name of the fact population to process (defaults to anonymous seed population)"],
    }
  )
end

Public Instance Methods

generate() click to toggle source
# File lib/activefacts/generator/population.rb, line 59
def generate
  @constellation = @composition.constellation
  @vocabulary = @constellation.Vocabulary.values[0]
  @population = @constellation.Population[[[@vocabulary.name], @population_name]]
  raise "Population #{@population_name.inspect} doesn't exist in #{@vocabulary.name.inspect}" unless @population_name
  @composites = @composition.all_composite.select{|c| c.mapping.object_type.all_instance.size > 0}
  header +
    @composites.
    map{|c| generate_composite c}.
    compact*"\n\n" +
  trailer
end
generate_composite(composite) click to toggle source
# File lib/activefacts/generator/population.rb, line 76
def generate_composite composite
  leaf_column_names = composite.mapping.all_leaf.map do |leaf|
    [leaf, safe_column_name(leaf)]
  end

  "-- #{composite.mapping.name}\n" +
  composite.mapping.object_type.all_instance.map do |instance|
    ncv = named_column_values leaf_column_names, instance
    %Q{INSERT INTO #{safe_table_name composite}(#{ncv.map{|name, value|name}*', '})\n} +
    %Q{\tVALUES (#{ncv.map{|name, value| value}*', '});}
  end.sort * "\n"
end
header() click to toggle source
# File lib/activefacts/generator/population.rb, line 72
def header
  schema_prefix
end
named_column_values(leaf_column_names, instance) click to toggle source
# File lib/activefacts/generator/population.rb, line 89
def named_column_values leaf_column_names, instance
  leaf_column_names.map do |leaf, column_name|
    current = instance
    value = nil
    leaf.path[1..-1].each do |component|
      trace :population, "Traversing #{component.inspect}" do
        case component
        when MM::Absorption       # Fact
          fact_type = component.parent_role.fact_type
          trace :population, "Fact Type #{fact_type.default_reading}"

          if MM::LinkFactType === fact_type
            # Populations do not contain LinkFactTypes for an Objectified Fact Type,
            # but Compositions use them. The child_role is probably a mirror role.
            role = fact_type.implying_role  # This is the real role
            fact_type = role.fact_type      # And this is the real fact
            raise "Internal error finding objectified fact" if instance.fact.fact_type != fact_type
            role_value = fact.all_role_value{|rv| rv.role == role}
          else
            role_values = instance.all_role_value.select{|rv| rv.population == @population && rv.fact.fact_type == fact_type}
            raise "Population contains duplicate fact for #{fact_type.default_reading.inspect} in violation of uniqueness constraint" if role_values.size > 1
            if role_values.empty?
              trace :population, "No fact is present"
              break
            end
            residual_rvs = role_values[0].fact.all_role_value.to_a-[role_values[0]]
            raise "Instance of #{fact_type.default_reading.inspect} lacks some role players" unless residual_rvs.size == fact_type.all_role.size-1
            if residual_rvs.size != 1
              raise "Internal error in fact population, n-ary fact type #{fact_type.default_reading}"
            end
            role_value = residual_rvs[0]
          end

          current = role_value.instance
          trace :population, "Instance is #{current.object_type.name}#{current.value ? ' = '+current.value.inspect : ''}"
          value = current.value.inspect

        when MM::Indicator
          fact_type = component.role.fact_type
          trace :population, "Fact Type #{fact_type.default_reading}"
          role_values = instance.all_role_value.select{|rv| rv.population == @population && rv.fact.fact_type == fact_type}
          raise "Population contains duplicate fact for #{fact_type.default_reading.inspect} in violation of uniqueness constraint" if role_values.size > 1
          value = true

        when MM::ValueField
          # The value is the value of the Value Type
          value = current.value.inspect

        when MM::Discriminator
          # The value depends on which of the discriminated roles are played (must be only one)
          raise "Cannot emit population containing a Discriminator, not implemented"
          drs = component.all_discriminated_role.select{|dr| instance.all_role_value.detect{|rv| rv.role == dr.role}}
          raise "Discriminator has ambiguous value, with more than one candidate role" if drs.size > 1
          value = drs[0].value.inspect if drs[0]

        # The following types are not conceptual so will never have asserted facts
        when MM::Injection,
             MM::Scoping,
             MM::SurrogateKey,
             MM::ValidFrom,
             MM::Mapping,
             MM::ComputedValue,
             MM::HashValue
          break

        else
          raise "Unhandled Component type #{component.class.name} in composition"
        end
      end
    end
    value ? [column_name, value] : nil
  end.compact
end
process_options(options) click to toggle source
Calls superclass method
# File lib/activefacts/generator/population.rb, line 55
def process_options options
  super
end
stylise_column_name(name) click to toggle source
# File lib/activefacts/generator/population.rb, line 167
def stylise_column_name name
  name.words.send(@column_case)*@column_joiner
end
trailer() click to toggle source
# File lib/activefacts/generator/population.rb, line 163
def trailer
  ''
end