activefacts 0.6.0

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 (84) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +83 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +41 -0
  5. data/bin/afgen +46 -0
  6. data/bin/cql +52 -0
  7. data/examples/CQL/Address.cql +46 -0
  8. data/examples/CQL/Blog.cql +54 -0
  9. data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
  10. data/examples/CQL/Death.cql +16 -0
  11. data/examples/CQL/Genealogy.cql +95 -0
  12. data/examples/CQL/Marriage.cql +18 -0
  13. data/examples/CQL/Metamodel.cql +238 -0
  14. data/examples/CQL/MultiInheritance.cql +19 -0
  15. data/examples/CQL/OilSupply.cql +47 -0
  16. data/examples/CQL/Orienteering.cql +108 -0
  17. data/examples/CQL/PersonPlaysGame.cql +17 -0
  18. data/examples/CQL/SchoolActivities.cql +31 -0
  19. data/examples/CQL/SimplestUnary.cql +12 -0
  20. data/examples/CQL/SubtypePI.cql +32 -0
  21. data/examples/CQL/Warehousing.cql +99 -0
  22. data/examples/CQL/WindowInRoomInBldg.cql +22 -0
  23. data/lib/activefacts.rb +10 -0
  24. data/lib/activefacts/api.rb +25 -0
  25. data/lib/activefacts/api/concept.rb +384 -0
  26. data/lib/activefacts/api/constellation.rb +106 -0
  27. data/lib/activefacts/api/entity.rb +239 -0
  28. data/lib/activefacts/api/instance.rb +54 -0
  29. data/lib/activefacts/api/numeric.rb +158 -0
  30. data/lib/activefacts/api/role.rb +94 -0
  31. data/lib/activefacts/api/standard_types.rb +67 -0
  32. data/lib/activefacts/api/support.rb +59 -0
  33. data/lib/activefacts/api/value.rb +122 -0
  34. data/lib/activefacts/api/vocabulary.rb +120 -0
  35. data/lib/activefacts/cql.rb +31 -0
  36. data/lib/activefacts/cql/CQLParser.treetop +104 -0
  37. data/lib/activefacts/cql/Concepts.treetop +112 -0
  38. data/lib/activefacts/cql/DataTypes.treetop +66 -0
  39. data/lib/activefacts/cql/Expressions.treetop +113 -0
  40. data/lib/activefacts/cql/FactTypes.treetop +185 -0
  41. data/lib/activefacts/cql/Language/English.treetop +92 -0
  42. data/lib/activefacts/cql/LexicalRules.treetop +169 -0
  43. data/lib/activefacts/cql/Rakefile +6 -0
  44. data/lib/activefacts/cql/parser.rb +88 -0
  45. data/lib/activefacts/generate/absorption.rb +87 -0
  46. data/lib/activefacts/generate/cql.rb +441 -0
  47. data/lib/activefacts/generate/cql/html.rb +397 -0
  48. data/lib/activefacts/generate/null.rb +19 -0
  49. data/lib/activefacts/generate/ordered.rb +557 -0
  50. data/lib/activefacts/generate/ruby.rb +326 -0
  51. data/lib/activefacts/generate/sql/server.rb +164 -0
  52. data/lib/activefacts/generate/text.rb +21 -0
  53. data/lib/activefacts/input/cql.rb +1268 -0
  54. data/lib/activefacts/input/orm.rb +926 -0
  55. data/lib/activefacts/persistence.rb +1 -0
  56. data/lib/activefacts/persistence/composition.rb +653 -0
  57. data/lib/activefacts/support.rb +51 -0
  58. data/lib/activefacts/version.rb +3 -0
  59. data/lib/activefacts/vocabulary.rb +6 -0
  60. data/lib/activefacts/vocabulary/extensions.rb +343 -0
  61. data/lib/activefacts/vocabulary/metamodel.rb +303 -0
  62. data/script/txt2html +71 -0
  63. data/spec/absorption_spec.rb +95 -0
  64. data/spec/api/autocounter.rb +82 -0
  65. data/spec/api/constellation.rb +130 -0
  66. data/spec/api/entity_type.rb +101 -0
  67. data/spec/api/instance.rb +428 -0
  68. data/spec/api/roles.rb +122 -0
  69. data/spec/api/value_type.rb +112 -0
  70. data/spec/api_spec.rb +14 -0
  71. data/spec/cql_cql_spec.rb +58 -0
  72. data/spec/cql_parse_spec.rb +31 -0
  73. data/spec/cql_ruby_spec.rb +60 -0
  74. data/spec/cql_sql_spec.rb +54 -0
  75. data/spec/cql_symbol_tables_spec.rb +259 -0
  76. data/spec/cql_unit_spec.rb +336 -0
  77. data/spec/cqldump_spec.rb +169 -0
  78. data/spec/norma_cql_spec.rb +48 -0
  79. data/spec/norma_ruby_spec.rb +50 -0
  80. data/spec/norma_sql_spec.rb +45 -0
  81. data/spec/norma_tables_spec.rb +94 -0
  82. data/spec/spec.opts +1 -0
  83. data/spec/spec_helper.rb +10 -0
  84. metadata +173 -0
@@ -0,0 +1,336 @@
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
+ require 'rubygems'
6
+ require 'treetop'
7
+ require 'activefacts/support'
8
+ require 'activefacts/api/support'
9
+ require 'activefacts/cql/parser'
10
+
11
+ describe "Valid Numbers, Strings and Ranges" do
12
+ ValidNumbersEtc = [
13
+ "a=b();", # Basic data type declaration, minimal whitespace
14
+ "a = b ( ) ; ", # Basic data type declaration, maximal whitespace
15
+ "a is defined as b();", # Verbally named data type declaration
16
+ "a=b() inch;", # Data type declaration with unit
17
+ "a=b() inch ; ", # Data type declaration with unit
18
+ "a=b() inch^2 ; ", # Data type declaration with unit and exponent
19
+
20
+ # Comments etc as whitespace
21
+ "\na\n= \nb\n(\n)\n;\n", # Basic data type declaration, newlines for whitespace
22
+ "\ra\r=\rb\r(\r)\r;\r", # Basic data type declaration, returns for whitespace
23
+ "\ta\t=\tb\t(\t)\t;\t", # Basic data type declaration, tabs for whitespace
24
+ " /* Plugh */ a /* Plugh */ =\n b /* *Plugh* / */ ( /* *Plugh* / */ ) /* *Plugh* / */ ; /* *Plugh* / */ ",
25
+ "//Plugh\na // Plugh\n = // Plugh\n b // Plugh\n ( // Plugh\n ) // Plugh\n ; // Plugh\n ",
26
+
27
+ # Integers
28
+ "a=b(0);", # Integer zero
29
+ "a=b( 0 ) ; ", # Integer zero, maximal whitespace
30
+ "a=b(1);", # Integer one
31
+ "a=b(-1);", # Integer negative one
32
+ "a=b(+1);", # Positive integer
33
+ "a=b(1e4);", # Integer with exponent
34
+ "a=b(1e-4);", # Integer with negative exponent
35
+ "a=b(-1e-4);", # Negative integer with negative exponent
36
+ "a=b(077);", # Octal integer
37
+ "a=b(0xFace8);", # Hexadecimal integer
38
+ "a=b(0,1);", # Two parameters
39
+ "a=b( 0 , 1 );",
40
+ "a=b(0,1,2) ;", # Three parameters now allowed
41
+
42
+ # Reals
43
+ "a=b(1.0);",
44
+ "a=b(-1.0);",
45
+ "a=b(+1.0);",
46
+ "a=b(0.1);",
47
+ "a=b(-0.1);",
48
+ "a=b(+0.1);",
49
+ "a=b(0.0);",
50
+ "a=b(-0.0);",
51
+ "a=b(+0.0);",
52
+
53
+ # Integer value restrictions
54
+ "a=b()restricted to{1};", # Integer, minimal whitespace
55
+ "a=b() restricted to { 1 } ;", # Integer, maximal whitespace
56
+ "a=b() restricted to {1..2};", # Integer range, minimal whitespace
57
+ "a=b() restricted to { 1 .. 2 };", # Integer range, maximal whitespace
58
+ "a=b() restricted to {..2};", # Integer range with open start, minimal whitespace
59
+ "a=b() restricted to { .. 2 };", # Integer range with open start, maximal whitespace
60
+ "a=b() restricted to { ..2,3};", # Range followed by integer, minimal whitespace
61
+ "a=b() restricted to { 1,..2,3};", # Integer, open-start range, integer, minimal whitespace
62
+ "a=b() restricted to { .. 2 , 3 };", # Range followed by integer, maximal whitespace
63
+ "a=b() restricted to { ..2 , 3..4 };", # Range followed by range
64
+ "a=b() restricted to { ..2, 3..};", # Range followed by range with open end, minimal whitespace
65
+ "a=b() restricted to { ..2, 3 .. };", # Range followed by range with open end, maximal whitespace
66
+ "a=b() restricted to { 1e4 } ;", # Integer with exponent
67
+ "a=b() restricted to { -1e4 } ;", # Negative integer with exponent
68
+ "a=b() restricted to { 1e-4 } ;", # Integer with negative exponent
69
+ "a=b() restricted to { -1e-4 } ;", # Negative integer with negative exponent
70
+
71
+ # Real value restrictions
72
+ "a=b() restricted to {1.0};", # Real, minimal whitespace
73
+ "a=b() restricted to { 1.0 } ;", # Real, maximal whitespace
74
+ "a=b() restricted to { 1.0e4 } ;", # Real with exponent
75
+ "a=b() restricted to { 1.0e-4 } ;", # Real with negative exponent
76
+ "a=b() restricted to { -1.0e-4 } ;", # Negative real with negative exponent
77
+ "a=b() restricted to { 1.1 .. 2.2 } ;", # Real range, maximal whitespace
78
+ "a=b() restricted to { -1.1 .. 2.2 } ;", # Real range, maximal whitespace
79
+ "a=b() restricted to { 1.1..2.2};", # Real range, minimal whitespace
80
+ "a=b() restricted to { 1.1..2 } ;", # Real-integer range
81
+ "a=b() restricted to { 1..2.2 } ;", # Integer-real range
82
+ "a=b() restricted to { ..2.2};", # Real range with open start
83
+ "a=b() restricted to { 1.1.. };", # Real range with open end
84
+ "a=b() restricted to { 1.1.., 2 };", # Real range with open end and following integer
85
+
86
+ # Strings and string value restrictions
87
+ "a=b() restricted to {''};", # String, empty, minimal whitespace
88
+ "a=b() restricted to {'A'};", # String, minimal whitespace
89
+ "a=b() restricted to { 'A' };", # String, maximal whitespace
90
+ "a=b() restricted to { '\\b\\t\\f\\n\\r\\e\\\\' };", # String with special escapes
91
+ "a=b() restricted to { ' ' };", # String with space
92
+ "a=b() restricted to { '\t' };", # String with literal tab
93
+ "a=b() restricted to { '\\0' };", # String with nul character
94
+ "a=b() restricted to { '\\077' };", # String with octal escape
95
+ "a=b() restricted to { '\\0xA9' };", # String with hexadecimal escape
96
+ "a=b() restricted to { '\\0uBabe' };", # String with unicode escape
97
+ "a=b() restricted to {'A'..'F'};", # String range, minimal whitespace
98
+ "a=b() restricted to { 'A' .. 'F' };", # String range, maximal whitespace
99
+ "a=b() restricted to { ..'F' };", # String range, open start
100
+ "a=b() restricted to { 'A'.. };", # String range, open end
101
+ ]
102
+
103
+ setup do
104
+ @parser = ActiveFacts::CQLParser.new
105
+ end
106
+
107
+ ValidNumbersEtc.each do |c|
108
+ source, ast = *[c].flatten
109
+ it "should parse #{source.inspect}" do
110
+ result = @parser.parse_all(source, :definition)
111
+
112
+ puts @parser.failure_reason unless result
113
+ result.should_not be_nil
114
+ result.map{|d| d.value}.should == ast if ast
115
+ # puts result.map{|d| d.value}.inspect unless ast
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "Invalid Numbers and Strings" do
121
+ InvalidValueTypes = [
122
+ "a=b(08);", # Invalid octalnumber
123
+ "a=b(0xDice);", # Invalid hexadecimal
124
+ "a=b(- 1);", # Invalid negative
125
+ "a=b(+ 1);", # Invalid positive
126
+ "b(- 1e-4);", # Negative integer with negative exponent
127
+ "a=b(-077);", # Invalid negative octal
128
+ "a=b(-0xFace);", # Invalid negative hexadecimal
129
+ "a=b(.0);", # Invalid real
130
+ "a=b(0.);", # Invalid real
131
+ "b() inch ^2 ; ", # Illegal whitespace around unit exponent
132
+ "b() inch^ 2 ; ", # Illegal whitespace around unit exponent
133
+ "b() restricted to { '\\7a' };", # String with bad octal escape
134
+ "b() restricted to { '\001' };", # String with control char
135
+ "b() restricted to { '\n' };", # String with literal newline
136
+ "b() restricted to { 0..'A' };", # Cross-typed range
137
+ "b() restricted to { 'a'..27 };", # Cross-typed range
138
+ ]
139
+
140
+ setup do
141
+ @parser = ActiveFacts::CQLParser.new
142
+ end
143
+
144
+ InvalidValueTypes.each do |c|
145
+ source, ast = *c
146
+ it "should not parse #{source.inspect}" do
147
+ result = @parser.parse_all(source, :definition)
148
+
149
+ # puts @parser.failure_reason unless result.success?
150
+ result.should be_nil
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "Data Types" do
156
+ DataTypes = [
157
+ [ "a = b(1, 2) inch restricted to { 3 .. 4 } inch ;",
158
+ [["a", [:data_type, "b", [ 1, 2 ], "inch", [[3, 4]]]]]
159
+ ],
160
+ # [ "a c = b(1, 2) inch restricted to { 3 .. 4 } inch ;",
161
+ # [["a c", [:data_type, "b", [1, 2], "inch", [[3, 4]]]]]
162
+ # ],
163
+ ]
164
+
165
+ setup do
166
+ @parser = ActiveFacts::CQLParser.new
167
+ end
168
+
169
+ DataTypes.each do |c|
170
+ source, ast = *c
171
+ it "should parse #{source.inspect}" do
172
+ result = @parser.parse_all(source, :definition)
173
+
174
+ puts @parser.failure_reason unless result
175
+ result.should_not be_nil
176
+
177
+ result.map{|d| d.value}.should == ast if ast
178
+ puts result.map{|d| d.value}.inspect unless ast
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "Entity Types" do
184
+ EntityTypes_RefMode = [
185
+ =begin
186
+ [ "a = entity(.id):c;", # Entity type declaration with reference mode
187
+ [["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
188
+ ],
189
+ [ "a = entity ( . id ) : c ;", # Entity type declaration with reference mode, maximal whitespace
190
+ [["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
191
+ ],
192
+ [ "a = entity(.id) where c;", # Entity type declaration with reference mode and where
193
+ [["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
194
+ ],
195
+ =end
196
+ ]
197
+
198
+ EntityTypes_Simple = [
199
+ [ "a is identified by b: c;", # Entity type declaration
200
+ [["a", [:entity_type, [], {:roles=>[["b"]]}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
201
+ ],
202
+ [ "a is identified by b where c;", # Entity type declaration with where
203
+ [["a", [:entity_type, [], {:roles=>[["b"]]}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
204
+ ],
205
+ [ "a is identified by b and c: d;", # Entity type declaration with two-part identifier
206
+ [["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
207
+ ],
208
+ [ "a is identified by b, c: d;", # Entity type declaration with two-part identifier
209
+ [["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
210
+ ],
211
+ [ "a=b(); c is identified by a:d;",
212
+ [["a", [:data_type, "b", [], nil, []]],
213
+ ["c", [:entity_type, [], {:roles=>[["a"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
214
+ ],
215
+ [ " a = b ( ) ; c is identified by a : d ; ",
216
+ [["a", [:data_type, "b", [ ], nil, []]],
217
+ ["c", [:entity_type, [], {:roles=>[["a"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
218
+ ],
219
+ [ "a is identified by c:maybe d;",
220
+ [["a", [:entity_type, [], {:roles=>[["c"]]}, [[:fact_clause, ["maybe"], [{:word=>"d"}]]]]]]
221
+ ],
222
+ ]
223
+
224
+ EntityTypes_Objectified = [
225
+ [ "Director = Person directs Company, Company is directed by Person;",
226
+ [["Director", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"Company"}]], [:fact_clause, [], [{:word=>"Company"}, {:word=>"is"}, {:word=>"directed"}, {:word=>"by"}, {:word=>"Person"}]]], []]]]
227
+ ],
228
+ [ "Director: Person directs company;",
229
+ [[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}]]]]]]
230
+ ],
231
+ ]
232
+
233
+ EntityTypes_Subtypes = [
234
+ [ "Employee is a kind of Person;",
235
+ [["Employee", [:entity_type, ["Person"], nil, nil]]]
236
+ ],
237
+ [ "Employee is a subtype of Person;",
238
+ [["Employee", [:entity_type, ["Person"], nil, nil]]]
239
+ ],
240
+ [ "Employee is defined as subtype of Person;",
241
+ [["Employee", [:entity_type, ["Person"], nil, nil]]]
242
+ ],
243
+ [ "AustralianEmployee is a subtype of Employee, Australian;",
244
+ [["AustralianEmployee", [:entity_type, ["Employee", "Australian"], nil, nil]]]
245
+ ],
246
+ [ "Employee is a kind of Person identified by EmployeeNumber;",
247
+ [["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
248
+ ],
249
+ [ "Employee is a subtype of Person identified by EmployeeNumber;",
250
+ [["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
251
+ ],
252
+ [ "Employee is defined as subtype of Person identified by EmployeeNumber;",
253
+ [["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
254
+ ],
255
+ [ "AustralianEmployee is a subtype of Employee, Australian identified by TaxFileNumber;",
256
+ [["AustralianEmployee", [:entity_type, ["Employee", "Australian"], {:roles=>[["TaxFileNumber"]]}, nil]]]
257
+ ],
258
+ ]
259
+
260
+ EntityTypes =
261
+ EntityTypes_RefMode +
262
+ EntityTypes_Simple +
263
+ EntityTypes_Objectified +
264
+ EntityTypes_Subtypes
265
+
266
+ setup do
267
+ @parser = ActiveFacts::CQLParser.new
268
+ end
269
+
270
+ EntityTypes.each do |c|
271
+ source, ast = *c
272
+ it "should parse #{source.inspect}" do
273
+ result = @parser.parse_all(source, :definition)
274
+
275
+ puts @parser.failure_reason unless result
276
+
277
+ result.should_not be_nil
278
+ if ast
279
+ result.map{|d| d.value}.should == ast
280
+ else
281
+ puts "\n"+result.map{|d| d.value}.inspect
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ describe "Fact Types" do
288
+ FactTypes = [
289
+ [ "Director is old: Person directs company, Person is of age, age > 60;",
290
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}, {:word=>"is"}, {:word=>"old"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}]], [:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"of"}, {:word=>"age"}]], [">", [:variable, "age"], 60]]]]
291
+ ],
292
+ [ "a: maybe a has completely- green b -totally [transitive, acyclic], b -c = 2;",
293
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}]]], [[:fact_clause, ["maybe", "transitive", "acyclic"], [{:word=>"a"}, {:word=>"has"}, {:word=>"green", :leading_adjective=>"completely"}, {:word=>"b", :trailing_adjective=>"totally"}]], ["=", [:+, [:variable, "b"], [:-, [:variable, "c"]]], 2]]]]
294
+ ],
295
+ [ "Person is independent: Person has taxable- Income, taxable Income >= 20000 dollars;",
296
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"independent"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"taxable", :word=>"Income"}]], [">=", [:variable, "taxable", "Income"], [20000, "dollars"]]]]]
297
+ ],
298
+ [ "Window requires toughening: Window has width-mm, Window has height-mm, width mm * height mm >= 10 foot^2;",
299
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Window"}, {:word=>"requires"}, {:word=>"toughening"}]]], [[:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"width", :word=>"mm"}]], [:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"height", :word=>"mm"}]], [">=", [:*, [:variable, "width", "mm"], [:variable, "height", "mm"]], [10, "foot^2"]]]]]
300
+ ],
301
+ # REVISIT: Test all quantifiers
302
+ # REVISIT: Test all post-qualifiers
303
+ # REVISIT: Test functions
304
+ [ "AnnualIncome is where Person has total- Income in Year: Person has total- Income.sum(), Income was earned in current- time.Year() (as Year);",
305
+ ["AnnualIncome", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"total", :word=>"Income"}, {:word=>"in"}, {:word=>"Year"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:function=>[:"(", "sum"], :leading_adjective=>"total", :word=>"Income"}]], [:fact_clause, [], [{:word=>"Income"}, {:word=>"was"}, {:word=>"earned"}, {:word=>"in"}, {:function=>[:"(", "Year"], :word=>"time", :role_name=>"Year", :leading_adjective=>"current"}]]]]]
306
+ ],
307
+ [ "a is interesting : b- c -d has e- f -g;",
308
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}, {:word=>"is"}, {:word=>"interesting"}]]], [[:fact_clause, [], [{:trailing_adjective=>"d", :word=>"c", :leading_adjective=>"b"}, {:word=>"has"}, {:trailing_adjective=>"g", :word=>"f", :leading_adjective=>"e"}]]]]]
309
+ ]
310
+ ]
311
+
312
+ setup do
313
+ @parser = ActiveFacts::CQLParser.new
314
+ end
315
+
316
+ FactTypes.each do |c|
317
+ source, ast, definition = *c
318
+ it "should parse #{source.inspect}" do
319
+ definitions = @parser.parse_all(source, :definition)
320
+
321
+ puts @parser.failure_reason unless definitions
322
+
323
+ definitions.should_not be_nil
324
+ result = definitions[-1]
325
+
326
+ if (definition)
327
+ result.definition.should == definition
328
+ else
329
+ #p @parser.definition(result)
330
+ end
331
+
332
+ result.value.should == ast if ast
333
+ puts result.map{|d| d.value}.inspect unless ast
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,169 @@
1
+ #
2
+ # ActiveFacts tests: Test the generated CQL for some simple cases.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'stringio'
7
+ require 'activefacts/support'
8
+ require 'activefacts/vocabulary'
9
+ require 'activefacts/generate/cql'
10
+
11
+ include ActiveFacts
12
+
13
+ describe "CQL Dumper" do
14
+ def self.hide(*a,&b)
15
+ end
16
+
17
+ setup do
18
+ @constellation = ActiveFacts::API::Constellation.new(ActiveFacts::Metamodel)
19
+ @vocabulary = @constellation.Vocabulary("TestVocab")
20
+ @string_type = @constellation.ValueType("String", @vocabulary)
21
+ @integer_type = @constellation.ValueType("Integer", @vocabulary)
22
+ @dumper = ActiveFacts::Generate::CQL.new(@constellation)
23
+ end
24
+
25
+ def cql
26
+ output = StringIO.new
27
+ @dumper.generate(output)
28
+ output.rewind
29
+ output.read
30
+ end
31
+
32
+ it "should dump a String ValueType" do
33
+ vt = @constellation.ValueType("Name", @vocabulary, :supertype => @string_type, :length => 20)
34
+ vt.supertype = @string_type
35
+ vt.length = 20
36
+ #p vt.class.roles.keys.sort_by{|s| s.to_s}
37
+ #p vt.supertype
38
+ cql.should == <<END
39
+ vocabulary TestVocab;
40
+
41
+ /*
42
+ * Value Types
43
+ */
44
+ Name is defined as String(20);
45
+
46
+ END
47
+ end
48
+
49
+ it "should dump an Integer ValueType" do
50
+ vt = @constellation.ValueType("Count", @vocabulary, :supertype => @integer_type, :length => 32)
51
+ cql.should == <<END
52
+ vocabulary TestVocab;
53
+
54
+ /*
55
+ * Value Types
56
+ */
57
+ Count is defined as Integer(32);
58
+
59
+ END
60
+ end
61
+
62
+ def value_type(name, datatype = "String", length = 0, scale = 0)
63
+ dt = @constellation.ValueType(datatype, @vocabulary)
64
+ vt = @constellation.ValueType(name, @vocabulary, :supertype => dt)
65
+ vt.length = length if length != 0
66
+ vt.scale = scale if scale != 0
67
+ vt
68
+ end
69
+
70
+ def one_to_many(one, many, reading)
71
+ # Join them with a fact type:
72
+ ft = @constellation.FactType(:new)
73
+ role0 = @constellation.Role(ft, 0, one)
74
+ role1 = @constellation.Role(ft, 1, many)
75
+
76
+ # Make a role sequence:
77
+ rs = @constellation.RoleSequence(:new)
78
+ rr0 = @constellation.RoleRef(rs, 0, :role => role0)
79
+ rr1 = @constellation.RoleRef(rs, 1, :role => role1)
80
+
81
+ # Make a uniqueness constraint:
82
+ pcrs = @constellation.RoleSequence(:new)
83
+ @constellation.RoleRef(pcrs, 0, :role => role0)
84
+ pc = @constellation.PresenceConstraint(:new, :is_mandatory => false, :is_preferred_identifier => false, :max_frequency => 1, :min_frequency => 0, :role_sequence => pcrs)
85
+
86
+ # Make a new reading:
87
+ reading = @constellation.Reading(ft, ft.all_reading.size, :role_sequence => rs, :reading_text => reading)
88
+
89
+ ft
90
+ end
91
+
92
+ def one_to_one(first, second, reading)
93
+ # Join them with a fact type:
94
+ ft = @constellation.FactType(:new)
95
+ role0 = @constellation.Role(ft, 0, first)
96
+ role1 = @constellation.Role(ft, 1, second)
97
+
98
+ # Make a role sequence for the reading
99
+ rs = @constellation.RoleSequence(:new)
100
+ rr0 = @constellation.RoleRef(rs, 0, :role => role0)
101
+ rr1 = @constellation.RoleRef(rs, 1, :role => role1)
102
+
103
+ # Make a new reading:
104
+ reading = @constellation.Reading(ft, ft.all_reading.size, :role_sequence => rs, :reading_text => reading)
105
+
106
+ # Make a uniqueness constraint for the first role
107
+ first_rs = @constellation.RoleSequence(:new)
108
+ @constellation.RoleRef(first_rs, 0, :role => role0)
109
+ first_pc = @constellation.PresenceConstraint(:new, :is_mandatory => true, :is_preferred_identifier => false, :max_frequency => 1, :min_frequency => 1, :role_sequence => first_rs)
110
+
111
+ # Make a uniqueness constraint for the second role
112
+ second_rs = @constellation.RoleSequence(:new)
113
+ @constellation.RoleRef(second_rs, 0, :role => role1)
114
+ second_pc = @constellation.PresenceConstraint(:new, :is_mandatory => true, :is_preferred_identifier => true, :max_frequency => 1, :min_frequency => 1, :role_sequence => second_rs)
115
+
116
+ ft
117
+ end
118
+
119
+ it "should dump a VT-VT FactType" do
120
+ # Make two valuetypes:
121
+ st = value_type("Name", "String", 20)
122
+ vt = value_type("Count", "Integer", 32)
123
+
124
+ ft = one_to_many(st, vt, "{0} occurs {1} times")
125
+
126
+ #puts @constellation.verbalise #; exit
127
+
128
+ cql.should == <<END
129
+ vocabulary TestVocab;
130
+
131
+ /*
132
+ * Value Types
133
+ */
134
+ Count is defined as Integer(32);
135
+ Name is defined as String(20);
136
+
137
+ /*
138
+ * Fact Types
139
+ */
140
+ Name occurs Count times;
141
+
142
+ END
143
+ end
144
+
145
+ it "should dump an named EntityType" do
146
+ vt = @constellation.ValueType("Name", @vocabulary, :supertype => @string_type, :length => 20)
147
+ et = @constellation.EntityType("Company", @vocabulary)
148
+
149
+ ft = one_to_one(et, vt, "{0} is called {1}")
150
+
151
+ cql.should == <<END
152
+ vocabulary TestVocab;
153
+
154
+ /*
155
+ * Value Types
156
+ */
157
+ Name is defined as String(20);
158
+
159
+ /*
160
+ * Entity Types
161
+ */
162
+ Company is identified by Name where
163
+ Company is called Name;
164
+
165
+ END
166
+
167
+ end
168
+
169
+ end