activefacts 0.8.9 → 0.8.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. data/spec/spec.opts +0 -1
@@ -1,20 +1,20 @@
1
1
  #
2
2
  # ActiveFacts Relational mapping and persistence.
3
- # A ForeignKey exists for every Reference from a Concept to another Concept that's a table.
3
+ # A ForeignKey exists for every Reference from a ObjectType to another ObjectType that's a table.
4
4
  #
5
5
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
7
  module ActiveFacts
8
8
  module Persistence
9
9
  class ForeignKey
10
- # What table (Concept) is the FK from?
10
+ # What table (ObjectType) is the FK from?
11
11
  def from; @from; end
12
12
 
13
- # What table (Concept) is the FK to?
13
+ # What table (ObjectType) is the FK to?
14
14
  def to; @to; end
15
15
 
16
16
  # What reference created the FK?
17
- def reference; @reference; end
17
+ def reference; @fk_ref; end
18
18
 
19
19
  # What columns in the *from* table form the FK
20
20
  def from_columns; @from_columns; end
@@ -30,7 +30,7 @@ module ActiveFacts
30
30
  end
31
31
 
32
32
  module Metamodel #:nodoc:
33
- class Concept
33
+ class ObjectType
34
34
  # When an EntityType is fully absorbed, its foreign keys are too.
35
35
  # Return an Array of Reference paths for such absorbed FKs
36
36
  def all_absorbed_foreign_key_reference_path
@@ -61,7 +61,7 @@ module ActiveFacts
61
61
  fk_ref_paths.map do |fk_ref_path|
62
62
  debug :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
63
63
 
64
- from_columns = columns.select{|column|
64
+ from_columns = (columns||all_columns({})).select{|column|
65
65
  column.references[0...fk_ref_path.size] == fk_ref_path
66
66
  }
67
67
  debug :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # ActiveFacts Relational mapping and persistence.
3
- # An Index on a Concept is used to represent a unique constraint across roles absorbed
4
- # into that concept's table.
3
+ # An Index on a ObjectType is used to represent a unique constraint across roles absorbed
4
+ # into that object_type's table.
5
5
  #
6
6
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
7
7
  #
@@ -16,7 +16,7 @@ module ActiveFacts
16
16
  def on; @on; end
17
17
 
18
18
  # If a non-mandatory reference was absorbed, only the non-nil instances are unique.
19
- # Return the Concept that was absorbed, which might differ from this Index's table.
19
+ # Return the ObjectType that was absorbed, which might differ from this Index's table.
20
20
  def over; @over; end
21
21
 
22
22
  # Return the array of columns in this index
@@ -78,7 +78,7 @@ module ActiveFacts
78
78
  end
79
79
 
80
80
  module Metamodel #:nodoc:
81
- class Concept
81
+ class ObjectType
82
82
  # An array of each Index for this table
83
83
  def indices; @indices; end
84
84
 
@@ -181,23 +181,23 @@ module ActiveFacts
181
181
 
182
182
  class Vocabulary
183
183
  def populate_all_indices #:nodoc:
184
- debug :index, "Populating all concept indices" do
185
- all_concept.each do |concept|
186
- concept.clear_indices
184
+ debug :index, "Populating all object_type indices" do
185
+ all_object_type.each do |object_type|
186
+ object_type.clear_indices
187
187
  end
188
- all_concept.each do |concept|
189
- next unless concept.is_table
190
- debug :index, "Populating indices for #{concept.name}" do
191
- concept.populate_indices
188
+ all_object_type.each do |object_type|
189
+ next unless object_type.is_table
190
+ debug :index, "Populating indices for #{object_type.name}" do
191
+ object_type.populate_indices
192
192
  end
193
193
  end
194
194
  end
195
- debug :index, "Finished concept indices" do
196
- all_concept.each do |concept|
197
- next unless concept.is_table
198
- next unless concept.indices.size > 0
199
- debug :index, "#{concept.name}:" do
200
- concept.indices.each do |index|
195
+ debug :index, "Finished object_type indices" do
196
+ all_object_type.each do |object_type|
197
+ next unless object_type.is_table
198
+ next unless object_type.indices.size > 0
199
+ debug :index, "#{object_type.name}:" do
200
+ object_type.indices.each do |index|
201
201
  debug :index, index
202
202
  end
203
203
  end
@@ -2,7 +2,7 @@ require 'activefacts/support'
2
2
 
3
3
  module ActiveFacts
4
4
  module API
5
- module Concept
5
+ module ObjectType
6
6
  def table
7
7
  @is_table = true
8
8
  end
@@ -29,7 +29,7 @@ module ActiveFacts
29
29
  end.inject([]) do |columns, role|
30
30
  rn = role.name.to_s.split(/_/)
31
31
  debug :persistence, "Role #{rn*'.'}" do
32
- columns += role.counterpart_concept.__absorb([rn], role.counterpart)
32
+ columns += role.counterpart_object_type.__absorb([rn], role.counterpart)
33
33
  end
34
34
  end +
35
35
  # And finally all absorbed subtypes:
@@ -65,11 +65,11 @@ module ActiveFacts
65
65
  if (role = fully_absorbed) && role != except_role
66
66
  # If this non-table is fully absorbed into another table (not our caller!)
67
67
  # (another table plays its single identifying role), then absorb that role only.
68
- # counterpart_concept = role.counterpart_concept
68
+ # counterpart_object_type = role.counterpart_object_type
69
69
  # This omission matches the one in columns.rb, see EntityType#reference_columns
70
70
  # new_prefix = prefix + [role.name.to_s.split(/_/)]
71
71
  debug :persistence, "Reference to #{role.name} (absorbed elsewhere)" do
72
- role.counterpart_concept.__absorb(prefix, role.counterpart)
72
+ role.counterpart_object_type.__absorb(prefix, role.counterpart)
73
73
  end
74
74
  else
75
75
  # Not a table -> all roles are absorbed
@@ -125,7 +125,7 @@ module ActiveFacts
125
125
  debug :persistence, "truncating transitive identifying role #{n.inspect}"
126
126
  owner.size.times { n.shift }
127
127
  new_prefix = prefix + [n]
128
- elsif (c = role.counterpart_concept).is_entity_type and
128
+ elsif (c = role.counterpart_object_type).is_entity_type and
129
129
  (irn = c.identifying_role_names).size == 1 and
130
130
  #irn[0].to_s.split(/_/)[0] == role.owner.basename.downcase
131
131
  irn[0] == role.counterpart.name
@@ -139,7 +139,7 @@ module ActiveFacts
139
139
  #debug :persistence, "new_prefix is #{new_prefix*"."}"
140
140
 
141
141
  debug :persistence, "Absorbing role #{role.name} as #{new_prefix[prefix.size..-1]*"."}" do
142
- role.counterpart_concept.__absorb(new_prefix, role.counterpart)
142
+ role.counterpart_object_type.__absorb(new_prefix, role.counterpart)
143
143
  end
144
144
  end
145
145
 
@@ -159,7 +159,7 @@ module ActiveFacts
159
159
  def fully_absorbed
160
160
  return false unless (ir = identifying_role_names) && ir.size == 1
161
161
  role = roles(ir[0])
162
- return role if ((cp = role.counterpart_concept).is_table ||
162
+ return role if ((cp = role.counterpart_object_type).is_table ||
163
163
  (cp.is_entity_type && cp.fully_absorbed))
164
164
  return superclass if superclass.is_entity_type # Absorbed subtype
165
165
  nil
@@ -168,10 +168,10 @@ module ActiveFacts
168
168
  end
169
169
 
170
170
  # A one-to-one can be absorbed into either table. We decide which by comparing
171
- # the names, just as happens in Concept.populate_reference (see reference.rb)
171
+ # the names, just as happens in ObjectType.populate_reference (see reference.rb)
172
172
  class Role
173
173
  def counterpart_unary_has_precedence
174
- counterpart_concept.is_table_subtype and
174
+ counterpart_object_type.is_table_subtype and
175
175
  counterpart.unique and
176
176
  owner.name.downcase < counterpart.owner.name.downcase
177
177
  end
@@ -1,18 +1,18 @@
1
1
  #
2
2
  # ActiveFacts Relational mapping and persistence.
3
- # Reference from one Concept to another, used to decide the relational mapping.
3
+ # Reference from one ObjectType to another, used to decide the relational mapping.
4
4
  #
5
5
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
6
  #
7
- # A Reference from one Concept to another is created for each many-1 or 1-1 relationship
8
- # (including subtyping), and also for a unary role (implicitly to Boolean concept).
7
+ # A Reference from one ObjectType to another is created for each many-1 or 1-1 relationship
8
+ # (including subtyping), and also for a unary role (implicitly to Boolean object_type).
9
9
  # A 1-1 or subtyping reference should be created in only one direction, and may be flipped
10
10
  # if needed.
11
11
  #
12
- # A reference to a concept that's a table or is fully absorbed into a table will
13
- # become a foreign key, otherwise it will absorb all that concept's references.
12
+ # A reference to a object_type that's a table or is fully absorbed into a table will
13
+ # become a foreign key, otherwise it will absorb all that object_type's references.
14
14
  #
15
- # Reference objects update each concept's list of the references *to* and *from* that concept.
15
+ # Reference objects update each object_type's list of the references *to* and *from* that object_type.
16
16
  #
17
17
  # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
18
18
  #
@@ -22,9 +22,9 @@ module ActiveFacts
22
22
 
23
23
  # This class contains the core data structure used in composing a relational schema.
24
24
  #
25
- # A Reference is *from* one Concept *to* another Concept, and relates to the *from_role* and the *to_role*.
26
- # When either Concept is an objectified fact type, the corresponding role is nil.
27
- # When the Reference from_role is of a unary fact type, there's no to_role or to Concept.
25
+ # A Reference is *from* one ObjectType *to* another ObjectType, and relates to the *from_role* and the *to_role*.
26
+ # When either ObjectType is an objectified fact type, the corresponding role is nil.
27
+ # When the Reference from_role is of a unary fact type, there's no to_role or to ObjectType.
28
28
  # The final kind of Reference is a self-reference which is added to a ValueType that becomes a table.
29
29
  #
30
30
  # When the underlying fact type is a one-to-one (including an inheritance fact type), the Reference may be flipped.
@@ -40,7 +40,7 @@ module ActiveFacts
40
40
  attr_reader :from_role, :to_role # For objectified facts, one role will be nil (a phantom)
41
41
  attr_reader :fact_type
42
42
 
43
- # A Reference is created from a concept in regard to a role it plays
43
+ # A Reference is created from a object_type in regard to a role it plays
44
44
  def initialize(from, role)
45
45
  @from = from
46
46
  return unless role # All done if it's a self-value reference for a ValueType
@@ -52,14 +52,14 @@ module ActiveFacts
52
52
  elsif (role.fact_type.entity_type == @from) # role is in "from", an objectified fact type
53
53
  @from_role = nil # Phantom role
54
54
  @to_role = role
55
- @to = @to_role.concept
55
+ @to = @to_role.object_type
56
56
  else
57
57
  @from_role = role
58
58
  @to = role.fact_type.entity_type # If set, to_role is a phantom
59
59
  unless @to
60
60
  raise "Illegal reference through >binary fact type" if @fact_type.all_role.size >2
61
61
  @to_role = (role.fact_type.all_role-[role])[0]
62
- @to = @to_role.concept
62
+ @to = @to_role.object_type
63
63
  end
64
64
  end
65
65
  end
@@ -98,7 +98,7 @@ module ActiveFacts
98
98
  !@to && !@to_role
99
99
  end
100
100
 
101
- # Is the *to* concept fully absorbed through this reference?
101
+ # Is the *to* object_type fully absorbed through this reference?
102
102
  def is_absorbing
103
103
  @to && @to.absorbed_via == self
104
104
  end
@@ -127,7 +127,28 @@ module ActiveFacts
127
127
  @to_role.role_name.camelwords
128
128
  else # Use the name from the preferred reading
129
129
  role_ref = @to_role.preferred_reference
130
- [role_ref.leading_adjective, @to_role.concept.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
130
+ [role_ref.leading_adjective, @to_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
131
+ end
132
+ end
133
+
134
+ # Return the array of names for the (perhaps implicit) *from_role* of this Reference
135
+ def from_names
136
+ case
137
+ when is_unary
138
+ if @from && @from.fact_type
139
+ @from.name.camelwords
140
+ else
141
+ @from_role.fact_type.preferred_reading.text.gsub(/\{[0-9]\}/,'').strip.camelwords
142
+ end
143
+ when @from && !@from_role # @from is an objectified fact type so @from_role is a phantom
144
+ @from.name.camelwords
145
+ when !@from_role # Self-value role of an independent ValueType
146
+ @from.name.camelwords + ["Value"]
147
+ when @from_role.role_name # Named role
148
+ @from_role.role_name.camelwords
149
+ else # Use the name from the preferred reading
150
+ role_ref = @from_role.preferred_reference
151
+ [role_ref.leading_adjective, @from_role.object_type.name, role_ref.trailing_adjective].compact.map{|w| w.camelwords}.flatten.reject{|s| s == ''}
131
152
  end
132
153
  end
133
154
 
@@ -172,7 +193,7 @@ module ActiveFacts
172
193
 
173
194
  # The reading for the fact type underlying this Reference
174
195
  def reading
175
- is_self_value ? "#{from.name} has value" : @fact_type.default_reading([], true) # Include role name defn's
196
+ is_self_value ? "#{from.name} has value" : @fact_type.default_reading
176
197
  end
177
198
 
178
199
  def inspect #:nodoc:
@@ -182,11 +203,11 @@ module ActiveFacts
182
203
  end
183
204
 
184
205
  module Metamodel #:nodoc:
185
- class Concept
206
+ class ObjectType
186
207
  # Say whether the independence of this object is still under consideration
187
208
  # This is used in detecting dependency cycles, such as occurs in the Metamodel
188
209
  attr_accessor :tentative #:nodoc:
189
- attr_writer :is_table # The two Concept subclasses provide the attr_reader method
210
+ attr_writer :is_table # The two ObjectType subclasses provide the attr_reader method
190
211
 
191
212
  def show_tabular #:nodoc:
192
213
  (tentative ? "tentatively " : "") +
@@ -213,17 +234,17 @@ module ActiveFacts
213
234
  @tentative = true
214
235
  end
215
236
 
216
- # References from this Concept
237
+ # References from this ObjectType
217
238
  def references_from
218
239
  @references_from ||= []
219
240
  end
220
241
 
221
- # References to this Concept
242
+ # References to this ObjectType
222
243
  def references_to
223
244
  @references_to ||= []
224
245
  end
225
246
 
226
- # True if this Concept has any References (to or from)
247
+ # True if this ObjectType has any References (to or from)
227
248
  def has_references #:nodoc:
228
249
  @references_from || @references_to
229
250
  end
@@ -236,7 +257,11 @@ module ActiveFacts
236
257
 
237
258
  def populate_references #:nodoc:
238
259
  all_role.each do |role|
239
- populate_reference role unless role.fact_type.is_a?(ImplicitFactType)
260
+ # It's possible that this role is in an implicit or derived fact type. Skip it if so.
261
+ next if role.fact_type.is_a?(ImplicitFactType) or
262
+ role.fact_type.preferred_reading.role_sequence.all_role_ref.to_a[0].join_role
263
+
264
+ populate_reference role
240
265
  end
241
266
  end
242
267
 
@@ -321,12 +346,12 @@ module ActiveFacts
321
346
  r.tabulate
322
347
  end
323
348
  else
324
- raise "Illegal role type, #{role.fact_type.describe(role)} no uniqueness constraint"
349
+ raise "Role #{role.object_type.name} in '#{role.fact_type.default_reading}' lacks a uniqueness constraint"
325
350
  end
326
351
  end
327
352
  end
328
353
 
329
- class EntityType < Concept
354
+ class EntityType < ObjectType
330
355
  def populate_references #:nodoc:
331
356
  if fact_type && fact_type.all_role.size > 1
332
357
  # NOT: fact_type.all_role.each do |role| # Place roles in the preferred order instead:
@@ -340,23 +365,23 @@ module ActiveFacts
340
365
 
341
366
  class Vocabulary
342
367
  def populate_all_references #:nodoc:
343
- debug :references, "Populating all concept references" do
344
- all_concept.each do |concept|
345
- concept.clear_references
346
- concept.is_table = nil # Undecided; force an attempt to decide
347
- concept.tentative = true # Uncertain
368
+ debug :references, "Populating all object_type references" do
369
+ all_object_type.each do |object_type|
370
+ object_type.clear_references
371
+ object_type.is_table = nil # Undecided; force an attempt to decide
372
+ object_type.tentative = true # Uncertain
348
373
  end
349
- all_concept.each do |concept|
350
- debug :references, "Populating references for #{concept.name}" do
351
- concept.populate_references
374
+ all_object_type.each do |object_type|
375
+ debug :references, "Populating references for #{object_type.name}" do
376
+ object_type.populate_references
352
377
  end
353
378
  end
354
379
  end
355
- debug :references, "Finished concept references" do
356
- all_concept.each do |concept|
357
- next unless concept.references_from.size > 0
358
- debug :references, "#{concept.name}:" do
359
- concept.references_from.each do |ref|
380
+ debug :references, "Finished object_type references" do
381
+ all_object_type.each do |object_type|
382
+ next unless object_type.references_from.size > 0
383
+ debug :references, "#{object_type.name}:" do
384
+ object_type.references_from.each do |ref|
360
385
  debug :references, "#{ref}"
361
386
  end
362
387
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # ActiveFacts Relational mapping and persistence.
3
3
  # Tables; Calculate the relational composition of a given Vocabulary.
4
- # The composition consists of decisions about which Concepts are tables,
4
+ # The composition consists of decisions about which ObjectTypes are tables,
5
5
  # and what columns (absorbed roled) those tables will have.
6
6
  #
7
7
  # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
@@ -17,7 +17,7 @@ require 'activefacts/persistence/reference'
17
17
  module ActiveFacts
18
18
  module Metamodel
19
19
 
20
- class ValueType < Concept
20
+ class ValueType < ObjectType
21
21
  def absorbed_via #:nodoc:
22
22
  # ValueTypes aren't absorbed in the way EntityTypes are
23
23
  nil
@@ -55,7 +55,7 @@ module ActiveFacts
55
55
  end
56
56
  end
57
57
 
58
- class EntityType < Concept
58
+ class EntityType < ObjectType
59
59
  # A Reference from an entity type that fully absorbs this one
60
60
  def absorbed_via; @absorbed_via; end
61
61
  def absorbed_via=(r) #:nodoc:
@@ -81,13 +81,15 @@ module ActiveFacts
81
81
  # Always a table if nowhere else to go, and has no one-to-ones that might flip:
82
82
  if references_to.empty? and
83
83
  !references_from.detect{|ref| ref.role_type == :one_one }
84
- debug :absorption, "EntityType #{name} is independent as it has nowhere to go"
84
+ debug :absorption, "EntityType #{name} is presumed independent as it has nowhere to go"
85
85
  return @is_table = true
86
86
  end
87
87
 
88
88
  # Subtypes are not a table unless partitioned or separate
89
89
  # REVISIT: Support partitioned subtypes here
90
90
  if (!supertypes.empty?)
91
+ as_ti = all_supertype_inheritance.detect{|ti| ti.assimilation}
92
+ debug :absorption, "EntityType #{name} is #{as_ti ? as_ti.assimilation+' from' : 'absorbed into'} supertype #{(as_ti ? as_ti.supertype : supertypes[0]).name}"
91
93
  return @is_table = all_supertype_inheritance.detect{|ti| ti.assimilation} != nil
92
94
  end
93
95
 
@@ -96,8 +98,8 @@ module ActiveFacts
96
98
  # to manage the auto-assignment.
97
99
  if references_to.size > 1 and
98
100
  preferred_identifier.role_sequence.all_role_ref.detect {|rr|
99
- next false unless rr.role.concept.is_a? ValueType
100
- rr.role.concept.is_auto_assigned
101
+ next false unless rr.role.object_type.is_a? ValueType
102
+ rr.role.object_type.is_auto_assigned
101
103
  }
102
104
  debug :absorption, "#{name} has an auto-assigned counter in its ID, so must be a table"
103
105
  @tentative = false
@@ -113,7 +115,7 @@ module ActiveFacts
113
115
  def role_type
114
116
  # TypeInheritance roles are always 1:1
115
117
  if TypeInheritance === fact_type
116
- return concept == fact_type.supertype ? :supertype : :subtype
118
+ return object_type == fact_type.supertype ? :supertype : :subtype
117
119
  end
118
120
 
119
121
  # Always N:1 if unary:
@@ -156,7 +158,7 @@ module ActiveFacts
156
158
  end
157
159
 
158
160
  class Vocabulary
159
- # return an Array of Concepts that will have their own tables
161
+ # return an Array of ObjectTypes that will have their own tables
160
162
  def tables
161
163
  decide_tables if !@tables
162
164
  @tables
@@ -164,9 +166,9 @@ module ActiveFacts
164
166
 
165
167
  def decide_tables #:nodoc:
166
168
  # Strategy:
167
- # 1) Populate references for all Concepts
168
- # 2) Decide which Concepts must be and must not be tables
169
- # a. Concepts labelled is_independent are tables (See the is_table methods above)
169
+ # 1) Populate references for all ObjectTypes
170
+ # 2) Decide which ObjectTypes must be and must not be tables
171
+ # a. ObjectTypes labelled is_independent are tables (See the is_table methods above)
170
172
  # b. Entity types having no references to them must be tables
171
173
  # c. subtypes are not tables unless marked with assimilation = separate or partitioned
172
174
  # d. ValueTypes are never tables unless they can have references (to other ValueTypes)
@@ -181,18 +183,18 @@ module ActiveFacts
181
183
  populate_all_references
182
184
 
183
185
  debug :absorption, "Calculating relational composition" do
184
- # Evaluate the possible independence of each concept, building an array of concepts of indeterminate status:
186
+ # Evaluate the possible independence of each object_type, building an array of object_types of indeterminate status:
185
187
  undecided =
186
- all_concept.select do |concept|
187
- concept.is_table # Ask it whether it thinks it should be a table
188
- concept.tentative # Selection criterion
188
+ all_object_type.select do |object_type|
189
+ object_type.is_table # Ask it whether it thinks it should be a table
190
+ object_type.tentative # Selection criterion
189
191
  end
190
192
 
191
- if debug :absorption, "Generating tables, #{undecided.size} undecided"
192
- (all_concept-undecided).each {|concept|
193
- next if ValueType === concept && !concept.is_table # Skip unremarkable cases
193
+ if debug :absorption, "Generating tables, #{undecided.size} undecided, already decided ones are"
194
+ (all_object_type-undecided).each {|object_type|
195
+ next if ValueType === object_type && !object_type.is_table # Skip unremarkable cases
194
196
  debug :absorption do
195
- debug :absorption, "#{concept.name} is #{concept.is_table ? "" : "not "}a table#{concept.tentative ? ", tentatively" : ""}"
197
+ debug :absorption, "#{object_type.name} is #{object_type.is_table ? "" : "not "}a table#{object_type.tentative ? ", tentatively" : ""}"
196
198
  end
197
199
  }
198
200
  end
@@ -203,50 +205,50 @@ module ActiveFacts
203
205
  debug :absorption, "Starting composition pass #{pass} with #{undecided.size} undecided tables"
204
206
  possible_flips = {} # A hash by table containing an array of references that can be flipped
205
207
  finalised = # Make an array of things we finalised during this pass
206
- undecided.select do |concept|
207
- debug :absorption, "Considering #{concept.name}:" do
208
- debug :absorption, "refs to #{concept.name} are from #{concept.references_to.map{|ref| ref.from.name}*", "}" if concept.references_to.size > 0
209
- debug :absorption, "refs from #{concept.name} are to #{concept.references_from.map{|ref| ref.to.name rescue ref.fact_type.default_reading}*", "}" if concept.references_from.size > 0
208
+ undecided.select do |object_type|
209
+ debug :absorption, "Considering #{object_type.name}:" do
210
+ debug :absorption, "refs to #{object_type.name} are from #{object_type.references_to.map{|ref| ref.from.name}*", "}" if object_type.references_to.size > 0
211
+ debug :absorption, "refs from #{object_type.name} are to #{object_type.references_from.map{|ref| ref.to.name rescue ref.fact_type.default_reading}*", "}" if object_type.references_from.size > 0
210
212
 
211
213
  # Always absorb an objectified unary into its role player:
212
- if concept.fact_type && concept.fact_type.all_role.size == 1
213
- debug :absorption, "Absorb objectified unary #{concept.name} into #{concept.fact_type.entity_type.name}"
214
- concept.definitely_not_table
215
- next concept
214
+ if object_type.fact_type && object_type.fact_type.all_role.size == 1
215
+ debug :absorption, "Absorb objectified unary #{object_type.name} into #{object_type.fact_type.entity_type.name}"
216
+ object_type.definitely_not_table
217
+ next object_type
216
218
  end
217
219
 
218
220
  # If the PI contains one role only, played by an entity type that can absorb us, do that.
219
- pi_roles = concept.preferred_identifier.role_sequence.all_role_ref.map(&:role)
220
- debug :absorption, "pi_roles are played by #{pi_roles.map{|role| role.concept.name}*", "}"
221
+ pi_roles = object_type.preferred_identifier.role_sequence.all_role_ref.map(&:role)
222
+ debug :absorption, "pi_roles are played by #{pi_roles.map{|role| role.object_type.name}*", "}"
221
223
  first_pi_role = pi_roles[0]
222
224
  pi_ref = nil
223
225
  if pi_roles.size == 1 and
224
- concept.references_to.detect{|ref| pi_ref = ref if ref.from_role == first_pi_role && ref.from.is_a?(EntityType)}
226
+ object_type.references_to.detect{|ref| pi_ref = ref if ref.from_role == first_pi_role && ref.from.is_a?(EntityType)}
225
227
 
226
- debug :absorption, "#{concept.name} is fully absorbed along its sole reference path into entity type #{pi_ref.from.name}"
227
- concept.definitely_not_table
228
- next concept
228
+ debug :absorption, "#{object_type.name} is fully absorbed along its sole reference path into entity type #{pi_ref.from.name}"
229
+ object_type.definitely_not_table
230
+ next object_type
229
231
  end
230
232
 
231
233
  # If there's more than one absorption path and any functional dependencies that can't absorb us, it's a table
232
234
  non_identifying_refs_from =
233
- concept.references_from.reject{|ref|
235
+ object_type.references_from.reject{|ref|
234
236
  pi_roles.include?(ref.to_role)
235
237
  }
236
- debug :absorption, "#{concept.name} has #{non_identifying_refs_from.size} non-identifying functional roles"
238
+ debug :absorption, "#{object_type.name} has #{non_identifying_refs_from.size} non-identifying functional roles"
237
239
 
238
- if concept.references_to.size > 1 and
240
+ if object_type.references_to.size > 1 and
239
241
  non_identifying_refs_from.size > 0
240
- debug :absorption, "#{concept.name} has non-identifying functional dependencies so 3NF requires it be a table"
241
- concept.definitely_table
242
- next concept
242
+ debug :absorption, "#{object_type.name} has non-identifying functional dependencies so 3NF requires it be a table"
243
+ object_type.definitely_table
244
+ next object_type
243
245
  end
244
246
 
245
247
  absorption_paths =
246
248
  (
247
249
  non_identifying_refs_from.reject do |ref|
248
250
  !ref.to or ref.to.absorbed_via == ref
249
- end+concept.references_to
251
+ end+object_type.references_to
250
252
  ).reject do |ref|
251
253
  next true if !ref.to.is_table or
252
254
  ![:one_one, :supertype, :subtype].include?(ref.role_type)
@@ -255,29 +257,29 @@ module ActiveFacts
255
257
  from_is_mandatory = !!ref.is_mandatory
256
258
  to_is_mandatory = !ref.to_role || !!ref.to_role.is_mandatory
257
259
 
258
- bad = !(ref.from == concept ? from_is_mandatory : to_is_mandatory)
259
- debug :absorption, "Not absorbing #{concept.name} through non-mandatory #{ref}" if bad
260
+ bad = !(ref.from == object_type ? from_is_mandatory : to_is_mandatory)
261
+ debug :absorption, "Not absorbing #{object_type.name} through non-mandatory #{ref}" if bad
260
262
  bad
261
263
  end
262
264
 
263
265
  # If this object can be fully absorbed, do that (might require flipping some references)
264
266
  if absorption_paths.size > 0
265
- debug :absorption, "#{concept.name} is fully absorbed through #{absorption_paths.inspect}"
267
+ debug :absorption, "#{object_type.name} is fully absorbed through #{absorption_paths.inspect}"
266
268
  absorption_paths.each do |ref|
267
- debug :absorption, "flip #{ref} so #{concept.name} can be absorbed"
268
- ref.flip if concept == ref.from
269
+ debug :absorption, "flip #{ref} so #{object_type.name} can be absorbed"
270
+ ref.flip if object_type == ref.from
269
271
  end
270
- concept.definitely_not_table
271
- next concept
272
+ object_type.definitely_not_table
273
+ next object_type
272
274
  end
273
275
 
274
276
  if non_identifying_refs_from.size == 0
275
- # and (!concept.is_a?(EntityType) ||
277
+ # and (!object_type.is_a?(EntityType) ||
276
278
  # # REVISIT: The roles may be collectively but not individually mandatory.
277
- # concept.references_to.detect { |ref| !ref.from_role || ref.from_role.is_mandatory })
278
- debug :absorption, "#{concept.name} is fully absorbed in #{concept.references_to.size} places: #{concept.references_to.map{|ref| ref.from.name}*", "}"
279
- concept.definitely_not_table
280
- next concept
279
+ # object_type.references_to.detect { |ref| !ref.from_role || ref.from_role.is_mandatory })
280
+ debug :absorption, "#{object_type.name} is fully absorbed in #{object_type.references_to.size} places: #{object_type.references_to.map{|ref| ref.from.name}*", "}"
281
+ object_type.definitely_not_table
282
+ next object_type
281
283
  end
282
284
 
283
285
  false # Failed to decide about this entity_type this time around
@@ -290,18 +292,18 @@ module ActiveFacts
290
292
 
291
293
  # A ValueType that isn't explicitly a table and isn't needed anywhere doesn't matter,
292
294
  # unless it should absorb something else (another ValueType is all it could be):
293
- all_concept.each do |concept|
294
- if (!concept.is_table and concept.references_to.size == 0 and concept.references_from.size > 0)
295
- debug :absorption, "Making #{concept.name} a table; it has nowhere else to go and needs to absorb things"
296
- concept.probably_table
295
+ all_object_type.each do |object_type|
296
+ if (!object_type.is_table and object_type.references_to.size == 0 and object_type.references_from.size > 0)
297
+ debug :absorption, "Making #{object_type.name} a table; it has nowhere else to go and needs to absorb things"
298
+ object_type.probably_table
297
299
  end
298
300
  end
299
301
 
300
302
  # Now, evaluate all possibilities of the tentative assignments
301
303
  # Incomplete. Apparently unnecessary as well... so far. We'll see.
302
304
  if debug :absorption
303
- undecided.each do |concept|
304
- debug :absorption, "Unable to decide independence of #{concept.name}, going with #{concept.show_tabular}"
305
+ undecided.each do |object_type|
306
+ debug :absorption, "Unable to decide independence of #{object_type.name}, going with #{object_type.show_tabular}"
305
307
  end
306
308
  end
307
309
  end
@@ -310,7 +312,7 @@ module ActiveFacts
310
312
  populate_all_indices
311
313
 
312
314
  @tables =
313
- all_concept.
315
+ all_object_type.
314
316
  select { |f| f.is_table }.
315
317
  sort_by { |table| table.name }
316
318
  end