Class: ActiveFacts::Persistence::Reference

Inherits:
Object
  • Object
show all
Defined in:
lib/activefacts/persistence/reference.rb,
lib/activefacts/persistence/columns.rb

Overview

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

A Reference is from one Concept to another Concept, and relates to the from_role and the to_role. When either Concept 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 Concept. 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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from, role) ⇒ Reference

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



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/activefacts/persistence/reference.rb', line 44

def initialize(from, role)
  @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.concept
  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.concept
    end
  end
end

Instance Attribute Details

#fact_typeObject (readonly)

Returns the value of attribute fact_type.



41
42
43
# File 'lib/activefacts/persistence/reference.rb', line 41

def fact_type
  @fact_type
end

#fromObject (readonly)

A “from” instance is related to one “to” instance



39
40
41
# File 'lib/activefacts/persistence/reference.rb', line 39

def from
  @from
end

#from_roleObject (readonly)

For objectified facts, one role will be nil (a phantom)



40
41
42
# File 'lib/activefacts/persistence/reference.rb', line 40

def from_role
  @from_role
end

#toObject (readonly)

A “from” instance is related to one “to” instance



39
40
41
# File 'lib/activefacts/persistence/reference.rb', line 39

def to
  @to
end

#to_roleObject (readonly)

For objectified facts, one role will be nil (a phantom)



40
41
42
# File 'lib/activefacts/persistence/reference.rb', line 40

def to_role
  @to_role
end

Instance Method Details

#columns(excluded_supertypes) ⇒ Object

:nodoc:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/activefacts/persistence/columns.rb', line 182

def columns(excluded_supertypes)  #:nodoc:
  kind = ""
  cols = 
    if is_unary && !(@to && @to.fact_type)
      kind = "unary "
      [Column.new()]
    elsif is_self_value
      kind = "self-role "
      [Column.new()]
    elsif is_simple_reference
      @to.reference_columns(excluded_supertypes)
    else
      kind = "absorbing "
      @to.all_columns(excluded_supertypes)
    end

  cols.each do |c|
    c.prepend self
  end

  debug :columns, "Columns from #{kind}#{self}" do
    cols.each {|c|
      debug :columns, "#{c}"
    }
  end
end

#detabulateObject

:nodoc:



161
162
163
164
165
166
167
# File 'lib/activefacts/persistence/reference.rb', line 161

def detabulate        #:nodoc:
  # Remove from @to and @from's reference lists if present
  return unless @from.references_from.delete(self)
  @to.references_to.delete self if @to    # Guard against self-values
  debug :references, "Dropping #{to_s}"
  self
end

#flipObject

For a one-to-one (or a subtyping fact type), reverse the direction.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/activefacts/persistence/reference.rb', line 135

def flip                          #:nodoc:
  raise "Illegal flip of #{self}" unless @to and [:one_one, :subtype, :supertype].include?(role_type)

  detabulate

  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

  tabulate
end

#inspectObject

:nodoc:



178
179
180
# File 'lib/activefacts/persistence/reference.rb', line 178

def inspect #:nodoc:
  to_s
end

#is_absorbingObject

Is the to concept fully absorbed through this reference?



102
103
104
# File 'lib/activefacts/persistence/reference.rb', line 102

def is_absorbing
  @to && @to.absorbed_via == self
end

#is_from_objectified_factObject

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



92
93
94
# File 'lib/activefacts/persistence/reference.rb', line 92

def is_from_objectified_fact
  @to && @to_role && !@from_role
end

#is_mandatoryObject

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



74
75
76
77
78
# File 'lib/activefacts/persistence/reference.rb', line 74

def is_mandatory
  !@from_role ||        # All phantom roles of fact types are mandatory
  is_unary ||           # Unary fact types become booleans, which must be true or false
  @from_role.is_mandatory
end

#is_self_valueObject

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



97
98
99
# File 'lib/activefacts/persistence/reference.rb', line 97

def is_self_value
  !@to && !@to_role
end

#is_simple_referenceObject

Is this a simple reference?



107
108
109
110
111
# File 'lib/activefacts/persistence/reference.rb', line 107

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_factObject

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



86
87
88
89
# File 'lib/activefacts/persistence/reference.rb', line 86

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_unaryObject

Is this Reference from a unary Role?



81
82
83
# File 'lib/activefacts/persistence/reference.rb', line 81

def is_unary
  @to_role && @to_role.fact_type.all_role.size == 1
end

#readingObject

The reading for the fact type underlying this Reference



174
175
176
# File 'lib/activefacts/persistence/reference.rb', line 174

def reading
  is_self_value ? "#{from.name} has value" : @fact_type.default_reading([], true) # Include role name defn's
end

#role_typeObject

What type of Role did this Reference arise from?



68
69
70
71
# File 'lib/activefacts/persistence/reference.rb', line 68

def role_type
  role = @from_role||@to_role
  role && role.role_type
end

#tabulateObject

:nodoc:



152
153
154
155
156
157
158
159
# File 'lib/activefacts/persistence/reference.rb', line 152

def tabulate        #:nodoc:
  # Add to @to and @from's reference lists
  @from.references_from << self
  @to.references_to << self if @to        # Guard against self-values

  debug :references, "Adding #{to_s}"
  self
end

#to_namesObject

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



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/activefacts/persistence/reference.rb', line 114

def to_names
  case
  when is_unary
    if @to && @to.fact_type
      @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.concept.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
  end
end

#to_sObject

:nodoc:



169
170
171
# File 'lib/activefacts/persistence/reference.rb', line 169

def to_s              #:nodoc:
  "reference from #{@from.name}#{@to ? " to #{@to.name}" : ""}" + (@fact_type ? " in '#{@fact_type.default_reading}'" : "")
end