activefacts 0.8.9 → 0.8.10

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.
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