Class: ActiveFacts::RMap::Column

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reference = nil) ⇒ Column

:nodoc:



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

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

Class Method Details

.name(refs, separator = "") ⇒ Object



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
132
133
134
135
136
137
138
139
# File 'lib/activefacts/rmap/columns.rb', line 69

def self.name(refs, separator = "")
  last_names = []
  name_array = nil
  trace :columns, "Building column name from #{refs.inspect}" do
    names = refs.
      inject([]) do |a, ref|

        # Skip any object after the first which is identified by this reference
        if ref != refs[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
          trace :columns, "Skipping #{ref}, identifies non-initial object"
          next a
        end

        names = ref.to_names(ref != refs.last)

        # 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?
            trace :columns, "Skipping supertype #{ref}"
            next a
          end
          trace :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
          trace :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
          trace :columns, "truncating repeated name in #{names.inspect}"
          names.shift
        end

        # If the reference is to the single identifying role of the object_type making the reference,
        # strip the object_type 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

          trace :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}}
    trace :columns, "column name is #{name_array*'.'}"
  end
  separator ? name_array * separator : name_array
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/rmap/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/rmap/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 series of steps (not a full verbalisation)



182
183
184
185
186
# File 'lib/activefacts/rmap/columns.rb', line 182

def comment
  @references.map do |ref|
    ref.verbalised_path
  end.compact * " and "
end

#is_auto_assignedObject

This column is auto-assigned if it’s an auto-assigned value type and is not a foreign key



149
150
151
152
153
154
155
# File 'lib/activefacts/rmap/columns.rb', line 149

def is_auto_assigned
  last_table_ref = references.reverse.detect{|r| r.from && r.from.is_table}
  (to = references[-1].to) &&
    to.is_auto_assigned &&
    references[0].from.identifier_columns.size == 1 &&
    references[0].from == last_table_ref.from
end

#is_mandatoryObject

Is this column mandatory or nullable?



142
143
144
145
146
# File 'lib/activefacts/rmap/columns.rb', line 142

def is_mandatory
  # Uncomment the following line for CWA unaries (not nullable, just T/F)
  # @references[-1].is_unary ||
    !@references.detect{|ref| !ref.is_mandatory || ref.is_unary }
end

#name(separator = "") ⇒ Object

A Column name is a sequence of names (derived from the to_roles of the References) appended by a separator 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
# File 'lib/activefacts/rmap/columns.rb', line 65

def name(separator = "")
  self.class.name(@references, separator)
end

#prepend(reference) ⇒ Object

:nodoc:



52
53
54
55
# File 'lib/activefacts/rmap/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/rmap/columns.rb', line 28

def references
  @references ||= []
end

#to_sObject

:nodoc:



188
189
190
# File 'lib/activefacts/rmap/columns.rb', line 188

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

#typeObject

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



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/activefacts/rmap/columns.rb', line 158

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.base_role.role_value_constraint
  end

  vt = references[-1].is_self_value ? references[-1].from : references[-1].to
  begin
    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
    last_vt = vt
    vt = vt.supertype
  end while vt
  params[:underlying_type] = last_vt
  return [last_vt.name, params, constraints]
end