Class: ActiveFacts::Persistence::Column

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

Instance Method Summary collapse

Constructor Details

#initialize(reference = nil) ⇒ Column

:nodoc:



23
24
25
# File 'lib/activefacts/persistence/columns.rb', line 23

def initialize(reference = nil) #:nodoc:
  references << reference if reference
end

Instance Method Details

#absorption_levelObject

How many of the initial references are involved in full absorption of an EntityType into this column’s table



43
44
45
46
47
48
49
50
# File 'lib/activefacts/persistence/columns.rb', line 43

def absorption_level
  l = 0
  @references.detect do |ref|
    l += 1 if ref.is_absorbing
    false
  end
  l
end

#absorption_referencesObject

All references up to and including the first non-absorbing reference



33
34
35
36
37
38
39
40
# File 'lib/activefacts/persistence/columns.rb', line 33

def absorption_references
  @references.inject([]) do |array, ref|
    array << ref
    # puts "Column #{name} spans #{ref}, #{ref.is_absorbing ? "" : "not "} absorbing (#{ref.to.name} absorbs via #{ref.to.absorbed_via.inspect})"
    break array unless ref.is_absorbing
    array
  end
end

#commentObject

The comment is the readings from the References expressed as a join



168
169
170
171
172
173
174
# File 'lib/activefacts/persistence/columns.rb', line 168

def comment
  @references.map do |ref|
    (ref.is_mandatory ? "" : "maybe ") +
    (ref.fact_type && ref.fact_type.entity_type ? ref.fact_type.entity_type.name+" is where " : "") +
    ref.reading
  end * " and "
end

#is_auto_assignedObject

Is this column an auto-assigned value type?



139
140
141
# File 'lib/activefacts/persistence/columns.rb', line 139

def is_auto_assigned
  (to = references[-1].to) && to.is_auto_assigned
end

#is_mandatoryObject

Is this column mandatory or nullable?



134
135
136
# File 'lib/activefacts/persistence/columns.rb', line 134

def is_mandatory
  !@references.detect{|ref| !ref.is_mandatory}
end

#name(joiner = "") ⇒ Object

A Column name is a sequence of names (derived from the to_roles of the References) joined by a joiner string (pass nil to get the original array of names) The names to use is derived from the to_names of each Reference, modified by these rules:

  • A reference after the first one which is not a TypeInheritance but where the from object plays the sole role in the preferred identifier of the to entity is ignored,

  • A reference (after a name has been retained) which is a TypeInheritance retains the names of the subtype,

  • If the names retained so far end in XYZ and the to_names start with XYZ, remove the duplication

  • If we have retained the name of an entity, and this reference is the sole identifying role of an entity, and the identifying object has a name that is prefixed by the name of the object it identifies, remove the prefix and use just the suffix.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/activefacts/persistence/columns.rb', line 65

def name(joiner = "")
  last_names = []
  names = @references.
    inject([]) do |a, ref|

      # Skip any object after the first which is identified by this reference
      if ref != @references[0] and
          !ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) and
          ref.to and
          ref.to.is_a?(ActiveFacts::Metamodel::EntityType) and
          (role_ref = ref.to.preferred_identifier.role_sequence.all_role_ref.single) and
          role_ref.role == ref.from_role
        debug :columns, "Skipping #{ref}, identifies non-initial object"
        next a
      end

      names = ref.to_names

      # When traversing type inheritances, keep the subtype name, not the supertype names as well:
      if a.size > 0 && ref.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
        if ref.to != ref.fact_type.subtype  # Did we already have the subtype?
          debug :columns, "Skipping supertype #{ref}"
          next a
        end
        debug :columns, "Eliding supertype in #{ref}"
        last_names.size.times { a.pop }   # Remove the last names added
      elsif last_names.last && last_names.last == names[0][0...last_names.last.size] 
        # When Xyz is followed by XyzID, truncate that to just ID
        debug :columns, "truncating repeated #{last_names.last} in #{names[0]}"
        names[0] = names[0][last_names.last.size..-1]
        names.shift if names[0] == ''
      elsif last_names.last == names[0]
        # Same, but where an underscore split up the words
        debug :columns, "truncating repeated name in #{names.inspect}"
        names.shift
      end

      # If the reference is to the single identifying role of the concept making the reference,
      # strip the concept name from the start of the reference role
      if a.size > 0 and
          (et = ref.from).is_a?(ActiveFacts::Metamodel::EntityType) and
          # This instead of the next 2 would apply to all identifying roles, but breaks some examples:
          # (role_ref = et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role == ref.to_role}) and
          (role_ref = et.preferred_identifier.role_sequence.all_role_ref.single) and
          role_ref.role == ref.to_role and
          names[0][0...et.name.size].downcase == et.name.downcase

        debug :columns, "truncating transitive identifying role #{names.inspect}"
        names[0] = names[0][et.name.size..-1]
        names.shift if names[0] == ""
      end

      last_names = names

      a += names
      a
    end.elide_repeated_subsequences { |a, b|
      if a.is_a?(Array)
        a.map{|e| e.downcase} == b.map{|e| e.downcase}
      else
        a.downcase == b.downcase
      end
    }

  name_array = names.map{|n| n.sub(/^[a-z]/){|s| s.upcase}}
  joiner ? name_array * joiner : name_array
end

#prepend(reference) ⇒ Object

:nodoc:



52
53
54
55
# File 'lib/activefacts/persistence/columns.rb', line 52

def prepend reference           #:nodoc:
  references.insert 0, reference
  self
end

#referencesObject

A Column is created from a path through an array of References to a ValueType



28
29
30
# File 'lib/activefacts/persistence/columns.rb', line 28

def references
  @references ||= []
end

#to_sObject

:nodoc:



176
177
178
# File 'lib/activefacts/persistence/columns.rb', line 176

def to_s  #:nodoc:
  "#{@references[0].from.name} column #{name('.')}"
end

#typeObject

What’s the underlying SQL data type of this column?



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/activefacts/persistence/columns.rb', line 144

def type
  params = {}
  constraints = []
  return ["BIT", params, constraints] if references[-1].is_unary   # It's a unary

  # Add a role value constraint
  # REVISIT: Can add join-role-value-constraints here, if we ever provide a way to define them
  if references[-1].to_role && references[-1].to_role.role_value_constraint
    constraints << references[-1].to_role.role_value_constraint
  end

  vt = references[-1].is_self_value ? references[-1].from : references[-1].to
  params[:length] ||= vt.length if vt.length.to_i != 0
  params[:scale] ||= vt.scale if vt.scale.to_i != 0
  while vt.supertype
    params[:length] ||= vt.length if vt.length.to_i != 0
    params[:scale] ||= vt.scale if vt.scale.to_i != 0
    constraints << vt.value_constraint if vt.value_constraint
    vt = vt.supertype
  end
  return [vt.name, params, constraints]
end