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
@@ -0,0 +1,89 @@
1
+ #
2
+ # ActiveFacts tests: Test the CQL parser by looking at its parse trees.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+
6
+ require 'activefacts/cql'
7
+ require 'activefacts/support'
8
+ require 'activefacts/api/support'
9
+ require 'helpers/test_parser'
10
+
11
+ describe "Entity Types" do
12
+ IndependentObjectTypes = [
13
+ # Value types
14
+ [ "a is written as b [independent];",
15
+ ['ValueType: a is written as b, pragmas [independent];'],
16
+ ],
17
+ [ "a [independent] is written as b;",
18
+ ['ValueType: a is written as b, pragmas [independent];'],
19
+ ],
20
+
21
+ # Entity types
22
+ [ "a is identified by its id [independent];",
23
+ ['EntityType: a identified by its id, pragmas [independent];']
24
+ ],
25
+ [ "a [independent] is identified by its id;",
26
+ ['EntityType: a identified by its id, pragmas [independent];']
27
+ ],
28
+ [ "a is independent identified by its id;",
29
+ ['EntityType: a identified by its id, pragmas [independent];']
30
+ ],
31
+ [ "a is identified by b [independent] where a has one b;",
32
+ ['EntityType: a [{b}] where [{a} "has" {[1..1] b}], pragmas [independent];']
33
+ ],
34
+ [ "a is independent identified by b where a has one b;",
35
+ ['EntityType: a [{b}] where [{a} "has" {[1..1] b}], pragmas [independent];']
36
+ ],
37
+
38
+ # Subtypes
39
+ [ "Employee [independent] is a kind of Person;",
40
+ ['EntityType: Employee < Person nil, pragmas [independent];']
41
+ ],
42
+ [ "Employee is a kind of Person [independent];",
43
+ ['EntityType: Employee < Person nil, pragmas [independent];']
44
+ ],
45
+ [ "Employee is a kind of independent Person;",
46
+ ['EntityType: Employee < Person nil, pragmas [independent];']
47
+ ],
48
+ [ "Employee is a kind of Person identified by its id [independent];",
49
+ ["EntityType: Employee < Person identified by its id, pragmas [independent];"]
50
+ ],
51
+
52
+ # Fact Types
53
+ [ "Director is where c relates to b;",
54
+ ["FactType: Director [{c} \"relates to\" {b}]"]
55
+ ],
56
+ [ "Director [independent] is where c relates to b;",
57
+ ["FactType: Director [{c} \"relates to\" {b}], pragmas [independent]"]
58
+ ],
59
+ [ "Director is independent where c relates to b;",
60
+ ["FactType: Director [{c} \"relates to\" {b}], pragmas [independent]"]
61
+ ],
62
+ ]
63
+
64
+ PragmaObjectTypes =
65
+ IndependentObjectTypes
66
+
67
+ before :each do
68
+ @parser = TestParser.new
69
+ @parser.parse_all("c is written as b;", :definition)
70
+ end
71
+
72
+ PragmaObjectTypes.each do |c|
73
+ source, ast = *c
74
+ it "should parse #{source.inspect}" do
75
+ result = @parser.parse_all(source, :definition)
76
+
77
+ puts @parser.failure_reason unless result
78
+ result.should_not be_nil
79
+
80
+ canonical_form = result.map{|d| d.ast.to_s}
81
+ if ast
82
+ canonical_form.should == ast
83
+ else
84
+ pending "#{source.inspect} should compile to\n" +
85
+ "\t#{canonical_form}"
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # ActiveFacts tests: Test the CQL parser by looking at its parse trees.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+
6
+ require 'activefacts/cql'
7
+ require 'activefacts/support'
8
+ require 'activefacts/api/support'
9
+ require 'helpers/test_parser'
10
+
11
+ describe "Value Types" do
12
+ ValueTypes = [
13
+ [ "a is written as b(1, 2) inch restricted to { 3 .. 4 } inch ;",
14
+ ['ValueType: a is written as b(1, 2) in [["inch", 1]] ValueConstraint to ([3..4]) in [["inch", 1]];']
15
+ ],
16
+ # [ "a c is written as b(1, 2) inch restricted to { 3 .. 4 } inch ;",
17
+ # [["a c", [:value_type, "b", [1, 2], "inch", [[3, 4]]]]]
18
+ # ],
19
+ ]
20
+
21
+ before :each do
22
+ @parser = TestParser.new
23
+ end
24
+
25
+ ValueTypes.each do |c|
26
+ source, ast = *c
27
+ it "should parse #{source.inspect}" do
28
+ result = @parser.parse_all(source, :definition)
29
+
30
+ puts @parser.failure_reason unless result
31
+ result.should_not be_nil
32
+
33
+ canonical_form = result.map{|d| d.ast.to_s}
34
+ if ast
35
+ canonical_form.should == ast
36
+ else
37
+ puts "#{source.inspect} should compile to"
38
+ puts "\t#{canonical_form}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,147 @@
1
+ #
2
+ # ActiveFacts CQL Fact Type matching tests
3
+ # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
+ #
5
+
6
+ require 'rspec/expectations'
7
+
8
+ require 'activefacts/support'
9
+ require 'activefacts/api/support'
10
+ require 'activefacts/cql/compiler'
11
+ require 'spec/helpers/compile_helpers'
12
+
13
+ describe "When comparing roles of a reading with an existing reading" do
14
+ before :each do
15
+ extend CompileHelpers
16
+
17
+ prefix = %q{
18
+ vocabulary Tests;
19
+ Boy is written as String;
20
+ Girl is written as Integer;
21
+ }
22
+ @compiler = ActiveFacts::CQL::Compiler.new('Test')
23
+ @compiler.compile(prefix)
24
+ @constellation = @compiler.vocabulary.constellation
25
+
26
+ baseline
27
+ end
28
+
29
+ describe "producing correct side effects" do
30
+ before :each do
31
+ @compiler.compile %q{ Boy is going out with Girl; }
32
+ @simple_ft = fact_types[0]
33
+ baseline
34
+ @compiler.compile %q{ Boy dislikes ugly-Girl; }
35
+ @ugly_ft = fact_types[0]
36
+ baseline
37
+ @compiler.compile %q{ Boy -monster hurts Girl; }
38
+ @hurts_ft = fact_types[0]
39
+ baseline
40
+ #debug_enable("matching"); debug_enable("matching_fails"); debug_enable("parse")
41
+ end
42
+ after :each do
43
+ # debug_disable("matching"); debug_disable("matching_fails"); debug_disable("parse")
44
+ end
45
+
46
+ it "should match exact reading" do
47
+ parse %q{Boy is going out with Girl;}
48
+ @asts.size.should == 1
49
+ side_effects = match_readings_to_existing(@asts[0], @simple_ft.all_reading.single)
50
+ side_effects[0].should_not be_nil
51
+ side_effects.to_s.should == 'side-effects are [{Boy} absorbs 0/0 at 0, {Girl} absorbs 0/0 at 5]'
52
+ end
53
+
54
+ it "should match with explicit leading adjective" do
55
+ parse %q{Boy dislikes ugly-Girl;}
56
+ @asts.size.should == 1
57
+ side_effects = match_readings_to_existing(@asts[0], @ugly_ft.all_reading.single)
58
+ side_effects[0].should_not be_nil
59
+ side_effects.to_s.should == 'side-effects are [{Boy} absorbs 0/0 at 0, {ugly- Girl} absorbs 0/0 at 2]'
60
+ end
61
+
62
+ it "should match with implicit leading adjective" do
63
+ parse %q{Boy dislikes ugly Girl;}
64
+ @asts.size.should == 1
65
+ side_effects = match_readings_to_existing(@asts[0], @ugly_ft.all_reading.single)
66
+ side_effects[0].should_not be_nil
67
+ side_effects.to_s.should == 'side-effects are [{Boy} absorbs 0/0 at 0, {Girl} absorbs 1/0 at 3]'
68
+ end
69
+
70
+ it "should match with local leading adjective" do
71
+ parse %q{
72
+ bad-Boy is going out with Girl,
73
+ Girl likes bad-Boy; // New reading, no match expected, but must use residual adjective
74
+ }
75
+ @asts.size.should == 1
76
+ side_effects = match_readings_to_existing(@asts[0], @simple_ft.all_reading.single)
77
+ side_effects.size.should == 2
78
+ side_effects[0].should_not be_nil
79
+ side_effects[1].should be_nil
80
+ pending side_effects[0].to_s
81
+ side_effects[0].to_s.should == '[{bad- Boy} absorbs 0/0 at 0 with residual adjectives, {Girl} absorbs 0/0 at 5] with residual adjectives'
82
+ #side_effects[1].to_s.should == ''
83
+ end
84
+
85
+ it "should match with explicit and local leading adjective" do
86
+ parse %q{
87
+ Boy dislikes nasty- ugly Girl,
88
+ nasty- Girl likes Boy; // New reading, no match expected, but must use residual adjective
89
+ }
90
+ @asts.size.should == 1
91
+ side_effects = match_readings_to_existing(@asts[0], @ugly_ft.all_reading.single)
92
+ side_effects.size.should == 2
93
+ pending "Matched adjectives must be removed and the role rebound before deciding whether residual adjectives have a purpose" do
94
+ side_effects[0].should_not be_nil
95
+ puts side_effects[0].to_s
96
+ #side_effects.to_s.should == ''
97
+ end
98
+ end
99
+
100
+ # Trailing adjectives
101
+ it "should match with explicit trailing adjective" do
102
+ parse %q{Boy-monster hurts Girl;}
103
+ @asts.size.should == 1
104
+ side_effects = match_readings_to_existing(@asts[0], @hurts_ft.all_reading.single)
105
+ pending "Thinks trailing adjectives are always residual"
106
+ side_effects[0].should_not be_nil
107
+ side_effects.to_s.should == ''
108
+ end
109
+
110
+ it "should match with implicit trailing adjective" do
111
+ parse %q{Boy monster hurts Girl;}
112
+ @asts.size.should == 1
113
+ side_effects = match_readings_to_existing(@asts[0], @hurts_ft.all_reading.single)
114
+ side_effects[0].should_not be_nil
115
+ side_effects.to_s.should == 'side-effects are [{Boy} absorbs 0/1 at 0, {Girl} absorbs 0/0 at 3]'
116
+ end
117
+
118
+ it "should match with local trailing adjective" do
119
+ parse %q{
120
+ Boy is going out with Girl-troublemaker,
121
+ Girl troublemaker likes Boy;
122
+ }
123
+ @asts.size.should == 1
124
+ side_effects = match_readings_to_existing(@asts[0], @simple_ft.all_reading.single)
125
+ side_effects.size.should == 2
126
+ side_effects[0].should_not be_nil
127
+ side_effects.to_s.should == 'side-effects are [{Boy} absorbs 0/0 at 0, {Girl -troublemaker} absorbs 0/0 at 5 with residual adjectives] with residual adjectives'
128
+ end
129
+
130
+ it "should match with explicit and local trailing adjective" do
131
+ parse %q{
132
+ Boy monster -foo hurts Girl,
133
+ Girl likes Boy foo;
134
+ }
135
+ @asts.size.should == 1
136
+ side_effects = match_readings_to_existing(@asts[0], @hurts_ft.all_reading.single)
137
+ side_effects.size.should == 2
138
+
139
+ pending "Matched adjectives must be removed and the role rebound before deciding whether residual adjectives have a purpose" do
140
+ side_effects[0].should_not be_nil
141
+ puts side_effects.to_s
142
+ # side_effects.to_s.should == ''
143
+ end
144
+ end
145
+
146
+ end
147
+ end
@@ -118,29 +118,29 @@ describe "Sample data" do
118
118
  # REVISIT: Include the instance_names of all role players
119
119
  end
120
120
 
121
- if i.concept.is_a?(ActiveFacts::Metamodel::ValueType)
122
- return "#{i.concept.name} #{i.value}"
121
+ if i.object_type.is_a?(ActiveFacts::Metamodel::ValueType)
122
+ return "#{i.object_type.name} #{i.value}"
123
123
  end
124
124
 
125
- if i.concept.fact_type # An instance of an objectified fact type
126
- return "#{i.concept.name} where #{instance_name(i.fact)}"
125
+ if i.object_type.fact_type # An instance of an objectified fact type
126
+ return "#{i.object_type.name} where #{instance_name(i.fact)}"
127
127
  end
128
128
 
129
129
  # It's an entity that's not an objectified fact type
130
130
  # REVISIT: If it has a simple identifier, there's no need to fully verbalise the identifying facts
131
- pi = i.concept.preferred_identifier
131
+ pi = i.object_type.preferred_identifier
132
132
  identifying_role_refs = pi.role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}
133
- return "#{i.concept.name}" +
133
+ return "#{i.object_type.name}" +
134
134
  " is identified by " +
135
135
  identifying_role_refs.map do |rr|
136
136
  [ (l = rr.leading_adjective) ? l+"-" : nil,
137
- rr.role.role_name || rr.role.concept.name,
137
+ rr.role.role_name || rr.role.object_type.name,
138
138
  (t = rr.trailing_adjective) ? l+"-" : nil
139
139
  ].compact*""
140
140
  end * " and " +
141
141
  " where " +
142
142
  identifying_role_refs.map do |rr| # Go through the identifying roles and emit the facts that define them
143
- instance_role = i.concept.all_role.detect{|r| r.fact_type == rr.role.fact_type}
143
+ instance_role = i.object_type.all_role.detect{|r| r.fact_type == rr.role.fact_type}
144
144
  identifying_fact = i.all_role_value.detect{|rv| rv.fact.fact_type == rr.role.fact_type}.fact
145
145
  #counterpart_role = (rr.role.fact_type.all_role.to_a-[instance_role])[0]
146
146
  #identifying_instance = counterpart_role.all_role_value.detect{|rv| rv.fact == identifying_fact}.instance
@@ -223,7 +223,7 @@ describe "Sample data" do
223
223
  end.should raise_error
224
224
 
225
225
  if missing
226
- missing.each do |m|
226
+ Array(missing).each do |m|
227
227
  @exception.message.should =~ (m.is_a?(Regexp) ? m : Regexp.new(Regexp.escape(m)))
228
228
  end
229
229
  else
@@ -3,7 +3,7 @@
3
3
  # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
4
  #
5
5
 
6
- require 'spec/spec_helper'
6
+ require File.dirname(__FILE__) + '/spec_helper'
7
7
  require 'stringio'
8
8
  require 'activefacts/vocabulary'
9
9
  require 'activefacts/support'
@@ -0,0 +1,116 @@
1
+ #
2
+ # ActiveFacts tests: Parse all CQL files and check the generated DataMapper models
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ # From IRC, on how to extract the SQL that DM creates for a set of models:
6
+ #
7
+ # I can tell you that ::DataObjects::Connection#log gets you the SQL
8
+ # after its run (not exactly what you asked for, but a place to start
9
+ # looking).
10
+ #
11
+ # your choices are intercepting
12
+ # ::DataMapper::Adapters::DataObjectsAdapter#{select,execute} for
13
+ # direct SQL calls, or at a lower level,
14
+ # ::DataObjects::Connection#create_command for all SQL.
15
+ #
16
+ # one trick I've been using is to selectively intercept the latter
17
+ # and not let it run (no-op it but log/inspect it), which can obviously
18
+ # screw up anything else back up the call stack but at least gives
19
+ # you the opportunity to catch it if you want.
20
+ #
21
+ # #create_command returns Command, which is subsequently called with
22
+ # #execute_reader(bind_values) or #execute_non_query(bind_values),
23
+ # so if the final query is what you're after, then you'll have to
24
+ # hook those both instead.
25
+
26
+ require 'spec_helper'
27
+ require 'stringio'
28
+ require 'activefacts/vocabulary'
29
+ require 'activefacts/support'
30
+ require 'activefacts/input/cql'
31
+ require 'activefacts/generate/dm'
32
+ require 'dm-core'
33
+ require 'dm-core/spec/lib/spec_helper'
34
+
35
+ describe "CQL Loader with DataMapper output" do
36
+ cql_failures = { # These CQL files can't be compiled
37
+ }
38
+ mapping_failures = { # These models can't be mapped to DM
39
+ 'OrienteeringER' => 'Invalid CQL results in unmappable model',
40
+ 'Insurance' => 'Cannot handle models that contain classes like Vehicle Incident with external supertypes (Incident)',
41
+ 'MetamodelNext' => 'Cannot handle models that contain classes like Constraint with external supertypes (ObjectType)',
42
+ 'MultiInheritance' => 'Cannot handle models that contain classes like Australian Employee with external supertypes (Australian)',
43
+ 'SeparateSubtype' => 'Cannot handle models that contain classes like Vehicle Incident with external supertypes (Incident)',
44
+ 'ServiceDirector' => 'Cannot handle models that contain classes like Client with external supertypes (Company)',
45
+ }
46
+ dm_failures = { # These mapped models don't work in DM
47
+ 'RedundantDependency' => 'Cannot find the child_model Addres for StateOrProvince in address while finalizing StateOrProvince.address',
48
+ 'Supervision' => 'Inflexion failure: Cannot find the parent_model Ceo for Company in ceo while finalizing Company.ceo',
49
+ }
50
+
51
+ # Generate and return the DataMapper models for the given vocabulary
52
+ def dm(vocabulary)
53
+ output = StringIO.new
54
+ @dumper = ActiveFacts::Generate::DM.new(vocabulary.constellation)
55
+ @dumper.generate(output)
56
+ output.rewind
57
+ output.read
58
+ end
59
+
60
+ pattern = ENV["AFTESTS"] || "*"
61
+ Dir["examples/CQL/#{pattern}.cql"].each do |cql_file|
62
+ expected_file = cql_file.sub(%r{/CQL/(.*).cql\Z}, '/datamapper/\1.dm.rb')
63
+ actual_file = cql_file.sub(%r{examples/CQL/(.*).cql\Z}, 'spec/actual/\1.dm.rb')
64
+ base = File.basename(cql_file, ".cql")
65
+
66
+ it "should load #{cql_file} and dump DataMapper models matching #{expected_file}" do
67
+ vocabulary = nil
68
+ broken = cql_failures[base]
69
+ if broken
70
+ pending(broken) {
71
+ vocabulary = ActiveFacts::Input::CQL.readfile(cql_file)
72
+ }
73
+ else
74
+ vocabulary = ActiveFacts::Input::CQL.readfile(cql_file)
75
+ end
76
+
77
+ # Build and save the actual file:
78
+ dm_text = ''
79
+ lambda do
80
+ begin
81
+ dm_text = dm(vocabulary)
82
+ File.open(actual_file, "w") { |f| f.write dm_text }
83
+ rescue
84
+ raise unless mapping_failures[base]
85
+ end
86
+ end.should_not raise_error
87
+
88
+ if m = mapping_failures[base]
89
+ File.delete(actual_file) rescue nil
90
+ pending m
91
+ end
92
+
93
+ lambda do
94
+ begin
95
+ eval dm_text
96
+ DataMapper.finalize
97
+ rescue
98
+ raise unless dm_failures[base]
99
+ ensure
100
+ DataMapper::Spec.cleanup_models
101
+ end
102
+ end.should_not raise_error
103
+ if m = dm_failures[base]
104
+ File.delete(actual_file) rescue nil
105
+ pending m
106
+ end
107
+
108
+ pending("expected output file #{expected_file} not found") unless File.exists? expected_file
109
+
110
+ # Compare with the expected file:
111
+ expected_text = File.open(expected_file) {|f| f.read }
112
+ dm_text.should_not differ_from(expected_text)
113
+ File.delete(actual_file) # It succeeded, we don't need the file.
114
+ end
115
+ end
116
+ end