class ActiveFacts::RMap::Reference

This class contains the core data structure used in composing a relational schema.

A Reference is from one ObjectType to another ObjectType, and relates to the from_role and the to_role. When either ObjectType is an objectified fact type, the corresponding role is nil. When the Reference from_role is of a unary fact type, there’s no to_role or to ObjectType. The final kind of Reference is a self-reference which is added to a ValueType that becomes a table.

When the underlying fact type is a one-to-one (including an inheritance fact type), the Reference may be flipped.

Each Reference has a name; an array of names in fact, in case of adjectives, etc. Each Refererence can produce the reading of the underlying fact type.

A Reference is indexed in the player’s references_from and references_to, and flipping updates those. Finally, a Reference may be marked as absorbing the whole referenced object, and that can flip too.

Attributes

fact_type[R]
fk_jump[RW]
from[R]
from_role[R]
to[R]
to_role[R]

Public Class Methods

new(from, role) click to toggle source

A Reference is created from a object_type in regard to a role it plays

# File lib/activefacts/rmap/reference.rb, line 45
def initialize(from, role)
  @fk_jump = false
  @from = from
  return unless role              # All done if it's a self-value reference for a ValueType
  @fact_type = role.fact_type
  if @fact_type.all_role.size == 1
    # @from_role is nil for a unary
    @to_role = role
    @to = role.fact_type.entity_type      # nil unless the unary is objectified
  elsif (role.fact_type.entity_type == @from)  # role is in "from", an objectified fact type
    @from_role = nil                      # Phantom role
    @to_role = role
    @to = @to_role.object_type
  else
    @from_role = role
    @to = role.fact_type.entity_type      # If set, to_role is a phantom
    unless @to
      raise "Illegal reference through >binary fact type" if @fact_type.all_role.size >2
      @to_role = (role.fact_type.all_role-[role])[0]
      @to = @to_role.object_type
    end
  end
end

Public Instance Methods

from_names() click to toggle source

Return the array of names for the (perhaps implicit) from_role of this Reference

# File lib/activefacts/rmap/reference.rb, line 137
def from_names
  case
  when @from && !@from_role           # @from is an objectified fact type so @from_role is a phantom
    @from.name.camelwords
  when is_unary
    if @from && @from.fact_type
      @from.name.camelwords
    else
      @from_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords
    end
  when !@from_role                  # Self-value role of an independent ValueType
    @from.name.camelwords + ["Value"]
  when @from_role.role_name         # Named role
    @from_role.role_name.camelwords
  else                            # Use the name from the preferred reading
    role_ref = @from_role.preferred_reference
    [role_ref.leading_adjective, @from_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
  end
end
is_absorbing() click to toggle source

Is the to object_type fully absorbed through this reference?

# File lib/activefacts/rmap/reference.rb, line 104
def is_absorbing
  @to && @to.absorbed_via == self
end
is_from_objectified_fact() click to toggle source

If this Reference is from an objectified FactType, there is no from_role

# File lib/activefacts/rmap/reference.rb, line 94
def is_from_objectified_fact
  @to && !@from_role && @to_role
end
is_mandatory() click to toggle source

Is this Reference covered by a mandatory constraint (implicitly or explicitly)

# File lib/activefacts/rmap/reference.rb, line 76
def is_mandatory
  !is_unary &&
    (!@from_role ||        # All phantom roles of fact types are mandatory
      @from_role.is_mandatory)
end
is_one_to_one() click to toggle source
# File lib/activefacts/rmap/reference.rb, line 157
def is_one_to_one
  [:one_one, :subtype, :supertype].include?(role_type)
end
is_self_value() click to toggle source

Is this reference an injected role as a result a ValueType being a table?

# File lib/activefacts/rmap/reference.rb, line 99
def is_self_value
  !@to && !@to_role
end
is_simple_reference() click to toggle source

Is this a simple reference?

# File lib/activefacts/rmap/reference.rb, line 109
def is_simple_reference
  # It's a simple reference to a thing if that thing is a table,
  # or is fully absorbed into another table but not via this reference.
  @to && (@to.is_table or @to.absorbed_via && !is_absorbing)
end
is_to_objectified_fact() click to toggle source

If this Reference is to an objectified FactType, there is no to_role

# File lib/activefacts/rmap/reference.rb, line 88
def is_to_objectified_fact
  # This case is the only one that cannot be used in the preferred identifier of @from
  @to && !@to_role && @from_role
end
is_unary() click to toggle source

Is this Reference from a unary Role?

# File lib/activefacts/rmap/reference.rb, line 83
def is_unary
  @to_role && @to_role.fact_type.all_role.size == 1
end
mirror() click to toggle source

Create a (non-tabulated) flipped version of this Reference. Careful not to tabulate it!

# File lib/activefacts/rmap/reference.rb, line 171
def mirror
  if @to.absorbed_via == self
    @to.absorbed_via = nil
    @from.absorbed_via = self
  end

  # Flip the reference
  @to, @from = @from, @to
  @to_role, @from_role = @from_role, @to_role
  trace :references, "Mirror #{self.inspect} absorbs #{@to.name}" if @to.absorbed_via == self
  self
end
reading() click to toggle source

The reading for the fact type underlying this Reference

# File lib/activefacts/rmap/reference.rb, line 211
def reading
  is_self_value ? "#{from.name} has value" : @fact_type.reading_preferably_starting_with_role(@from_role).expand
end
reversed() click to toggle source
# File lib/activefacts/rmap/reference.rb, line 184
def reversed
  clone.mirror
end
role_type() click to toggle source

What type of Role did this Reference arise from?

# File lib/activefacts/rmap/reference.rb, line 70
def role_type
  role = @from_role||@to_role
  role && role.role_type
end
to_names(is_prefix = true) click to toggle source

Return the array of names for the (perhaps implicit) to_role of this Reference

# File lib/activefacts/rmap/reference.rb, line 116
def to_names(is_prefix = true)
  case
  when is_unary
    if @to && @to.fact_type && is_prefix
      @to.name.camelwords
    else
      @to_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords
    end
  when @to && !@to_role           # @to is an objectified fact type so @to_role is a phantom
    @to.name.camelwords
  when !@to_role                  # Self-value role of an independent ValueType
    @from.name.camelwords + ["Value"]
  when @to_role.role_name         # Named role
    @to_role.role_name.camelwords
  else                            # Use the name from the preferred reading
    role_ref = @to_role.preferred_reference
    [role_ref.leading_adjective, @to_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
  end
end
verbalised_path(reverse = false) click to toggle source
# File lib/activefacts/rmap/reference.rb, line 215
def verbalised_path reverse = false
  return "#{from.name} Value" if is_self_value
  objectified = fact_type.entity_type 
  f =     # Switch to the Link Fact Type if we're traversing an objectification
    (to_role && to_role.link_fact_type) ||
    (from_role && from_role.link_fact_type) ||
    fact_type

  start_role =
    if objectified
      target = reverse ? to : from
      [to_role, from_role, f.all_role_in_order[0]].compact.detect{|role| role.object_type == target}
    else
      reverse ? to_role : from_role
    end
  reading = f.reading_preferably_starting_with_role(start_role)
  (is_mandatory || is_unary ? '' : 'maybe ') +
    reading.expand
end