class ActiveFacts::Metamodel::EntityType

Public Instance Methods

identifying_refs_from() click to toggle source
# File lib/activefacts/generators/transform/surrogate.rb, line 80
      def identifying_refs_from
        pi = preferred_identifier
        rrs = pi.role_sequence.all_role_ref

#       REVISIT: This is actually a ref to us, not from
#       if absorbed_via
#         return [absorbed_via]
#       end

        rrs.map do |rr|
          r = references_from.detect{|ref| rr.role == ref.to_role }
          raise "failed to find #{name} identifying reference for #{rr.role.object_type.name} in #{references_from.inspect}" unless r
          r
        end
      end
inject_surrogate() click to toggle source
# File lib/activefacts/generators/transform/surrogate.rb, line 165
def inject_surrogate
  trace :transform_surrogate, "Injecting a surrogate key into #{self.name}"

  # Disable the preferred identifier:
  pi = preferred_identifier
  trace :transform_surrogate, "pi for #{name} was '#{pi.describe}'"
  pi.is_preferred_identifier = false
  @preferred_identifier = nil   # Kill the cache

  add_surrogate

  trace :transform_surrogate, "pi for #{name} is now '#{preferred_identifier.describe}'"
end
needs_surrogate() click to toggle source
# File lib/activefacts/generators/transform/surrogate.rb, line 96
def needs_surrogate

  # A recursive proc to replace any reference to an Entity Type by its identifying references:
  trace :transform_surrogate_expansion, "Expanding key for #{name}"
  substitute_identifying_refs = proc do |object|
    if ref = object.absorbed_via
      # This shouldn't be necessary, but see the absorbed_via comment above.
      absorbed_into = ref.from
      trace :transform_surrogate_expansion, "recursing to handle absorption of #{object.name} into #{absorbed_into.name}"
      [substitute_identifying_refs.call(absorbed_into)]
    else
      irf = object.identifying_refs_from
      trace :transform_surrogate_expansion, "Iterating for #{object.name} over #{irf.inspect}" do
        irf.each_with_index do |ref, i|
          next if ref.is_unary
          next if ref.to_role.object_type.kind_of?(ActiveFacts::Metamodel::ValueType)
          recurse_to = ref.to_role.object_type

          trace :transform_surrogate_expansion, "#{i}: recursing to expand #{recurse_to.name} key in #{ref}" do
            irf[i] = substitute_identifying_refs.call(recurse_to)
          end
        end
      end
      irf
    end
  end
  irf = substitute_identifying_refs.call(self)

  trace :transform_surrogate, "Does #{name} need a surrogate? it's identified by #{irf.inspect}" do

    pk_fks = identifying_refs_from.map do |ref|
      ref.to && ref.to.is_table ? ref.to : nil
    end

    irf.flatten!

    # Multi-part identifiers are only allowed if:
    # * each part is a foreign key (i.e. it's a join table),
    # * there are no other columns (that might require updating) and
    # * the object is not the target of a foreign key:
    if irf.size >= 2
      if pk_fks.include?(nil)
        trace :transform_surrogate, "#{self.name} needs a surrogate because its multi-part key contains a non-table"
        return true
      elsif references_to.size != 0
        trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect} but is also an FK target"
        return true
      elsif (references_from-identifying_refs_from).size > 0
        # There are other attributes to worry about
        return true
      else
        trace :transform_surrogate, "#{self.name} is a join table between #{pk_fks.map(&:name).inspect}"
        return false
      end
      return true
    end

    # Single-part key. It must be an Auto Counter, or we will add a surrogate

    identifying_type = irf[0].to
    if identifying_type.needs_surrogate
      trace :transform_surrogate, "#{self.name} needs a surrogate because #{irf[0].to.name} is not an AutoCounter, but #{identifying_type.supertypes_transitive.map(&:name).inspect}"
      return true
    end

    false
  end
end