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.
- data/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- 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
|
data/spec/cql/samples_spec.rb
CHANGED
|
@@ -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.
|
|
122
|
-
return "#{i.
|
|
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.
|
|
126
|
-
return "#{i.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
data/spec/cql_cql_spec.rb
CHANGED
data/spec/cql_dm_spec.rb
ADDED
|
@@ -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
|