activefacts 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,7 @@ module ActiveFacts
21
21
  @indices = options.include? "indices"
22
22
  end
23
23
 
24
+ public
24
25
  def generate(out = $>)
25
26
  @vocabulary.tables if @tables || @columns || @indices
26
27
  end
@@ -31,12 +31,13 @@ module ActiveFacts
31
31
  end
32
32
 
33
33
  def vocabulary_start(vocabulary)
34
+ puts "require 'activefacts/api'\n"
34
35
  if @sql
35
36
  require 'activefacts/persistence'
37
+ puts "require 'activefacts/persistence'\n"
36
38
  @tables = vocabulary.tables
37
39
  end
38
- puts "require 'activefacts/api'\n\n"
39
- puts "module #{vocabulary.name}\n\n"
40
+ puts "\nmodule #{vocabulary.name}\n\n"
40
41
  end
41
42
 
42
43
  def vocabulary_end
@@ -63,7 +64,7 @@ module ActiveFacts
63
64
 
64
65
  puts " class #{o.name} < #{ruby_type_name}\n" +
65
66
  " value_type #{params}\n"
66
- puts " table" if @sql and @tables.include? o
67
+ puts " table" if @sql and o.is_table
67
68
  puts " \# REVISIT: #{o.name} has restricted values\n" if o.value_restriction
68
69
  puts " \# REVISIT: #{o.name} is in units of #{o.unit.name}\n" if o.unit
69
70
  roles_dump(o)
@@ -71,9 +72,13 @@ module ActiveFacts
71
72
  end
72
73
 
73
74
  def subtype_dump(o, supertypes, pi = nil)
74
- puts " class #{o.name} < #{ supertypes[0].name }"
75
+ primary_supertype = o && (o.identifying_supertype || o.supertypes[0])
76
+ secondary_supertypes = o.supertypes-[primary_supertype]
77
+
78
+ puts " class #{o.name} < #{ primary_supertype.name }"
75
79
  puts " identified_by #{identified_by(o, pi)}" if pi
76
- puts " table" if @sql and @tables.include? o
80
+ puts " supertypes "+secondary_supertypes.map(&:name)*", " if secondary_supertypes.size > 0
81
+ puts " table" if @sql and o.is_table
77
82
  fact_roles_dump(o.fact_type) if o.fact_type
78
83
  roles_dump(o)
79
84
  puts " end\n\n"
@@ -83,7 +88,7 @@ module ActiveFacts
83
88
  def non_subtype_dump(o, pi)
84
89
  puts " class #{o.name}"
85
90
  puts " identified_by #{identified_by(o, pi)}"
86
- puts " table" if @sql and @tables.include? o
91
+ puts " table" if @sql and o.is_table
87
92
  fact_roles_dump(o.fact_type) if o.fact_type
88
93
  roles_dump(o)
89
94
  puts " end\n\n"
@@ -107,6 +112,7 @@ module ActiveFacts
107
112
  "\n" +
108
113
  secondary_supertypes.map{|sst| " supertype :#{sst.name}"}*"\n" +
109
114
  (pi ? " identified_by #{identified_by(o, pi)}" : "")
115
+ puts " table" if @sql and o.is_table
110
116
  fact_roles_dump(fact_type)
111
117
  roles_dump(o)
112
118
  puts " end\n\n"
@@ -120,32 +126,10 @@ module ActiveFacts
120
126
  }*", "
121
127
  end
122
128
 
123
- def roles_dump(o)
124
- @ar_by_role = nil
125
- if @sql and @tables.include?(o)
126
- ar = o.absorbed_roles
127
- @ar_by_role = ar.all_role_ref.inject({}){|h,rr|
128
- input_role = (j=rr.all_join_path).size > 0 ? j[0].input_role : rr.role
129
- (h[input_role] ||= []) << rr
130
- h
131
- }
132
- #puts ar.all_role_ref.map{|rr| "\t"+rr.describe}*"\n"
133
- end
134
- super
135
- end
136
-
137
129
  def unary_dump(role, role_name)
138
130
  puts " maybe :"+role_name
139
131
  end
140
132
 
141
- def role_dump(role)
142
- other_role = role.fact_type.all_role.select{|r| r != role}[0] || role
143
- if @ar_by_role and @ar_by_role[other_role] and @sql
144
- puts " # role #{role.fact_type.describe(role)}: absorbs in through #{preferred_role_name(other_role)}: "+@ar_by_role[other_role].map(&:column_name)*", "
145
- end
146
- super
147
- end
148
-
149
133
  def binary_dump(role, role_name, role_player, one_to_one = nil, readings = nil, other_role_name = nil, other_method_name = nil)
150
134
  # Find whether we need the name of the other role player, and whether it's defined yet:
151
135
  if role_name.camelcase(true) == role_player.name
@@ -116,7 +116,7 @@ module ActiveFacts
116
116
  tables_emitted = {}
117
117
  delayed_foreign_keys = []
118
118
 
119
- @vocabulary.tables.sort_by{|table| table.name}.each do |table|
119
+ @vocabulary.tables.each do |table|
120
120
  puts "CREATE TABLE #{escape table.name} ("
121
121
 
122
122
  pk = table.identifier_columns
@@ -127,9 +127,9 @@ module ActiveFacts
127
127
  column.references[0].is_simple_reference
128
128
  end
129
129
 
130
- columns = table.columns.sort_by do |column|
131
- column.name(nil)
132
- end.map do |column|
130
+ # We sort the columns here, not in the persistence layer, because it affects
131
+ # the ordering of columns in an index :-(.
132
+ columns = table.columns.sort_by { |column| column.name(nil) }.map do |column|
133
133
  name = escape column.name("")
134
134
  padding = " "*(name.size >= ColumnNameMax ? 1 : ColumnNameMax-name.size)
135
135
  type, params, restrictions = column.type
@@ -158,22 +158,11 @@ module ActiveFacts
158
158
  ")"
159
159
 
160
160
  inline_fks = []
161
- table.foreign_keys.sort_by do |fk|
162
- # Put the Foreign keys in a defined order:
163
- [ fk.to.name,
164
- fk.to_columns.map{|col| col.name(nil).sort},
165
- fk.from_columns.map{|col| col.name(nil).sort}
166
- ]
167
- end.each do |fk|
168
- # Put the column pairs in a defined order, sorting key pairs by to-name:
169
- froms, tos = fk.from_columns.zip(fk.to_columns).sort_by { |pair|
170
- pair[1].name(nil)
171
- }.transpose
172
-
161
+ table.foreign_keys.each do |fk|
173
162
  fk_text = "FOREIGN KEY (" +
174
- froms.map{|column| column.name}*", " +
163
+ fk.from_columns.map{|column| column.name}*", " +
175
164
  ") REFERENCES #{escape fk.to.name} (" +
176
- tos.map{|column| column.name}*", " +
165
+ fk.to_columns.map{|column| column.name}*", " +
177
166
  ")"
178
167
  if !@delay_fks and # We don't want to delay all Fks
179
168
  (tables_emitted[fk.to] or # The target table has been emitted
@@ -187,10 +176,7 @@ module ActiveFacts
187
176
  indices = table.indices
188
177
  inline_indices = []
189
178
  delayed_indices = []
190
- indices.sort_by do |index|
191
- # Put the indices in a defined order:
192
- index.columns.map(&:name)
193
- end.each do |index|
179
+ indices.each do |index|
194
180
  next if index.over == table && index.is_primary # Already did the primary keys
195
181
  abbreviated_column_names = index.abbreviated_column_names*""
196
182
  column_names = index.column_names
@@ -16,6 +16,7 @@ module ActiveFacts
16
16
  @vocabulary = @vocabulary.Vocabulary.values[0] if ActiveFacts::API::Constellation === @vocabulary
17
17
  end
18
18
 
19
+ public
19
20
  def generate(out = $>)
20
21
  out.puts @vocabulary.constellation.verbalise
21
22
  end
@@ -1,5 +1,3 @@
1
- #
2
- # ActiveFacts Vocabulary Input.
3
1
  # Compile a CQL file into an ActiveFacts vocabulary.
4
2
  #
5
3
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
@@ -97,15 +95,15 @@ module ActiveFacts
97
95
  # Create the base type:
98
96
  base_type = nil
99
97
  if (base_type_name != name)
100
- unless base_type = @constellation.ValueType[[@constellation.Name(base_type_name), @vocabulary]]
98
+ unless base_type = @constellation.ValueType[[@vocabulary, @constellation.Name(base_type_name)]]
101
99
  #puts "REVISIT: Creating base ValueType #{base_type_name} in #{@vocabulary.inspect}"
102
- base_type = @constellation.ValueType(base_type_name, @vocabulary)
100
+ base_type = @constellation.ValueType(@vocabulary, base_type_name)
103
101
  return if base_type_name == name
104
102
  end
105
103
  end
106
104
 
107
105
  # Create and initialise the ValueType:
108
- vt = @constellation.ValueType(name, @vocabulary)
106
+ vt = @constellation.ValueType(@vocabulary, name)
109
107
  vt.supertype = base_type if base_type
110
108
  vt.length = length if length
111
109
  vt.scale = scale if scale
@@ -120,7 +118,7 @@ module ActiveFacts
120
118
  min ? [min.to_s, true] : nil,
121
119
  max ? [max.to_s, true] : nil
122
120
  )
123
- ar = @constellation.AllowedRange(v_range, vt.value_restriction)
121
+ ar = @constellation.AllowedRange(vt.value_restriction, v_range)
124
122
  end
125
123
  end
126
124
  end
@@ -130,7 +128,7 @@ module ActiveFacts
130
128
  debug :entity, "Defining Entity Type #{name}" do
131
129
  # Assert the entity:
132
130
  # If this entity was forward referenced, this won't be a new object, and will subsume its roles
133
- entity_type = @constellation.EntityType(name, @vocabulary)
131
+ entity_type = @constellation.EntityType(@vocabulary, name)
134
132
 
135
133
  # Set up its supertypes:
136
134
  supertypes.each do |supertype_name|
@@ -162,9 +160,9 @@ module ActiveFacts
162
160
 
163
161
  # Find or Create an appropriate ValueType called "#{name}#{mode}", of the supertype "#{mode}"
164
162
  vt_name = "#{name}#{mode}"
165
- unless vt = @constellation.ValueType[[vt_name, @vocabulary]]
166
- base_vt = @constellation.ValueType(mode, @vocabulary)
167
- vt = @constellation.ValueType(vt_name, @vocabulary, :supertype => base_vt)
163
+ unless vt = @constellation.ValueType[[@vocabulary, vt_name]]
164
+ base_vt = @constellation.ValueType(@vocabulary, mode)
165
+ vt = @constellation.ValueType(@vocabulary, vt_name, :supertype => base_vt)
168
166
  end
169
167
  end
170
168
 
@@ -373,7 +371,7 @@ module ActiveFacts
373
371
 
374
372
  def add_supertype(entity_type, supertype_name, identifying_supertype)
375
373
  debug :supertype, "Supertype #{supertype_name}"
376
- supertype = @constellation.EntityType(supertype_name, @vocabulary)
374
+ supertype = @constellation.EntityType(@vocabulary, supertype_name)
377
375
  inheritance_fact = @constellation.TypeInheritance(entity_type, supertype, :fact_type_id => :new)
378
376
 
379
377
  # Create a reading:
@@ -459,7 +457,7 @@ module ActiveFacts
459
457
 
460
458
  # The fact type has a name iff it's objectified as an entity type
461
459
  #puts "============= Creating entity #{name} to nominalize fact type #{fact_type.default_reading} ======================" if name
462
- fact_type.entity_type = @constellation.EntityType(name, @vocabulary) if name
460
+ fact_type.entity_type = @constellation.EntityType(@vocabulary, name) if name
463
461
 
464
462
  # Add the identifying PresenceConstraint for this fact type:
465
463
  if fact_type.all_role.size == 1 && !fact_type.entity_type
@@ -496,7 +494,7 @@ module ActiveFacts
496
494
  # sequences. Each binding that isn't common at this top level
497
495
  # must occur more than once in each group of fact types where
498
496
  # it appears, and it forms a join between those fact types.
499
- def bind_join_paths_as_role_sequences(readings_list)
497
+ def bind_joins_as_role_sequences(readings_list)
500
498
  @symbols = SymbolTable.new(@constellation, @vocabulary)
501
499
  fact_roles_list = []
502
500
  bindings_list = []
@@ -520,8 +518,8 @@ module ActiveFacts
520
518
  end
521
519
 
522
520
  # Each set of binding arrays in the list must share at least one common binding
523
- bindings_by_join_path = bindings_list.map{|join_path| join_path.flatten}
524
- common_bindings = bindings_by_join_path[1..-1].inject(bindings_by_join_path[0]) { |c, b| c & b }
521
+ bindings_by_join = bindings_list.map{|join| join.flatten}
522
+ common_bindings = bindings_by_join[1..-1].inject(bindings_by_join[0]) { |c, b| c & b }
525
523
  # Was:
526
524
  # common_bindings = bindings_list.inject(bindings_list[0]) { |common, bindings| common & bindings }
527
525
  raise "Set constraints must have at least one common role between the sets" unless common_bindings.size > 0
@@ -533,9 +531,9 @@ module ActiveFacts
533
531
  # Each element of a join path is the array of bindings for a fact type invocation.
534
532
  # Each invocation must share a binding (not one of the globally common ones) with
535
533
  # another invocation in that join path.
536
- bindings_list.each_with_index do |join_path, jpnum|
534
+ bindings_list.each_with_index do |join, jpnum|
537
535
  # Check that this bindings array creates a complete join path:
538
- join_path.each_with_index do |bindings, i|
536
+ join.each_with_index do |bindings, i|
539
537
  fact_type_roles = fact_roles_list[jpnum][i]
540
538
  fact_type = fact_type_roles[0].fact_type
541
539
 
@@ -543,10 +541,10 @@ module ActiveFacts
543
541
  # These bindings must be joined to some later fact type by a common binding that isn't a globally-common one:
544
542
  local_bindings = bindings-common_bindings
545
543
  next if local_bindings.size == 0 # No join path is required, as only one fact type is invoked.
546
- next if i == join_path.size-1 # We already checked that the last fact type invocation is joined
544
+ next if i == join.size-1 # We already checked that the last fact type invocation is joined
547
545
  ok = local_bindings.detect do |local_binding|
548
546
  j = i+1
549
- join_path[j..-1].detect do |other_bindings|
547
+ join[j..-1].detect do |other_bindings|
550
548
  other_fact_type_roles = fact_roles_list[jpnum][j]
551
549
  other_fact_type = other_fact_type_roles[0].fact_type
552
550
  j += 1
@@ -568,10 +566,10 @@ module ActiveFacts
568
566
  role_sequences = readings_list.map{|r| @constellation.RoleSequence(:new) }
569
567
  common_bindings.each_with_index do |binding, index|
570
568
  role_sequences.each_with_index do |rs, rsi|
571
- join_path = bindings_list[rsi]
569
+ join = bindings_list[rsi]
572
570
  fact_pos = nil
573
- join_pos = (0...join_path.size).detect do |i|
574
- fact_pos = join_path[i].index(binding)
571
+ join_pos = (0...join.size).detect do |i|
572
+ fact_pos = join[i].index(binding)
575
573
  end
576
574
  @constellation.RoleRef(rs, index).role = fact_roles_list[rsi][join_pos][fact_pos]
577
575
  end
@@ -581,7 +579,7 @@ module ActiveFacts
581
579
  end
582
580
 
583
581
  def subset_constraint(subset_readings, superset_readings)
584
- role_sequences = bind_join_paths_as_role_sequences([subset_readings, superset_readings])
582
+ role_sequences = bind_joins_as_role_sequences([subset_readings, superset_readings])
585
583
 
586
584
  #puts "subset_constraint:\n\t#{subset_readings.inspect}\n\t#{superset_readings.inspect}"
587
585
  #puts "\t#{role_sequences.map{|rs| rs.describe}.inspect}"
@@ -601,7 +599,7 @@ module ActiveFacts
601
599
  # Exactly one or at most one, nothing else will do
602
600
  raise "Set comparison constraint must use 'at most' or 'exactly' one" if quantifier[1] != 1
603
601
 
604
- role_sequences = bind_join_paths_as_role_sequences(readings_list)
602
+ role_sequences = bind_joins_as_role_sequences(readings_list)
605
603
 
606
604
  # Create the constraint:
607
605
  constraint = @constellation.SetExclusionConstraint(:new)
@@ -615,7 +613,7 @@ module ActiveFacts
615
613
  def equality_constraint(*readings_list)
616
614
  #puts "REVISIT: equality\n\t#{readings_list.map{|rl| rl.inspect}*"\n\tif and only if\n\t"}"
617
615
 
618
- role_sequences = bind_join_paths_as_role_sequences(readings_list)
616
+ role_sequences = bind_joins_as_role_sequences(readings_list)
619
617
 
620
618
  # Create the constraint:
621
619
  constraint = @constellation.SetEqualityConstraint(:new)
@@ -994,7 +992,7 @@ module ActiveFacts
994
992
  return nil if roles.size == 0 # Safeguard; this would chuck an exception otherwise
995
993
  roles[0].all_role_ref.each do |role_ref|
996
994
  next if role_ref.role_sequence.all_role_ref.map(&:role) != roles
997
- pc = role_ref.role_sequence.all_presence_constraint.only # Will throw an exception if there's more than one.
995
+ pc = role_ref.role_sequence.all_presence_constraint.single # Will return nil if there's more than one.
998
996
  #puts "Existing PresenceConstraint matches those roles!" if pc
999
997
  return pc if pc
1000
998
  end
@@ -1018,15 +1016,15 @@ module ActiveFacts
1018
1016
  end
1019
1017
 
1020
1018
  def concept_by_name(name)
1021
- player = @constellation.Concept[[name, @vocabulary.identifying_role_values]]
1019
+ player = @constellation.Concept[[@vocabulary.identifying_role_values, name]]
1022
1020
 
1023
1021
  # REVISIT: Hack to allow facts to refer to standard types that will be imported from standard vocabulary:
1024
1022
  if !player && %w{Date DateAndTime Time}.include?(name)
1025
- player = @constellation.ValueType(name, @vocabulary.identifying_role_values)
1023
+ player = @constellation.ValueType(@vocabulary.identifying_role_values, name)
1026
1024
  end
1027
1025
 
1028
1026
  if (!player && @symbols.allowed_forward[name])
1029
- player = @constellation.EntityType(name, @vocabulary)
1027
+ player = @constellation.EntityType(@vocabulary, name)
1030
1028
  end
1031
1029
  player
1032
1030
  end
@@ -1173,15 +1171,15 @@ module ActiveFacts
1173
1171
  # return the EntityType or ValueType this name refers to:
1174
1172
  def concept(name, allowed_forward = false)
1175
1173
  # See if the name is a defined concept in this vocabulary:
1176
- player = @constellation.Concept[[name, virv = @vocabulary.identifying_role_values]]
1174
+ player = @constellation.Concept[[virv = @vocabulary.identifying_role_values, name]]
1177
1175
 
1178
1176
  # REVISIT: Hack to allow facts to refer to standard types that will be imported from standard vocabulary:
1179
1177
  if !player && %w{Date DateAndTime Time}.include?(name)
1180
- player = @constellation.ValueType(name, virv)
1178
+ player = @constellation.ValueType(virv, name)
1181
1179
  end
1182
1180
 
1183
1181
  if !player && allowed_forward
1184
- player = @constellation.EntityType(name, @vocabulary)
1182
+ player = @constellation.EntityType(@vocabulary, name)
1185
1183
  end
1186
1184
 
1187
1185
  player
@@ -101,11 +101,11 @@ module ActiveFacts
101
101
  entity_types <<
102
102
  @by_id[id] =
103
103
  entity_type =
104
- @constellation.EntityType(name, @vocabulary)
104
+ @constellation.EntityType(@vocabulary, name)
105
105
  independent = x.attributes['IsIndependent']
106
106
  entity_type.is_independent = true if independent && independent == 'true'
107
107
  personal = x.attributes['IsPersonal']
108
- entity_type.is_personal = true if personal && personal == 'true'
108
+ entity_type.pronoun = 'personal' if personal && personal == 'true'
109
109
  # x_pref = x.elements.to_a("orm:PreferredIdentifier")[0]
110
110
  # if x_pref
111
111
  # pi_id = x_pref.attributes['ref']
@@ -139,26 +139,26 @@ module ActiveFacts
139
139
  length = 32 if type_name =~ /Integer\Z/ && length.to_i == 0 # Set default integer length
140
140
 
141
141
  # REVISIT: Need to handle standard types better here:
142
- data_type = type_name != name ? @constellation.ValueType(type_name, @vocabulary) : nil
142
+ data_type = type_name != name ? @constellation.ValueType(@vocabulary, type_name) : nil
143
143
 
144
144
  # puts "ValueType #{name} is #{id}"
145
145
  value_types <<
146
146
  @by_id[id] =
147
- vt = @constellation.ValueType(name, @vocabulary)
147
+ vt = @constellation.ValueType(@vocabulary, name)
148
148
  vt.supertype = data_type
149
149
  vt.length = length if length
150
150
  vt.scale = scale if scale
151
151
  independent = x.attributes['IsIndependent']
152
152
  vt.is_independent = true if independent && independent == 'true'
153
153
  personal = x.attributes['IsPersonal']
154
- vt.is_personal = true if personal && personal == 'true'
154
+ vt.pronoun = 'personal' if personal && personal == 'true'
155
155
 
156
156
  x_ranges = x.elements.to_a("orm:ValueRestriction/orm:ValueConstraint/orm:ValueRanges/orm:ValueRange")
157
157
  next if x_ranges.size == 0
158
158
  vt.value_restriction = @constellation.ValueRestriction(:new)
159
159
  x_ranges.each{|x_range|
160
160
  v_range = value_range(x_range)
161
- ar = @constellation.AllowedRange(v_range, vt.value_restriction)
161
+ ar = @constellation.AllowedRange(vt.value_restriction, v_range)
162
162
  }
163
163
  }
164
164
  end
@@ -317,7 +317,7 @@ module ActiveFacts
317
317
  #puts "NestedType #{name} is #{id}, nests #{fact_type.fact_type_id}"
318
318
  nested_types <<
319
319
  @by_id[id] =
320
- nested_type = @constellation.EntityType(name, @vocabulary)
320
+ nested_type = @constellation.EntityType(@vocabulary, name)
321
321
  nested_type.fact_type = fact_type
322
322
  end
323
323
  }
@@ -390,7 +390,7 @@ module ActiveFacts
390
390
  role.role_value_restriction = @constellation.ValueRestriction(:new)
391
391
  x_ranges.each{|x_range|
392
392
  v_range = value_range(x_range)
393
- ar = @constellation.AllowedRange(v_range, role.role_value_restriction)
393
+ ar = @constellation.AllowedRange(role.role_value_restriction, v_range)
394
394
  }
395
395
  }
396
396
 
@@ -632,8 +632,8 @@ module ActiveFacts
632
632
  # A TypeInheritance fact type has a uniqueness constraint on each role.
633
633
  # If this UC is on the supertype and identifies the subtype, it's preferred:
634
634
  is_supertype_constraint =
635
- roles.all_role_ref.size == 1 &&
636
- (role = roles.all_role_ref.only.role) &&
635
+ (rr = roles.all_role_ref.single) &&
636
+ (role = rr.role) &&
637
637
  (fact_type = role.fact_type) &&
638
638
  fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance) &&
639
639
  role.concept == fact_type.supertype &&
@@ -649,8 +649,7 @@ module ActiveFacts
649
649
  pc.is_preferred_identifier = true if pi || unary_identifier || is_supertype_constraint
650
650
  #puts "#{name} covers #{roles.describe} has min=#{pc.min_frequency}, max=1, preferred=#{pc.is_preferred_identifier.inspect}" if emit_special_debug
651
651
 
652
- #puts roles.verbalise
653
- #puts pc.verbalise
652
+ #puts roles.all_role_ref.to_a[0].role.fact_type.describe + " is subject to " + pc.describe if roles.all_role_ref.all?{|r| r.role.fact_type.is_a? ActiveFacts::Metamodel::TypeInheritance }
654
653
 
655
654
  (@constraints_by_rs[roles] ||= []) << pc
656
655
  }
@@ -3,8 +3,13 @@
3
3
  #
4
4
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
5
5
  #
6
+
7
+ # These files are concerned with calculating a relational schema for a vocabulary:
6
8
  require 'activefacts/persistence/reference'
7
9
  require 'activefacts/persistence/tables'
8
10
  require 'activefacts/persistence/columns'
9
11
  require 'activefacts/persistence/foreignkey'
10
12
  require 'activefacts/persistence/index'
13
+
14
+ # These extend the API classes with relational awareness:
15
+ require 'activefacts/persistence/concept'