activefacts 0.8.9 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. data/spec/spec.opts +0 -1
@@ -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 'spec_helper'
7
7
  require 'stringio'
8
8
  require 'activefacts/vocabulary'
9
9
  require 'activefacts/support'
@@ -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 'spec_helper'
7
7
  require 'stringio'
8
8
  require 'activefacts/vocabulary'
9
9
  require 'activefacts/support'
@@ -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 'spec_helper'
7
7
  require 'stringio'
8
8
  require 'activefacts/vocabulary'
9
9
  require 'activefacts/support'
@@ -58,8 +58,8 @@ describe "CQL Loader with SQL output" do
58
58
  }
59
59
  else
60
60
  # Discard index names:
61
- sql_text.gsub!(/(CREATE .* INDEX )([^ ]* )ON /, '\1ON ')
62
- expected_text.gsub!(/(CREATE .* INDEX )([^ ]* )ON /, '\1ON ')
61
+ sql_text.gsub!(/ INDEX (\[[^\]]*\]|`[^`]*`|[^ ]*) ON /, ' INDEX <Name is hidden> ON ')
62
+ expected_text.gsub!(/ INDEX (\[[^\]]*\]|`[^`]*`|[^ ]*) ON /, ' INDEX <Name is hidden> ON ')
63
63
  sql_text.should_not differ_from(expected_text)
64
64
  File.delete(actual_file) # It succeeded, we don't need the file.
65
65
  end
@@ -11,8 +11,8 @@ require 'activefacts/input/cql'
11
11
 
12
12
  describe "CQL Symbol table" do
13
13
 
14
- # A Form here is a form of reference to a concept, being a name and optional adjectives, possibly designated by a role name:
15
- # Struct.new("Form", :concept, :name, :leading_adjective, :trailing_adjective, :role_name)
14
+ # A Form here is a form of reference to a object_type, being a name and optional adjectives, possibly designated by a role name:
15
+ # Struct.new("Form", :object_type, :name, :leading_adjective, :trailing_adjective, :role_name)
16
16
  # def initialize(constellation, vocabulary)
17
17
  # def bind(words, leading_adjective = nil, trailing_adjective = nil, role_name = nil, allowed_forward = false, leading_speculative = false, trailing_speculative = false)
18
18
 
@@ -106,7 +106,7 @@ describe "CQL Symbol table" do
106
106
 
107
107
  player, bound = @symbols.bind(*args)
108
108
  player.should_not be_nil
109
- player.should == bound.concept
109
+ player.should == bound.object_type
110
110
  [bound.name, bound.leading_adjective, bound.trailing_adjective, bound.role_name].should == result
111
111
  else
112
112
  lambda {player, bound = @symbols.bind(*args)}.should raise_error
@@ -115,17 +115,17 @@ describe "CQL Symbol table" do
115
115
  end
116
116
 
117
117
  it "should disallow binding to a role name where a leading adjective is provided" do
118
- concept, = @symbols.bind("Name", nil, nil, "GivenName", true)
118
+ object_type, = @symbols.bind("Name", nil, nil, "GivenName", true)
119
119
  lambda{player, bound = @symbols.bind("GivenName", "SomeAdj")}.should raise_error
120
120
  end
121
121
 
122
122
  it "should disallow binding to a role name where a trailing adjective is provided" do
123
- concept, = @symbols.bind("Name", nil, nil, "GivenName", true)
123
+ object_type, = @symbols.bind("Name", nil, nil, "GivenName", true)
124
124
  lambda{player, bound = @symbols.bind("GivenName", nil, "SomeAdj")}.should raise_error
125
125
  end
126
126
 
127
127
  it "should disallow binding to a role name and defining a new role name" do
128
- concept, = @symbols.bind("Name", nil, nil, "GivenName", true)
128
+ object_type, = @symbols.bind("Name", nil, nil, "GivenName", true)
129
129
  lambda{player, bound = @symbols.bind("GivenName", nil, nil, "SomeName")}.should raise_error
130
130
  end
131
131
 
@@ -137,50 +137,50 @@ describe "CQL Symbol table" do
137
137
  ]
138
138
 
139
139
  it "should allow adding a role name to an adjectival form without one" do
140
- concept, = @symbols.bind("Name", "Given", nil, nil, true)
140
+ object_type, = @symbols.bind("Name", "Given", nil, nil, true)
141
141
  player, bound = @symbols.bind("Name", nil, nil, "GivenName")
142
- player.should == concept
142
+ player.should == object_type
143
143
  end
144
144
 
145
145
  it "should create new binding with a role name rather than binding to existing simple player without adjectives" do
146
- concept, bare = @symbols.bind("Name", nil, nil, nil, true)
146
+ object_type, bare = @symbols.bind("Name", nil, nil, nil, true)
147
147
  player, bound = @symbols.bind("Name", nil, nil, "SomeAdj")
148
148
  bare.should_not == bound
149
149
  end
150
150
 
151
- it "should disallow adding a role name which is the name of an existing concept" do
152
- concept, = @symbols.bind("Name", "Given", nil, nil, true)
151
+ it "should disallow adding a role name which is the name of an existing object_type" do
152
+ object_type, = @symbols.bind("Name", "Given", nil, nil, true)
153
153
  lambda{player, bound = @symbols.bind("Name", "Given", nil, "Date")}.should raise_error
154
154
  end
155
155
 
156
156
  it "should disallow adding a role name to a role player that already has one" do
157
- concept, first = @symbols.bind("Name", "Given", nil, "GivenName", true)
157
+ object_type, first = @symbols.bind("Name", "Given", nil, "GivenName", true)
158
158
  player, bound = @symbols.bind("Name", "Given", nil, "FirstName")
159
159
  first.should_not == bound
160
160
  end
161
161
 
162
162
  it "should bind to an existing player without adjectives" do
163
- concept, = @symbols.bind("Name", nil, nil, nil, true)
163
+ object_type, = @symbols.bind("Name", nil, nil, nil, true)
164
164
  player, bound = @symbols.bind("Name")
165
- player.should == concept
165
+ player.should == object_type
166
166
  end
167
167
 
168
168
  it "should bind to an existing player using a leading adjective" do
169
- concept, = @symbols.bind("Name", nil, nil, nil, true)
169
+ object_type, = @symbols.bind("Name", nil, nil, nil, true)
170
170
  player, bound = @symbols.bind("Name", "Given")
171
- player.should == concept
171
+ player.should == object_type
172
172
  end
173
173
 
174
174
  it "should bind to an existing player using a trailing adjective" do
175
- concept, = @symbols.bind("Name", nil, nil, nil, true)
175
+ object_type, = @symbols.bind("Name", nil, nil, nil, true)
176
176
  player, bound = @symbols.bind("Name", nil, "Donné")
177
- player.should == concept
177
+ player.should == object_type
178
178
  end
179
179
 
180
180
  it "should bind to an existing player only using the defined leading adjective" do
181
- concept, = @symbols.bind("Name", "Given", nil, nil, true)
181
+ object_type, = @symbols.bind("Name", "Given", nil, nil, true)
182
182
  player, bound = @symbols.bind("Name", "Given")
183
- player.should == concept
183
+ player.should == object_type
184
184
  forms = [bound]
185
185
 
186
186
  player, alt = @symbols.bind("Name")
@@ -196,9 +196,9 @@ describe "CQL Symbol table" do
196
196
  end
197
197
 
198
198
  it "should bind to an existing player only using a defined trailing adjective" do
199
- concept, = @symbols.bind("Name", nil, "Donné", nil, true)
199
+ object_type, = @symbols.bind("Name", nil, "Donné", nil, true)
200
200
  player, bound = @symbols.bind("Name", nil, "Donné")
201
- player.should == concept
201
+ player.should == object_type
202
202
  forms = [bound]
203
203
 
204
204
  player, alt = @symbols.bind("Name")
@@ -214,9 +214,9 @@ describe "CQL Symbol table" do
214
214
  end
215
215
 
216
216
  it "should bind to an existing player only using defined leading and trailing adjective" do
217
- concept, = @symbols.bind("Name", "Given", "Donné", nil, true)
217
+ object_type, = @symbols.bind("Name", "Given", "Donné", nil, true)
218
218
  player, bound = @symbols.bind("Name", "Given", "Donné")
219
- player.should == concept
219
+ player.should == object_type
220
220
  forms = [bound]
221
221
 
222
222
  player, alt = @symbols.bind("Name")
@@ -232,9 +232,9 @@ describe "CQL Symbol table" do
232
232
  end
233
233
 
234
234
  it "should bind to an existing player using a speculative leading adjective" do
235
- concept, = @symbols.bind("Name", "Given", nil, nil, true)
235
+ object_type, = @symbols.bind("Name", "Given", nil, nil, true)
236
236
  player, bound = @symbols.bind("Name", l = "Given", t = "Donné", nil, nil, true, true)
237
- player.should == concept
237
+ player.should == object_type
238
238
  bound.leading_adjective.should == "Given"
239
239
  bound.trailing_adjective.should be_nil
240
240
  l.should be_empty
@@ -242,17 +242,17 @@ describe "CQL Symbol table" do
242
242
  end
243
243
 
244
244
  it "should bind to an existing player using a speculative trailing adjective" do
245
- concept, = @symbols.bind("Name", nil, "Donné", nil, true)
245
+ object_type, = @symbols.bind("Name", nil, "Donné", nil, true)
246
246
  player, bound = @symbols.bind("Name", l = "Given", t = "Donné", nil, nil, true, true)
247
- player.should == concept
247
+ player.should == object_type
248
248
  l.should == "Given"
249
249
  t.should be_empty
250
250
  end
251
251
 
252
252
  it "should bind to an existing player using a speculative leading and trailing adjective" do
253
- concept, = @symbols.bind("Name", "Given", "Donné", nil, true)
253
+ object_type, = @symbols.bind("Name", "Given", "Donné", nil, true)
254
254
  player, bound = @symbols.bind("Name", l = "Given", t = "Donné", nil, nil, true, true)
255
- player.should == concept
255
+ player.should == object_type
256
256
  l.should be_empty
257
257
  t.should be_empty
258
258
  end
@@ -68,8 +68,8 @@ END
68
68
  def one_to_many(one, many, reading)
69
69
  # Join them with a fact type:
70
70
  ft = @constellation.FactType(:new)
71
- role0 = @constellation.Role(ft, 0, :concept => one)
72
- role1 = @constellation.Role(ft, 1, :concept => many)
71
+ role0 = @constellation.Role(ft, 0, :object_type => one)
72
+ role1 = @constellation.Role(ft, 1, :object_type => many)
73
73
 
74
74
  # Make a role sequence:
75
75
  rs = @constellation.RoleSequence(:new)
@@ -90,8 +90,8 @@ END
90
90
  def one_to_one(first, second, reading)
91
91
  # Join them with a fact type:
92
92
  ft = @constellation.FactType(:new)
93
- role0 = @constellation.Role(ft, 0, :concept => first)
94
- role1 = @constellation.Role(ft, 1, :concept => second)
93
+ role0 = @constellation.Role(ft, 0, :object_type => first)
94
+ role1 = @constellation.Role(ft, 1, :object_type => second)
95
95
 
96
96
  # Make a role sequence for the reading
97
97
  rs = @constellation.RoleSequence(:new)
@@ -1,35 +1,40 @@
1
- require 'pathname'
1
+ module RSpec
2
+ module Matchers
3
+ class ArrayMatcher < Matcher
4
+ def initialize expected
5
+ super(:be_different_array_from, expected) do |*_expected|
6
+ match_for_should do |actual|
7
+ perform_match(actual, _expected[0])
8
+ @extra + @missing != []
9
+ end
2
10
 
3
- module ArrayMatcher
4
- class BeDifferentArray
5
- def initialize(expected)
6
- @expected = expected
7
- end
11
+ match_for_should_not do |actual|
12
+ perform_match(actual, _expected[0])
13
+ @extra + @missing == []
14
+ end
8
15
 
9
- def matches?(actual)
10
- @extra = actual - @expected
11
- @missing = @expected - actual
12
- @extra + @missing != [] # Because the predicate is "be_different_array_from", the sense is inverted
13
- end
16
+ def perform_match(actual, expected)
17
+ @extra = actual - expected
18
+ @missing = expected - actual
19
+ end
14
20
 
15
- def failure_message
16
- "expected a difference in the two lists, but got none"
17
- end
21
+ def failure_message_for_should
22
+ "expected a difference in the two lists, but got none"
23
+ end
18
24
 
19
- def negative_failure_message
20
- "expected no difference, but result #{
21
- [ (@missing.empty? ? nil : 'lacks '+@missing.sort.inspect),
22
- (@extra.empty? ? nil : 'has extra '+@extra.sort.inspect)
23
- ].compact * ' and '
24
- }"
25
+ failure_message_for_should_not do |actual|
26
+ "expected no difference, but result #{
27
+ [ (@missing.empty? ? nil : 'lacks '+@missing.sort.inspect),
28
+ (@extra.empty? ? nil : 'has extra '+@extra.sort.inspect)
29
+ ].compact * ' and '
30
+ }"
31
+ end
32
+ end
33
+ end
25
34
  end
26
- end
27
35
 
28
- def be_different_array_from(expected)
29
- BeDifferentArray.new(expected)
36
+ def be_different_array_from(expected)
37
+ ArrayMatcher.new(expected)
38
+ end
30
39
  end
31
40
  end
32
-
33
- Spec::Runner.configure do |config|
34
- config.include(ArrayMatcher)
35
- end
@@ -1,38 +1,18 @@
1
1
  require 'pathname'
2
2
 
3
- module DiffMatcher
4
- class DifferFrom
5
- def initialize(expected)
3
+ module RSpec
4
+ module Matchers
5
+ def differ_from(expected)
6
6
  case expected
7
7
  when Pathname
8
- @matcher = FileMatcher::BeDifferentFile.new(expected)
8
+ FileMatcher.new(expected)
9
9
  when Array
10
- @matcher = ArrayMatcher::BeDifferentArray.new(expected)
10
+ ArrayMatcher.new(expected)
11
11
  when String
12
- @matcher = FileMatcher::BeDifferentFile.new(expected)
12
+ FileMatcher.new(expected)
13
13
  else
14
14
  raise "DiffMatcher doesn't know how to match a #{expected.class}"
15
15
  end
16
16
  end
17
-
18
- def matches?(actual)
19
- @matcher.matches?(actual)
20
- end
21
-
22
- def failure_message
23
- @matcher.failure_message
24
- end
25
-
26
- def negative_failure_message
27
- @matcher.negative_failure_message
28
- end
29
- end
30
-
31
- def differ_from(expected)
32
- DifferFrom.new(expected)
33
17
  end
34
18
  end
35
-
36
- Spec::Runner.configure do |config|
37
- config.include(DiffMatcher)
38
- end
@@ -1,41 +1,50 @@
1
1
  require 'diff/lcs'
2
2
 
3
- module FileMatcher
4
- class BeDifferentFile
5
- def initialize(expected)
6
- expected = File.open(expected).read if expected.is_a?(Pathname)
7
- @expected = expected.scan(/[^\n]+/)
8
- end
3
+ module RSpec
4
+ module Matchers
5
+ class FileMatcher < Matcher
6
+ def initialize expected
7
+ super(:have_different_contents, expected) do |*_expected|
8
+ match_for_should do |actual|
9
+ perform_match(actual, _expected[0])
10
+ end
9
11
 
10
- def matches?(actual)
11
- actual = File.open(actual).read if actual.is_a?(Pathname)
12
- actual_lines = actual.scan(/[^\n]+/)
13
- differences = Diff::LCS::diff(@expected, actual_lines)
14
- @diff = differences.map do |chunk|
15
- added_at = (add = chunk.detect{|d| d.action == '+'}) && add.position+1
16
- removed_at = (remove = chunk.detect{|d| d.action == '-'}) && remove.position+1
17
- "Line #{added_at}/#{removed_at}:\n"+
18
- chunk.map do |change|
19
- "#{change.action} #{change.element}"
20
- end*"\n"
21
- end*"\n"
22
- @diff != ''
23
- end
12
+ match_for_should_not do |actual|
13
+ !perform_match(actual, _expected[0])
14
+ end
24
15
 
25
- def failure_message
26
- "expected a difference, but got none"
27
- end
16
+ def perform_match(actual, expected)
17
+ expected = File.open(expected).read if expected.is_a?(Pathname)
18
+ expected = expected.scan(/[^\n]+/) unless expected.is_a?(Array)
19
+
20
+ actual = File.open(actual).read if actual.is_a?(Pathname)
21
+ actual = actual.scan(/[^\n]+/) unless actual.is_a?(Array)
22
+
23
+ differences = Diff::LCS::diff(expected, actual)
24
+ @diff = differences.map do |chunk|
25
+ added_at = (add = chunk.detect{|d| d.action == '+'}) && add.position+1
26
+ removed_at = (remove = chunk.detect{|d| d.action == '-'}) && remove.position+1
27
+ "Line #{added_at}/#{removed_at}:\n"+
28
+ chunk.map do |change|
29
+ "#{change.action} #{change.element}"
30
+ end*"\n"
31
+ end*"\n"
32
+ @diff != ''
33
+ end
28
34
 
29
- def negative_failure_message
30
- "expected no difference, but got:\n#{@diff}"
35
+ def failure_message_for_should
36
+ "expected a difference, but got none"
37
+ end
38
+
39
+ failure_message_for_should_not do |actual|
40
+ "expected no difference, but got:\n#{@diff}"
41
+ end
42
+ end
43
+ end
31
44
  end
32
- end
33
45
 
34
- def have_different_contents(expected)
35
- BeDifferentFile.new(expected)
46
+ def have_different_contents(expected)
47
+ FileMatcher.new(expected)
48
+ end
36
49
  end
37
50
  end
38
-
39
- Spec::Runner.configure do |config|
40
- config.include(FileMatcher)
41
- end
@@ -0,0 +1,76 @@
1
+ require 'rspec/expectations'
2
+
3
+ require 'activefacts/cql'
4
+ require 'activefacts/support'
5
+ require 'activefacts/api/support'
6
+ require 'helpers/test_parser'
7
+
8
+ RSpec::Matchers.define :parse_to_ast do |*expected_asts|
9
+ def canonicalise(s)
10
+ if s.is_a?(Array)
11
+ s.map{|e| canonicalise(e)}
12
+ else
13
+ s.to_s.gsub(/\s+/,' ').strip
14
+ end
15
+ end
16
+
17
+ match do |actual|
18
+ @parser = TestParser.new
19
+ @result = @parser.parse_all(actual, :definition)
20
+
21
+ # If the expected_asts is "false", treat this test as pending:
22
+ if expected_asts == [false]
23
+ if @result
24
+ # Unfortunately there's no way to say why the failure was expected here
25
+ # RSpec stores it in example.metadata[:execution_result][:pending_message]
26
+ raise RSpec::Core::PendingExampleFixedError.new
27
+ else
28
+ throw :pending_declared_in_example, "Should parse #{actual.strip.inspect}"
29
+ end
30
+ end
31
+
32
+ # If we failed to parse, fail and say why:
33
+ next false unless @result
34
+
35
+ # Otherwise compare the canonical form of the AST
36
+ @canonical_form = @result.map{|d| canonicalise(d.ast)}
37
+
38
+ # If we weren't given an AST, this test is pending. Show what result we obtained:
39
+ throw :pending_declared_in_example, actual.inspect+' should parse to ['+@canonical_form*', '+']' if expected_asts.empty?
40
+
41
+ @canonical_form == canonicalise(expected_asts)
42
+ end
43
+
44
+ failure_message_for_should do
45
+ if !@result
46
+ @parser.failure_reason
47
+ else
48
+ "Expected %q{#{canonicalise(expected_asts)}}\nbut got: %q{#{@canonical_form}}"
49
+ end
50
+ end
51
+ end
52
+
53
+ RSpec::Matchers.define :fail_to_parse do |*error_regexp|
54
+ match do |actual|
55
+ @parser = TestParser.new
56
+ @actual = actual
57
+ @result = @parser.parse_all(actual, :definition)
58
+ @result.should be_nil
59
+ if @re = error_regexp[0]
60
+ @parser.failure_reason.should =~ @re
61
+ else
62
+ throw :pending_declared_in_example, actual.inspect+' fails, please add message pattern to match '+@parser.failure_reason.inspect
63
+ end
64
+ end
65
+
66
+ failure_message_for_should do
67
+ if @result
68
+ @canonical_form = @result.map{|d| canonicalise(d.ast)}
69
+ "Expected not to succeed in parsing #{actual.inspect}\nbut got #{@canonical_form.inspect}"
70
+ else
71
+ "Failed as expected in parsing #{actual.inspect}\n" +
72
+ "but not for the right reason: #{@re.inspect}\n"+
73
+ "got #{@parser.failure_reason.inspect}"
74
+ end
75
+ end
76
+ end