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