activefacts 0.8.6 → 0.8.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +33 -2
- data/README.rdoc +30 -36
- data/Rakefile +16 -20
- data/bin/afgen +17 -11
- data/bin/cql +313 -36
- data/download.html +43 -19
- data/examples/CQL/Address.cql +15 -15
- data/examples/CQL/Blog.cql +8 -8
- data/examples/CQL/CompanyDirectorEmployee.cql +6 -5
- data/examples/CQL/Death.cql +3 -3
- data/examples/CQL/Diplomacy.cql +48 -0
- data/examples/CQL/Genealogy.cql +41 -41
- data/examples/CQL/Insurance.cql +311 -0
- data/examples/CQL/JoinEquality.cql +35 -0
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +290 -185
- data/examples/CQL/MetamodelNext.cql +420 -0
- data/examples/CQL/Monogamy.cql +24 -0
- data/examples/CQL/MonthInSeason.cql +27 -0
- data/examples/CQL/Moon.cql +23 -0
- data/examples/CQL/MultiInheritance.cql +4 -4
- data/examples/CQL/NonRoleId.cql +14 -0
- data/examples/CQL/OddIdentifier.cql +18 -0
- data/examples/CQL/OilSupply.cql +24 -24
- data/examples/CQL/OneToOnes.cql +17 -0
- data/examples/CQL/Orienteering.cql +55 -55
- data/examples/CQL/OrienteeringER.cql +58 -0
- data/examples/CQL/PersonPlaysGame.cql +2 -2
- data/examples/CQL/RedundantDependency.cql +34 -0
- data/examples/CQL/SchoolActivities.cql +5 -5
- data/examples/CQL/SeparateSubtype.cql +28 -0
- data/examples/CQL/ServiceDirector.cql +283 -0
- data/examples/CQL/SimplestUnary.cql +2 -2
- data/examples/CQL/SubtypePI.cql +11 -11
- data/examples/CQL/Supervision.cql +38 -0
- data/examples/CQL/Tests.Test5.Load.cql +38 -0
- data/examples/CQL/WaiterTips.cql +33 -0
- data/examples/CQL/Warehousing.cql +55 -53
- data/examples/CQL/WindowInRoomInBldg.cql +9 -9
- data/examples/CQL/unit.cql +433 -544
- data/examples/index.html +314 -170
- data/examples/intro.html +6 -176
- data/examples/local.css +8 -4
- data/index.html +40 -25
- data/lib/activefacts/api/concept.rb +2 -2
- data/lib/activefacts/api/constellation.rb +4 -4
- data/lib/activefacts/api/instance.rb +2 -2
- data/lib/activefacts/api/instance_index.rb +4 -0
- data/lib/activefacts/api/numeric.rb +3 -1
- data/lib/activefacts/api/role.rb +1 -1
- data/lib/activefacts/api/standard_types.rb +23 -16
- data/lib/activefacts/api/support.rb +3 -1
- data/lib/activefacts/api/vocabulary.rb +4 -0
- data/lib/activefacts/cql/CQLParser.treetop +87 -39
- data/lib/activefacts/cql/Concepts.treetop +95 -69
- data/lib/activefacts/cql/Context.treetop +11 -2
- data/lib/activefacts/cql/Expressions.treetop +23 -59
- data/lib/activefacts/cql/FactTypes.treetop +141 -95
- data/lib/activefacts/cql/Language/English.treetop +33 -21
- data/lib/activefacts/cql/LexicalRules.treetop +6 -1
- data/lib/activefacts/cql/Terms.treetop +75 -26
- data/lib/activefacts/cql/ValueTypes.treetop +52 -54
- data/lib/activefacts/cql/compiler.rb +46 -1691
- data/lib/activefacts/cql/compiler/constraint.rb +602 -0
- data/lib/activefacts/cql/compiler/entity_type.rb +425 -0
- data/lib/activefacts/cql/compiler/fact.rb +300 -0
- data/lib/activefacts/cql/compiler/fact_type.rb +230 -0
- data/lib/activefacts/cql/compiler/reading.rb +832 -0
- data/lib/activefacts/cql/compiler/shared.rb +109 -0
- data/lib/activefacts/cql/compiler/value_type.rb +104 -0
- data/lib/activefacts/cql/parser.rb +132 -81
- data/lib/activefacts/generate/cql.rb +397 -274
- data/lib/activefacts/generate/oo.rb +13 -12
- data/lib/activefacts/generate/ordered.rb +107 -117
- data/lib/activefacts/generate/ruby.rb +34 -38
- data/lib/activefacts/generate/sql/mysql.rb +62 -45
- data/lib/activefacts/generate/sql/server.rb +59 -42
- data/lib/activefacts/input/cql.rb +6 -3
- data/lib/activefacts/input/orm.rb +991 -557
- data/lib/activefacts/persistence/columns.rb +16 -12
- data/lib/activefacts/persistence/foreignkey.rb +7 -4
- data/lib/activefacts/persistence/index.rb +3 -4
- data/lib/activefacts/persistence/reference.rb +5 -2
- data/lib/activefacts/support.rb +20 -14
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary.rb +1 -0
- data/lib/activefacts/vocabulary/extensions.rb +328 -44
- data/lib/activefacts/vocabulary/metamodel.rb +145 -20
- data/lib/activefacts/vocabulary/verbaliser.rb +621 -0
- data/spec/absorption_spec.rb +4 -4
- data/spec/api/value_type.rb +1 -1
- data/spec/cql/context_spec.rb +45 -22
- data/spec/cql/deontic_spec.rb +88 -0
- data/spec/cql/matching_spec.rb +517 -0
- data/spec/cql/samples_spec.rb +88 -31
- data/spec/cql/unit_spec.rb +58 -37
- data/spec/cql_cql_spec.rb +12 -7
- data/spec/cql_mysql_spec.rb +3 -7
- data/spec/cql_parse_spec.rb +0 -4
- data/spec/cql_ruby_spec.rb +1 -4
- data/spec/cql_sql_spec.rb +5 -18
- data/spec/cql_symbol_tables_spec.rb +3 -0
- data/spec/cqldump_spec.rb +0 -2
- data/spec/helpers/array_matcher.rb +35 -0
- data/spec/helpers/ctrl_c_support.rb +52 -0
- data/spec/helpers/diff_matcher.rb +38 -0
- data/spec/helpers/file_matcher.rb +5 -3
- data/spec/helpers/string_matcher.rb +39 -0
- data/spec/helpers/test_parser.rb +13 -0
- data/spec/norma_cql_spec.rb +13 -5
- data/spec/norma_ruby_spec.rb +11 -3
- data/spec/{absorption_ruby_spec.rb → norma_ruby_sql_spec.rb} +37 -32
- data/spec/norma_sql_spec.rb +11 -5
- data/spec/norma_tables_spec.rb +33 -29
- data/spec/spec_helper.rb +4 -1
- data/status.html +92 -23
- metadata +102 -36
- data/lib/activefacts/generate/cql/html.rb +0 -403
data/spec/absorption_spec.rb
CHANGED
@@ -59,11 +59,11 @@ describe "Absorption" do
|
|
59
59
|
Lodgement is where
|
60
60
|
Claim was lodged by at most one Person;
|
61
61
|
Lodgement was made at at most one DateTime;
|
62
|
-
Person has exactly one birth-
|
62
|
+
Person has exactly one birth-DateTime;
|
63
63
|
},
|
64
64
|
:tables => {
|
65
65
|
"Claim" => [%w{Claim ID}, %w{Lodgement Date Time}, %w{Lodgement Person ID}],
|
66
|
-
"Party" => [%w{Party ID}, %w{Person Birth Date}]
|
66
|
+
"Party" => [%w{Party ID}, %w{Person Birth Date Time}]
|
67
67
|
}
|
68
68
|
},
|
69
69
|
|
@@ -74,8 +74,8 @@ describe "Absorption" do
|
|
74
74
|
cql = test[:cql]
|
75
75
|
expected_tables = test[:tables]
|
76
76
|
it "should #{should}" do
|
77
|
-
@compiler = ActiveFacts::CQL::Compiler.new(
|
78
|
-
@vocabulary = @compiler.
|
77
|
+
@compiler = ActiveFacts::CQL::Compiler.new(should)
|
78
|
+
@vocabulary = @compiler.compile(cql)
|
79
79
|
|
80
80
|
# puts cql
|
81
81
|
|
data/spec/api/value_type.rb
CHANGED
data/spec/cql/context_spec.rb
CHANGED
@@ -5,67 +5,90 @@
|
|
5
5
|
|
6
6
|
require 'activefacts/support'
|
7
7
|
require 'activefacts/api/support'
|
8
|
-
require 'activefacts/cql/
|
8
|
+
require 'activefacts/cql/compiler'
|
9
9
|
|
10
10
|
describe "Business Context Notes" do
|
11
11
|
# (according_to people ',')? (because / as_opposed_to / so_that / to_avoid) discussion (',' as_agreed_by)? s
|
12
|
-
|
12
|
+
ContextNotePrefix = %q{
|
13
|
+
vocabulary Test;
|
13
14
|
Person is written as Person;
|
15
|
+
Person is employed;
|
16
|
+
Person is unemployed;
|
17
|
+
Person is a bad credit risk;
|
18
|
+
Person is good credit risk;
|
19
|
+
Bar is written as Bar;
|
20
|
+
Baz is written as Baz;
|
14
21
|
}
|
15
22
|
Notes = [
|
16
23
|
# Constraints:
|
17
24
|
[ 'each Person occurs one time in Person is employed, Person is unemployed (because it can be no other way!);',
|
18
|
-
|
25
|
+
1, 'because'
|
19
26
|
],
|
20
27
|
[ 'each Person occurs one time in Person is employed, Person is unemployed (as opposed to blah!);',
|
21
|
-
|
28
|
+
1, 'as_opposed_to'
|
22
29
|
],
|
23
30
|
[ 'for each Person at least one of these holds: Person is employed, Person is a bad credit risk (so that blah);',
|
24
|
-
|
31
|
+
1, 'so_that'
|
25
32
|
],
|
26
33
|
[ 'Person is good credit risk only if Person is employed (to avoid lending to people who can\'t repay);',
|
27
|
-
|
34
|
+
1, 'to_avoid'
|
28
35
|
],
|
29
36
|
[ 'Person is good credit risk if and only if Person is employed (to avoid lending to people who can\'t repay);',
|
30
|
-
|
37
|
+
1, 'to_avoid'
|
31
38
|
],
|
39
|
+
[ 'Person is good credit risk if and only if Person is employed (to avoid lending to people who can\'t repay, as agreed by Jim);',
|
40
|
+
1, 'to_avoid', nil, ['Jim']
|
41
|
+
],
|
42
|
+
# Entity and Fact types
|
32
43
|
# Entity and Fact types
|
33
44
|
[ 'Foo is identified by Bar [independent] where Foo has one Bar (so that we have an id);',
|
34
|
-
|
45
|
+
1, 'so_that'
|
35
46
|
],
|
36
|
-
[ '
|
37
|
-
|
47
|
+
[ 'Baz has one Bar (so that we have an id), Bar is of one Baz (because we need that);',
|
48
|
+
2
|
38
49
|
],
|
39
50
|
# REVISIT: No context notes on quantifiers yet
|
40
51
|
# As agreed by:
|
41
52
|
[ 'for each Person at least one of these holds: Person is employed, Person is a bad credit risk (so that blah, as agreed by Jim);',
|
42
|
-
|
53
|
+
1, 'so_that', nil, ['Jim']
|
43
54
|
],
|
44
55
|
# REVISIT: Populate an "as agreed by" with a date
|
45
|
-
[ 'for each Person at least one of these holds: Person is employed, Person is a bad credit risk (so that blah, as agreed on
|
46
|
-
|
56
|
+
[ 'for each Person at least one of these holds: Person is employed, Person is a bad credit risk (so that blah, as agreed on 10-04-10 by Jim);',
|
57
|
+
1, 'so_that', nil, ['Jim'], '10-04-10'
|
47
58
|
],
|
48
59
|
# According to:
|
49
60
|
[ 'for each Person at least one of these holds: Person is employed, Person is a bad credit risk (according to jim, so that blah);',
|
50
|
-
|
61
|
+
1, 'so_that', ['jim']
|
51
62
|
],
|
52
63
|
]
|
53
64
|
|
54
65
|
before :each do
|
55
|
-
@
|
66
|
+
@compiler = ActiveFacts::CQL::Compiler.new('Test')
|
56
67
|
end
|
57
68
|
|
58
69
|
Notes.each do |c|
|
59
|
-
source,
|
70
|
+
source, count, kind, according_to, agreed_by, agreed_date = *c
|
60
71
|
it "should parse #{source.inspect}" do
|
61
|
-
#debugger
|
62
|
-
result = @parser.parse_all(Prefix+source, :definition)
|
63
72
|
|
64
|
-
|
73
|
+
result =
|
74
|
+
begin
|
75
|
+
@compiler.compile(ContextNotePrefix+source)
|
76
|
+
rescue => e
|
77
|
+
puts "#{e}:\n\t#{e.backtrace*"\n\t"}"
|
78
|
+
end
|
79
|
+
puts @compiler.failure_reason unless result
|
65
80
|
result.should_not be_nil
|
66
|
-
|
67
|
-
|
68
|
-
#
|
81
|
+
constellation = @compiler.vocabulary.constellation
|
82
|
+
|
83
|
+
# constellation.ContextNote.each{|k,cn| puts "#{k.inspect} => #{cn.inspect}" }
|
84
|
+
constellation.ContextNote.size.should == (count || 1)
|
85
|
+
context_note = constellation.ContextNote.values[0]
|
86
|
+
context_note.context_note_kind.should == kind if kind
|
87
|
+
context_note.all_context_according_to.map{|cat| cat.agent.agent_name }.sort.should == according_to if according_to
|
88
|
+
# context_note.discussion.should == discussion if discussion
|
89
|
+
context_note.agreement.should_not be_nil if agreed_by || agreed_date
|
90
|
+
context_note.agreement.all_context_agreed_by.map{|cab| cab.agent.agent_name}.sort.should == agreed_by if agreed_by
|
91
|
+
context_note.agreement.date.should == Date.parse(agreed_date) if agreed_date
|
69
92
|
end
|
70
93
|
end
|
71
94
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Deontic Constraints tests
|
3
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'activefacts/support'
|
7
|
+
require 'activefacts/api/support'
|
8
|
+
require 'activefacts/cql/compiler'
|
9
|
+
|
10
|
+
describe "Deontic Constraints" do
|
11
|
+
DeonticPrefix = %q{
|
12
|
+
vocabulary Test;
|
13
|
+
Person is written as Person;
|
14
|
+
Person is employed;
|
15
|
+
Person is unemployed;
|
16
|
+
Person has borrowed cash;
|
17
|
+
Person is a bad credit risk;
|
18
|
+
Nr is written as Nr;
|
19
|
+
Person is good credit risk;
|
20
|
+
Bar is written as Bar;
|
21
|
+
Baz is written as Baz;
|
22
|
+
}
|
23
|
+
Cases = [
|
24
|
+
# Constraints:
|
25
|
+
[ 'each Person occurs one time (otherwise alert fraud dept) in Person is employed, Person is unemployed;',
|
26
|
+
'alert', 'fraud dept'
|
27
|
+
],
|
28
|
+
[ 'for each Person at most one of these holds ( otherwise email auditors ) : Person has borrowed cash, Person is a bad credit risk;',
|
29
|
+
'email', 'auditors'
|
30
|
+
],
|
31
|
+
[ 'either Person has borrowed cash or Person is a bad credit risk ( otherwise email auditors );',
|
32
|
+
'email', 'auditors'
|
33
|
+
],
|
34
|
+
[ 'either Person has borrowed cash or Person is a bad credit risk but not both ( otherwise email auditors );',
|
35
|
+
'email', 'auditors'
|
36
|
+
],
|
37
|
+
[ 'Person is good credit risk only if Person is employed (otherwise consider foreclosure);',
|
38
|
+
'consider', 'foreclosure'
|
39
|
+
],
|
40
|
+
[ 'Person is good credit risk if and only if Person is employed (otherwise log event);',
|
41
|
+
'log', 'event'
|
42
|
+
],
|
43
|
+
[ 'Foo is written as Nr restricted to {1..10} (otherwise log);',
|
44
|
+
'log', ''
|
45
|
+
],
|
46
|
+
[ 'Foo is identified by its Nr restricted to {1..10} (otherwise log);',
|
47
|
+
'log', ''
|
48
|
+
],
|
49
|
+
[ 'Baz has at most one (otherwise notify security) Bar, Bar is of one Baz restricted to {1..10};',
|
50
|
+
'notify', 'security'
|
51
|
+
],
|
52
|
+
[ 'Baz has at most one Bar, Bar is of one Baz restricted to {1..10} (otherwise log exception);',
|
53
|
+
'log', 'exception'
|
54
|
+
],
|
55
|
+
]
|
56
|
+
|
57
|
+
before :each do
|
58
|
+
@compiler = ActiveFacts::CQL::Compiler.new('Test')
|
59
|
+
end
|
60
|
+
|
61
|
+
Cases.each do |c|
|
62
|
+
source, action, agent = *c
|
63
|
+
it "should parse #{source.inspect}" do
|
64
|
+
result = @compiler.compile(DeonticPrefix+source)
|
65
|
+
puts @compiler.failure_reason unless result
|
66
|
+
result.should_not be_nil
|
67
|
+
constellation = @compiler.vocabulary.constellation
|
68
|
+
|
69
|
+
enforcements =
|
70
|
+
constellation.Constraint.values.map do |c|
|
71
|
+
c.enforcement
|
72
|
+
end.compact
|
73
|
+
enforcements.size.should == 1
|
74
|
+
enforcements[0].enforcement_code.should == action if action
|
75
|
+
if agent
|
76
|
+
if agent != ''
|
77
|
+
enforcements[0].agent.agent_name.should == agent
|
78
|
+
else
|
79
|
+
enforcements[0].agent.should be_nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#result.map{|d| d.value}.should == ast if ast
|
84
|
+
# Uncomment this to see what should replace "nil" in the cases above:
|
85
|
+
#puts result.map{|d| d.value}.inspect unless ast
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,517 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Fact Type matching tests
|
3
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'activefacts/support'
|
7
|
+
require 'activefacts/api/support'
|
8
|
+
require 'activefacts/cql/compiler'
|
9
|
+
# require File.dirname(__FILE__) + '/../helpers/compiler_helper' # Can't see how to include/extend these methods correctly
|
10
|
+
|
11
|
+
describe "Fact Type Role Matching" do
|
12
|
+
MatchingPrefix = %q{
|
13
|
+
vocabulary Tests;
|
14
|
+
Boy is written as String;
|
15
|
+
Girl is written as String;
|
16
|
+
}
|
17
|
+
BaseConcepts = 3 # String, Boy, Girl
|
18
|
+
|
19
|
+
def self.SingleFact &b
|
20
|
+
lambda {|c|
|
21
|
+
real_fact_types = c.FactType.values-c.ImplicitFactType.values
|
22
|
+
real_fact_types.size.should == 1
|
23
|
+
@fact_type = real_fact_types[0]
|
24
|
+
b.call(@fact_type) if b
|
25
|
+
@fact_type
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.FactHavingPlayers(*a, &b)
|
30
|
+
lambda {|c|
|
31
|
+
@fact_type = c.FactType.detect do |key, ft|
|
32
|
+
ft.all_role.map{|r| r.concept.name}.sort == a.sort
|
33
|
+
end
|
34
|
+
b.call(@fact_type) if b
|
35
|
+
@fact_type
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.PresenceConstraints fact_type, &b
|
40
|
+
@presence_constraints =
|
41
|
+
fact_type.all_role.map{|r|
|
42
|
+
r.all_role_ref.map{|rr|
|
43
|
+
rr.role_sequence.all_presence_constraint.to_a
|
44
|
+
}
|
45
|
+
}.flatten.uniq
|
46
|
+
b.call(@presence_constraints) if b
|
47
|
+
@presence_constraints
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.Readings fact_type, &b
|
51
|
+
@readings = fact_type.all_reading.sort_by{|r| r.ordinal}
|
52
|
+
b.call(@readings) if b
|
53
|
+
@readings
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.ReadingCount n
|
57
|
+
lambda {|c|
|
58
|
+
unless @fact_type.all_reading.size == n
|
59
|
+
puts "SPEC FAILED, wrong number of readings (should be #{n}):\n\t#{
|
60
|
+
@fact_type.all_reading.map{ |r| r.expand}*"\n\t"
|
61
|
+
}"
|
62
|
+
end
|
63
|
+
@fact_type.all_reading.size.should == n
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.PresenceConstraintCount n
|
68
|
+
lambda{ |c|
|
69
|
+
@fact_type.all_role.map{|r|
|
70
|
+
r.all_role_ref.map{|rr|
|
71
|
+
rr.role_sequence.all_presence_constraint.to_a
|
72
|
+
}
|
73
|
+
}.flatten.uniq.size.should == n
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.ConceptCount n
|
78
|
+
lambda {|c|
|
79
|
+
@constellation = c
|
80
|
+
c.Concept.values.size.should == n
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.Concept name, &b
|
85
|
+
lambda {|c|
|
86
|
+
@concept = c.Concept[[["Tests"], name]]
|
87
|
+
@concept.should_not == nil
|
88
|
+
b.call(@concept) if b
|
89
|
+
@concept
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.WrittenAs name
|
94
|
+
lambda {|c|
|
95
|
+
@base_type = c.Concept[[["Tests"], name]]
|
96
|
+
@base_type.class.should == ActiveFacts::Metamodel::ValueType
|
97
|
+
@concept.class.should == ActiveFacts::Metamodel::ValueType
|
98
|
+
@concept.supertype.should == @base_type
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.PreferredIdentifier num_roles
|
103
|
+
lambda {|c|
|
104
|
+
@preferred_identifier = @concept.preferred_identifier
|
105
|
+
@preferred_identifier.should_not == nil
|
106
|
+
@preferred_identifier.role_sequence.all_role_ref.size.should == num_roles
|
107
|
+
#@preferred_identifier.min_frequency.should == 1
|
108
|
+
@preferred_identifier.max_frequency.should == 1
|
109
|
+
@preferred_identifier.is_preferred_identifier.should == true
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.PreferredIdentifierRolePlayedBy name, num = 0
|
114
|
+
lambda {|c|
|
115
|
+
@preferred_identifier.role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}[num].role.concept.name.should == name
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
class BlackHole
|
120
|
+
def method_missing(m,*a,&b)
|
121
|
+
self
|
122
|
+
end
|
123
|
+
end
|
124
|
+
class PendingSilencer
|
125
|
+
def STDOUT; BlackHole.new; end
|
126
|
+
def puts; BlackHole.new; end
|
127
|
+
def p; BlackHole.new; end
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.pending(msg = "TODO", &b)
|
131
|
+
lambda {|*c|
|
132
|
+
raised = nil
|
133
|
+
begin
|
134
|
+
example = b.call
|
135
|
+
eval(lambda { example.call(*c) }, BlackHole.new)
|
136
|
+
rescue => raised
|
137
|
+
end
|
138
|
+
raise Spec::Example::PendingExampleFixedError.new(msg) unless raised
|
139
|
+
raise Spec::Example::ExamplePendingError.new(msg)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.ReadingContainsHyphenatedWord reading_num
|
144
|
+
lambda {|c|
|
145
|
+
hyphenated_reading =
|
146
|
+
c.FactType.values[0].all_reading.select {|reading|
|
147
|
+
reading.ordinal == reading_num
|
148
|
+
}[0]
|
149
|
+
hyphenated_reading.should_not == nil
|
150
|
+
(hyphenated_reading.text =~ /[a-z]-[a-z]/).should_not == nil
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
SimpleBinaryFactTypeTests = [
|
155
|
+
[ # Simple create
|
156
|
+
%q{Girl is going out with at most one Boy; },
|
157
|
+
SingleFact() do |fact_type|
|
158
|
+
Readings(fact_type).size.should == 1
|
159
|
+
PresenceConstraints(fact_type) do |pcs|
|
160
|
+
pcs.size.should == 1
|
161
|
+
end
|
162
|
+
end
|
163
|
+
],
|
164
|
+
[ # Create with explicit adjective
|
165
|
+
%q{Girl is going out with at most one ugly-Boy;},
|
166
|
+
SingleFact() do |fact_type|
|
167
|
+
Readings(fact_type).size.should == 1
|
168
|
+
PresenceConstraints(fact_type).size.should == 1
|
169
|
+
end
|
170
|
+
],
|
171
|
+
[ # Simple match
|
172
|
+
%q{Girl is going out with at most one Boy; },
|
173
|
+
%q{
|
174
|
+
Girl is going out with Boy,
|
175
|
+
Boy is going out with Girl;
|
176
|
+
},
|
177
|
+
SingleFact() do |fact_type|
|
178
|
+
Readings(fact_type).size.should == 2
|
179
|
+
PresenceConstraints(fact_type).size.should == 1
|
180
|
+
end
|
181
|
+
],
|
182
|
+
[ # Simple match with repetition
|
183
|
+
%q{Girl is going out with at most one Boy; },
|
184
|
+
%q{
|
185
|
+
Girl is going out with Boy,
|
186
|
+
Girl is going out with Boy,
|
187
|
+
Boy is going out with Girl,
|
188
|
+
Boy is going out with Girl;
|
189
|
+
},
|
190
|
+
SingleFact() do |fact_type|
|
191
|
+
PresenceConstraints(fact_type).size.should == 1
|
192
|
+
pending("duplicate new clauses are not eliminated") do
|
193
|
+
Readings(fact_type).size.should == 2
|
194
|
+
end.call # Must call the pending block
|
195
|
+
end,
|
196
|
+
# pending("duplicate new clauses are not eliminated") do
|
197
|
+
# @readings.size.should == 2
|
198
|
+
# end
|
199
|
+
],
|
200
|
+
[ # Simple match with a new presence Constraint
|
201
|
+
%q{Girl is going out with at most one Boy; },
|
202
|
+
%q{
|
203
|
+
Girl is going out with Boy,
|
204
|
+
Boy is going out with at most one Girl;
|
205
|
+
},
|
206
|
+
SingleFact() do |fact_type|
|
207
|
+
Readings(fact_type).size.should == 2
|
208
|
+
PresenceConstraints(fact_type).size.should == 2
|
209
|
+
end
|
210
|
+
],
|
211
|
+
[ # RoleName matching
|
212
|
+
%q{Girl is going out with at most one Boy;},
|
213
|
+
%q{
|
214
|
+
Boy is going out with Girlfriend,
|
215
|
+
Girl (as Girlfriend) is going out with at most one Boy;
|
216
|
+
},
|
217
|
+
SingleFact() do |fact_type|
|
218
|
+
Readings(fact_type).size.should == 3
|
219
|
+
PresenceConstraints(fact_type).size.should == 1
|
220
|
+
end
|
221
|
+
],
|
222
|
+
[ # Match with explicit adjective
|
223
|
+
%q{Girl is going out with at most one ugly-Boy;},
|
224
|
+
%q{Girl is going out with at most one ugly-Boy,
|
225
|
+
ugly-Boy is best friend of Girl;
|
226
|
+
},
|
227
|
+
SingleFact() do |fact_type|
|
228
|
+
Readings(fact_type).size.should == 2
|
229
|
+
PresenceConstraints(fact_type).size.should == 1
|
230
|
+
end
|
231
|
+
],
|
232
|
+
[ # Match with implicit adjective
|
233
|
+
%q{Girl is going out with at most one ugly-Boy;},
|
234
|
+
%q{Girl is going out with ugly Boy,
|
235
|
+
Boy is going out with Girl;
|
236
|
+
},
|
237
|
+
SingleFact() do |fact_type|
|
238
|
+
Readings(fact_type).size.should == 2
|
239
|
+
PresenceConstraints(fact_type).size.should == 1
|
240
|
+
end
|
241
|
+
],
|
242
|
+
[ # Match with explicit trailing adjective
|
243
|
+
%q{Girl is going out with at most one Boy-monster;},
|
244
|
+
%q{Girl is going out with Boy-monster,
|
245
|
+
Boy is going out with Girl;
|
246
|
+
},
|
247
|
+
SingleFact() do |fact_type|
|
248
|
+
Readings(fact_type).size.should == 2
|
249
|
+
PresenceConstraints(fact_type).size.should == 1
|
250
|
+
end
|
251
|
+
],
|
252
|
+
[ # Match with implicit trailing adjective
|
253
|
+
%q{Girl is going out with at most one Boy-monster;},
|
254
|
+
%q{Girl is going out with Boy monster,
|
255
|
+
Boy is going out with Girl;
|
256
|
+
},
|
257
|
+
SingleFact() do |fact_type|
|
258
|
+
Readings(fact_type).size.should == 2
|
259
|
+
PresenceConstraints(fact_type).size.should == 1
|
260
|
+
end
|
261
|
+
],
|
262
|
+
[ # Match with two explicit adjectives
|
263
|
+
%q{Girl is going out with at most one ugly- bad Boy;},
|
264
|
+
%q{Girl is going out with ugly- bad Boy,
|
265
|
+
ugly- bad Boy is going out with Girl;
|
266
|
+
},
|
267
|
+
SingleFact() do |fact_type|
|
268
|
+
Readings(fact_type).size.should == 2
|
269
|
+
PresenceConstraints(fact_type).size.should == 1
|
270
|
+
end
|
271
|
+
],
|
272
|
+
[ # Match with two implicit adjective
|
273
|
+
%q{Girl is going out with at most one ugly- bad Boy;},
|
274
|
+
%q{Girl is going out with ugly bad Boy,
|
275
|
+
Boy is going out with Girl;
|
276
|
+
},
|
277
|
+
SingleFact(),
|
278
|
+
ReadingCount(2),
|
279
|
+
PresenceConstraintCount(1)
|
280
|
+
],
|
281
|
+
[ # Match with two explicit trailing adjective
|
282
|
+
%q{Girl is going out with at most one Boy real -monster;},
|
283
|
+
%q{Girl is going out with Boy real -monster,
|
284
|
+
Boy is going out with Girl;
|
285
|
+
},
|
286
|
+
SingleFact() do |fact_type|
|
287
|
+
Readings(fact_type).size.should == 2
|
288
|
+
PresenceConstraints(fact_type).size.should == 1
|
289
|
+
end
|
290
|
+
],
|
291
|
+
[ # Match with two implicit trailing adjectives
|
292
|
+
%q{Girl is going out with at most one Boy real -monster;},
|
293
|
+
%q{Girl is going out with Boy real monster,
|
294
|
+
Boy is going out with Girl;
|
295
|
+
},
|
296
|
+
SingleFact() do |fact_type|
|
297
|
+
Readings(fact_type).size.should == 2
|
298
|
+
PresenceConstraints(fact_type).size.should == 1
|
299
|
+
end
|
300
|
+
],
|
301
|
+
[ # Match with hyphenated word
|
302
|
+
%q{Girl is going out with at most one Boy; },
|
303
|
+
%q{
|
304
|
+
Girl is going out with Boy,
|
305
|
+
Boy is out driving a semi-trailer with Girl;
|
306
|
+
},
|
307
|
+
SingleFact() do |fact_type|
|
308
|
+
(readings = Readings(fact_type)).size.should == 2
|
309
|
+
## REVISIT: Refactor test
|
310
|
+
#ReadingContainsHyphenatedWord(readings[1])
|
311
|
+
ReadingContainsHyphenatedWord(1)
|
312
|
+
PresenceConstraintCount(1)
|
313
|
+
end
|
314
|
+
],
|
315
|
+
[ # Match with implicit leading ignoring explicit trailing adjective
|
316
|
+
%q{Girl is going out with at most one ugly-Boy;},
|
317
|
+
%q{Girl is going out with ugly Boy-monster,
|
318
|
+
Boy is going out with Girl;
|
319
|
+
},
|
320
|
+
SingleFact() do |fact_type|
|
321
|
+
Readings(fact_type).size.should == 3
|
322
|
+
PresenceConstraints(fact_type).size.should == 1
|
323
|
+
end
|
324
|
+
],
|
325
|
+
[ # Match with implicit leading ignoring implicit trailing adjective
|
326
|
+
%q{Girl is going out with at most one ugly-Boy;},
|
327
|
+
%q{Girl is going out with ugly Boy monster,
|
328
|
+
Boy-monster is going out with Girl;
|
329
|
+
},
|
330
|
+
SingleFact(),
|
331
|
+
ReadingCount(3),
|
332
|
+
PresenceConstraintCount(1)
|
333
|
+
],
|
334
|
+
[ # Match with implicit trailing ignoring explicit leading adjective
|
335
|
+
%q{Girl is going out with at most one Boy-monster;},
|
336
|
+
%q{Girl is going out with ugly-Boy monster,
|
337
|
+
Boy is going out with Girl;
|
338
|
+
},
|
339
|
+
SingleFact() do |fact_type|
|
340
|
+
Readings(fact_type).size.should == 3
|
341
|
+
PresenceConstraints(fact_type).size.should == 1
|
342
|
+
end
|
343
|
+
],
|
344
|
+
[ # Match with implicit trailing ignoring implicit leading adjective
|
345
|
+
%q{Girl is going out with at most one Boy-monster;},
|
346
|
+
%q{Girl is going out with ugly Boy monster,
|
347
|
+
ugly-Boy is going out with Girl;
|
348
|
+
},
|
349
|
+
SingleFact() do |fact_type|
|
350
|
+
Readings(fact_type).size.should == 3
|
351
|
+
PresenceConstraints(fact_type).size.should == 1
|
352
|
+
end
|
353
|
+
],
|
354
|
+
]
|
355
|
+
|
356
|
+
EntityIdentificationTests = [
|
357
|
+
[
|
358
|
+
# REVISIT: At present, this doesn't add the minimum frequency constraint that a preferred identifier requires.
|
359
|
+
%q{Thong is written as String;},
|
360
|
+
%q{Thing is identified by Thong where Thing has one Thong;},
|
361
|
+
SingleFact() do |fact_type|
|
362
|
+
Readings(fact_type).size.should == 1
|
363
|
+
PresenceConstraints(fact_type).size.should == 2
|
364
|
+
end,
|
365
|
+
Concept('Thong') do |concept|
|
366
|
+
concept.class.should == ActiveFacts::Metamodel::ValueType
|
367
|
+
# REVISIT: Figure out how WrittenAs can access the constellation.
|
368
|
+
WrittenAs('String')
|
369
|
+
end,
|
370
|
+
ConceptCount(2+BaseConcepts),
|
371
|
+
Concept('Thing'),
|
372
|
+
PreferredIdentifier(1),
|
373
|
+
PreferredIdentifierRolePlayedBy('Thong'),
|
374
|
+
],
|
375
|
+
|
376
|
+
[ # Auto-create Id and Thing Id:
|
377
|
+
%q{Thing is identified by its Id;},
|
378
|
+
SingleFact() do |fact_type|
|
379
|
+
Readings(fact_type).size.should == 2
|
380
|
+
PresenceConstraints(fact_type).size.should == 2
|
381
|
+
end,
|
382
|
+
ConceptCount(3+BaseConcepts),
|
383
|
+
Concept('Thing'),
|
384
|
+
PreferredIdentifier(1),
|
385
|
+
PreferredIdentifierRolePlayedBy('Thing Id'),
|
386
|
+
],
|
387
|
+
|
388
|
+
[ # Auto-create Thing Id:
|
389
|
+
%q{Id is written as String;},
|
390
|
+
%q{Thing is identified by its Id;},
|
391
|
+
SingleFact() do |fact_type|
|
392
|
+
Readings(fact_type).size.should == 2
|
393
|
+
PresenceConstraints(fact_type).size.should == 2
|
394
|
+
end,
|
395
|
+
ConceptCount(3+BaseConcepts),
|
396
|
+
Concept('Thing'),
|
397
|
+
PreferredIdentifier(1),
|
398
|
+
PreferredIdentifierRolePlayedBy('Thing Id'),
|
399
|
+
],
|
400
|
+
|
401
|
+
[ # Auto-create nothing (identifying value type exists already)
|
402
|
+
%q{Thing Id is written as String;},
|
403
|
+
%q{Thing is identified by its Id;},
|
404
|
+
SingleFact() do |fact_type|
|
405
|
+
Readings(fact_type).size.should == 2
|
406
|
+
PresenceConstraints(fact_type).size.should == 2
|
407
|
+
end,
|
408
|
+
ConceptCount(2+BaseConcepts),
|
409
|
+
Concept('Thing'),
|
410
|
+
PreferredIdentifier(1),
|
411
|
+
PreferredIdentifierRolePlayedBy('Thing Id'),
|
412
|
+
],
|
413
|
+
|
414
|
+
[ # Auto-create nothing (identifying entity type exists already so don't create a VT)
|
415
|
+
%q{Id is written as Id;},
|
416
|
+
%q{Thing Id is identified by Id where Thing Id has one Id, Id is of one Thing Id;},
|
417
|
+
%q{Thing is identified by its Id;},
|
418
|
+
FactHavingPlayers("Thing", "Thing Id") do |fact_type|
|
419
|
+
Readings(fact_type).size.should == 2
|
420
|
+
PresenceConstraints(fact_type).size.should == 2
|
421
|
+
end,
|
422
|
+
ConceptCount(3+BaseConcepts),
|
423
|
+
Concept('Thing'),
|
424
|
+
PreferredIdentifier(1),
|
425
|
+
PreferredIdentifierRolePlayedBy('Thing Id'),
|
426
|
+
],
|
427
|
+
|
428
|
+
[
|
429
|
+
%q{Thong is written as String;},
|
430
|
+
%q{Thing is identified by Thong where Thing has one Thong, Thong is of one Thing;},
|
431
|
+
SingleFact() do |fact_type|
|
432
|
+
Readings(fact_type).size.should == 2
|
433
|
+
PresenceConstraints(fact_type).size.should == 2
|
434
|
+
end,
|
435
|
+
ConceptCount(2+BaseConcepts),
|
436
|
+
Concept('Thing'),
|
437
|
+
PreferredIdentifier(1),
|
438
|
+
PreferredIdentifierRolePlayedBy('Thong'),
|
439
|
+
],
|
440
|
+
|
441
|
+
[ # Objectified fact type with internal identification
|
442
|
+
%q{Relationship is where Boy relates to Girl;},
|
443
|
+
SingleFact() do |fact_type|
|
444
|
+
Readings(fact_type).size.should == 1
|
445
|
+
PresenceConstraints(fact_type).size.should == 1
|
446
|
+
end,
|
447
|
+
ConceptCount(1+BaseConcepts),
|
448
|
+
Concept('Relationship'),
|
449
|
+
PreferredIdentifier(2),
|
450
|
+
# PreferredIdentifierRolePlayedBy('Thong'),
|
451
|
+
],
|
452
|
+
|
453
|
+
[ # Objectified fact type with external identification
|
454
|
+
%q{Relationship is identified by its Id where Boy relates to Girl;},
|
455
|
+
ConceptCount(3+BaseConcepts),
|
456
|
+
Concept('Relationship'),
|
457
|
+
PreferredIdentifier(1), # 1 role in PI
|
458
|
+
PreferredIdentifierRolePlayedBy('Relationship Id'),
|
459
|
+
FactHavingPlayers('Relationship', 'Relationship Id') do |fact_type|
|
460
|
+
Readings(fact_type).size.should == 2
|
461
|
+
PresenceConstraints(fact_type).size.should == 2
|
462
|
+
fact_type.all_reading.detect{|r| r.text == '{0} has {1}'}.should_not == nil
|
463
|
+
fact_type.all_reading.detect{|r| r.text == '{0} is of {1}'}.should_not == nil
|
464
|
+
end,
|
465
|
+
FactHavingPlayers('Boy', 'Girl') do |fact_type|
|
466
|
+
fact_type.entity_type.should == @concept
|
467
|
+
end,
|
468
|
+
],
|
469
|
+
|
470
|
+
[ # Objectified fact type with external identification and explicit reading
|
471
|
+
%q{Relationship is identified by its Id where Boy relates to Girl, Relationship is known by Relationship Id;},
|
472
|
+
ConceptCount(3+BaseConcepts),
|
473
|
+
Concept('Relationship'),
|
474
|
+
PreferredIdentifier(1),
|
475
|
+
PreferredIdentifierRolePlayedBy('Relationship Id'),
|
476
|
+
FactHavingPlayers('Relationship', 'Relationship Id') do |fact_type|
|
477
|
+
Readings(fact_type).size.should == 2
|
478
|
+
PresenceConstraints(fact_type).size.should == 2
|
479
|
+
fact_type.all_reading.detect{|r| r.text == '{0} is known by {1}'}.should_not == nil
|
480
|
+
fact_type.all_reading.detect{|r| r.text == '{0} is of {1}'}.should_not == nil
|
481
|
+
end,
|
482
|
+
FactHavingPlayers('Boy', 'Girl') do |fact_type|
|
483
|
+
fact_type.entity_type.should == @concept
|
484
|
+
end,
|
485
|
+
],
|
486
|
+
|
487
|
+
]
|
488
|
+
AllTests =
|
489
|
+
# SimpleBinaryFactTypeTests +
|
490
|
+
EntityIdentificationTests
|
491
|
+
|
492
|
+
before :each do
|
493
|
+
@compiler = ActiveFacts::CQL::Compiler.new('Test')
|
494
|
+
end
|
495
|
+
|
496
|
+
AllTests.each do |tests|
|
497
|
+
it "should process '#{(tests.select{|t| t.is_a?(String)}*' ').gsub(/\s+/m,' ')}' correctly" do
|
498
|
+
tests.each do |test|
|
499
|
+
case test
|
500
|
+
when String
|
501
|
+
result = @compiler.compile(MatchingPrefix+test)
|
502
|
+
puts @compiler.failure_reason unless result
|
503
|
+
result.should_not be_nil
|
504
|
+
when Proc
|
505
|
+
begin
|
506
|
+
test.call(@compiler.vocabulary.constellation)
|
507
|
+
rescue Spec::Example::ExamplePendingError
|
508
|
+
raise
|
509
|
+
rescue => e
|
510
|
+
puts "Failed on\n\t"+tests.select{|t| t.is_a?(String)}*" "
|
511
|
+
raise
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|