Class: ActiveFacts::Persistence::Column

Inherits:
Object
  • Object
show all
Defined in:
lib/activefacts/mapping/rails.rb,
lib/activefacts/persistence/columns.rb,
lib/activefacts/generate/rails/models.rb,
lib/activefacts/generate/transform/surrogate.rb

Class Method Summary collapse

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

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
# File 'lib/activefacts/persistence/columns.rb', line 69

def self.name(refs, separator = "")
  last_names = []
  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
        debug :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?
          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 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

        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}}
  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/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 series of steps (not a full verbalisation)



178
179
180
181
182
183
184
# File 'lib/activefacts/persistence/columns.rb', line 178

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

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



145
146
147
148
149
150
151
# File 'lib/activefacts/persistence/columns.rb', line 145

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_auto_timestampObject



232
233
234
235
236
237
238
239
# File 'lib/activefacts/generate/rails/models.rb', line 232

def is_auto_timestamp
	case name('_')
	when /\A(created|updated)_(at|on)\Z/i
	  true
	else
	  false
	end
end

#is_injected_surrogateObject



178
179
180
181
# File 'lib/activefacts/generate/transform/surrogate.rb', line 178

def is_injected_surrogate
	references.size == 1 and
	  references[0].from_role == references[0].from.injected_surrogate_role
end

#is_mandatoryObject

Is this column mandatory or nullable?



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

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/persistence/columns.rb', line 65

def name(separator = "")
  self.class.name(@references, separator)
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

#rails_nameObject



74
75
76
# File 'lib/activefacts/mapping/rails.rb', line 74

def rails_name
	Persistence::rails_singular_name(name('_'))
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:



186
187
188
# File 'lib/activefacts/persistence/columns.rb', line 186

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

#typeObject

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



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/activefacts/persistence/columns.rb', line 154

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