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 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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from, role) ⇒ Reference

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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/activefacts/persistence/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

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

#fk_jumpObject

True if this reference links a table to another in an FK (between absorbed references)



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

def fk_jump
  @fk_jump
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:



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/activefacts/persistence/columns.rb', line 192

def columns(excluded_supertypes)  #:nodoc:
  kind = ""
  cols = 
    if is_unary
      kind = "unary "
      [Column.new()] +
        ((@to && @to.fact_type) ?  @to.all_columns(excluded_supertypes) : [])
    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:



195
196
197
198
199
200
201
# File 'lib/activefacts/persistence/reference.rb', line 195

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.



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

def flip                          #:nodoc:
  raise "Illegal flip of #{self}" unless @to and is_one_to_one

  detabulate
  mirror
  tabulate
end

#from_namesObject

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



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

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

#inspectObject

:nodoc:



213
214
215
# File 'lib/activefacts/persistence/reference.rb', line 213

def inspect #:nodoc:
  to_s
end

#is_absorbingObject

Is the to object_type fully absorbed through this reference?



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

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



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

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

#is_mandatoryObject

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



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

def is_mandatory
  !@from_role ||        # All phantom roles of fact types are mandatory
  @from_role.is_mandatory
end

#is_one_to_oneObject



156
157
158
# File 'lib/activefacts/persistence/reference.rb', line 156

def is_one_to_one
  [:one_one, :subtype, :supertype].include?(role_type)
end

#is_self_valueObject

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



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

def is_self_value
  !@to && !@to_role
end

#is_simple_referenceObject

Is this a simple reference?



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

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



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

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?



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

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

#mirrorObject

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



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/activefacts/persistence/reference.rb', line 170

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
  self
end

#readingObject

The reading for the fact type underlying this Reference



209
210
211
# File 'lib/activefacts/persistence/reference.rb', line 209

def reading
  is_self_value ? "#{from.name} has value" : @fact_type.default_reading
end

#reversedObject



182
183
184
# File 'lib/activefacts/persistence/reference.rb', line 182

def reversed
  clone.mirror
end

#role_typeObject

What type of Role did this Reference arise from?



70
71
72
73
# File 'lib/activefacts/persistence/reference.rb', line 70

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

#tabulateObject

:nodoc:



186
187
188
189
190
191
192
193
# File 'lib/activefacts/persistence/reference.rb', line 186

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_names(is_prefix = true) ⇒ Object

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



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

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

#to_sObject

:nodoc:



203
204
205
206
# File 'lib/activefacts/persistence/reference.rb', line 203

def to_s              #:nodoc:
  ref_type = fk_jump ? "jumping to" : (is_absorbing ? "absorbing" : "to")
  "reference from #{@from.name}#{@to ? " #{ref_type} #{@to.name}" : ""}" + (@fact_type ? " in '#{@fact_type.default_reading}'" : "")
end