cql 1.4.0 → 1.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 (52) hide show
  1. checksums.yaml +5 -5
  2. data/lib/cql.rb +4 -169
  3. data/lib/cql/dsl.rb +39 -5
  4. data/lib/cql/feature_filters.rb +2 -0
  5. data/lib/cql/filters.rb +25 -1
  6. data/lib/cql/map_reduce.rb +3 -0
  7. data/lib/cql/model_dsl.rb +7 -1
  8. data/lib/cql/queriable.rb +6 -1
  9. data/lib/cql/query.rb +157 -0
  10. data/lib/cql/repository.rb +25 -0
  11. data/lib/cql/sso_filters.rb +4 -0
  12. data/lib/cql/version.rb +2 -1
  13. data/testing/cucumber/features/clauses/as_clause.feature +10 -9
  14. data/testing/cucumber/features/clauses/from_clause.feature +15 -20
  15. data/testing/cucumber/features/clauses/predefined_with_filters.feature +392 -0
  16. data/testing/cucumber/features/clauses/select_clause.feature +11 -14
  17. data/testing/cucumber/features/clauses/transform_clause.feature +10 -9
  18. data/testing/cucumber/features/clauses/with_clause.feature +10 -170
  19. data/testing/cucumber/features/dsl.feature +15 -34
  20. data/testing/cucumber/step_definitions/setup_steps.rb +1 -13
  21. data/testing/cucumber/step_definitions/verification_steps.rb +6 -7
  22. data/testing/cucumber/support/env.rb +9 -9
  23. data/testing/gemfiles/cuke_modeler0.gemfile +10 -7
  24. data/testing/gemfiles/cuke_modeler1.gemfile +6 -7
  25. data/testing/gemfiles/cuke_modeler2.gemfile +33 -0
  26. data/testing/gemfiles/cuke_modeler3.gemfile +10 -0
  27. data/testing/helper_methods.rb +13 -0
  28. data/testing/model_helper.rb +28 -0
  29. data/testing/rspec/spec/clauses/as_clause_spec.rb +1 -0
  30. data/testing/rspec/spec/clauses/from_clause_spec.rb +146 -0
  31. data/testing/rspec/spec/clauses/select_clause_spec.rb +184 -0
  32. data/testing/rspec/spec/clauses/transform_clause_spec.rb +35 -0
  33. data/testing/rspec/spec/clauses/with_clause_spec.rb +84 -0
  34. data/testing/rspec/spec/clauses/without_clause_spec.rb +171 -0
  35. data/testing/rspec/spec/cql_spec.rb +25 -0
  36. data/testing/rspec/spec/dsl_spec.rb +3 -575
  37. data/testing/rspec/spec/filter_example_spec.rb +1 -1
  38. data/testing/rspec/spec/filter_feature_dsl_spec.rb +13 -13
  39. data/testing/rspec/spec/filter_sso_spec.rb +2 -2
  40. data/testing/rspec/spec/line_filterable_specs.rb +1 -1
  41. data/testing/rspec/spec/map_reduce_spec.rb +1 -1
  42. data/testing/rspec/spec/model_query_spec.rb +1 -1
  43. data/testing/rspec/spec/multiple_queries_spec.rb +1 -1
  44. data/testing/rspec/spec/name_filterable_specs.rb +1 -1
  45. data/testing/rspec/spec/predefined_filters_spec.rb +284 -0
  46. data/testing/rspec/spec/repository_spec.rb +3 -3
  47. data/testing/rspec/spec/select_feature_dsl_spec.rb +8 -8
  48. data/testing/rspec/spec/select_scen_outline_dsl_spec.rb +14 -14
  49. data/testing/rspec/spec/select_scenario_dsl_spec.rb +9 -9
  50. data/testing/rspec/spec/spec_helper.rb +7 -17
  51. metadata +52 -23
  52. data/testing/cucumber/support/transforms.rb +0 -3
@@ -6,10 +6,13 @@ require 'cql/dsl'
6
6
 
7
7
  module CQL
8
8
 
9
+ # Not a part of the public API. Subject to change at any time.
9
10
  class MapReduce
10
11
 
11
12
  extend Dsl
12
13
 
14
+
15
+ # Recursively gathers all models that match the given targets and filters
13
16
  def self.gather_objects(current_object, target_classes, filters)
14
17
  gathered_objects = Array.new.tap { |gathered_objects| collect_all_in(target_classes, current_object, gathered_objects) }
15
18
 
@@ -1,13 +1,19 @@
1
- # todo - add some sore of error/warning if loading with the 0.x cuke_modeler?
1
+ # todo - add some sort of error/warning if loading with the 0.x cuke_modeler?
2
2
 
3
3
  module CukeModeler
4
+
5
+ # A monkey patch that allows models to be queried directly by having them use this gem's query module.
6
+
4
7
  class Model
5
8
 
6
9
  include CQL::Queriable
7
10
 
11
+
12
+ # Hanging on to the original method so that it can be invoked and thus ensure that all of the normal, un-patched behavior occurs
8
13
  alias_method :original_initialize, :initialize
9
14
 
10
15
 
16
+ # Sets itself as the model's *query_root*. Otherwise, as per the un-patched method.
11
17
  def initialize(source_text = nil)
12
18
  original_initialize(source_text)
13
19
 
@@ -1,9 +1,14 @@
1
1
  module CQL
2
+
3
+ # A mix-in module containing methods used by objects that want to be able to run queries against objects (often themselves).
4
+
2
5
  module Queriable
3
6
 
4
- attr_accessor :query_root
7
+ # The object against which the query will be run.
8
+ attr_accessor :query_root # todo - deprecate this such that queries are always performed against *self*
5
9
 
6
10
 
11
+ # Performs a query against the current *query_root*
7
12
  def query(&block)
8
13
  raise(ArgumentError, 'Query cannot be run. No query root has been set.') unless @query_root
9
14
 
@@ -0,0 +1,157 @@
1
+ module CQL
2
+
3
+ # Not a part of the public API. Subject to change at any time.
4
+ class Query
5
+
6
+ include Dsl
7
+
8
+ # the root object that will be queried
9
+ attr_reader :data,
10
+ # what kinds of objects will be selected
11
+ :what
12
+
13
+ # Creates a new query object
14
+ def initialize(directory, &block)
15
+ # Set root object
16
+ @data = directory
17
+
18
+ # Populate configurables from DSL block
19
+ self.instance_eval(&block)
20
+
21
+
22
+ raise(ArgumentError, "A query must specify a 'select' clause") unless @what
23
+ raise(ArgumentError, "A query must specify a 'from' clause") unless @from
24
+
25
+ warn("Multiple selections made without using an 'as' clause") unless @name_transforms || (@what.count == @what.uniq.count)
26
+
27
+ # Gather relevant objects from root object and filters
28
+ @data = CQL::MapReduce.gather_objects(@data, @from, @filters)
29
+
30
+ # Extract properties from gathered objects
31
+ @data = format_output(@data)
32
+ end
33
+
34
+
35
+ private
36
+
37
+
38
+ def format_output(data)
39
+ format_data(data)
40
+ end
41
+
42
+ def format_data data
43
+ space_data
44
+
45
+ Array.new.tap do |result_array|
46
+ data.each do |element|
47
+ result_array << Hash.new.tap do |result|
48
+ @what.each_with_index do |attribute, index|
49
+ key = determine_key(attribute, index)
50
+ value = determine_value(element, attribute, index)
51
+
52
+ result[key] = value
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def determine_key(attribute, index)
60
+ key = mapped_attribute(@name_transforms, attribute, index) if @name_transforms
61
+
62
+ key || attribute
63
+ end
64
+
65
+ def determine_value(element, attribute, index)
66
+ original_value = attribute.is_a?(Symbol) ? determine_special_value(element, attribute) : determine_normal_value(element, attribute)
67
+
68
+ if @value_transforms
69
+ value = mapped_attribute(@value_transforms, attribute, index)
70
+ value = value.call(original_value) if value.is_a?(Proc)
71
+ end
72
+
73
+ value || original_value
74
+ end
75
+
76
+ def determine_special_value(element, attribute)
77
+ # todo - Not sure what other special values to have but this could be expanded upon later.
78
+ case attribute
79
+ when :self, :model
80
+ val = element
81
+ else
82
+ raise(ArgumentError, ":#{attribute} is not a valid attribute for selection.")
83
+ end
84
+
85
+ val
86
+ end
87
+
88
+ def determine_normal_value(element, attribute)
89
+ if element.respond_to?(attribute)
90
+ element.send(attribute)
91
+ else
92
+ raise(ArgumentError, "'#{attribute}' is not a valid attribute for selection from a '#{element.class}'.")
93
+ end
94
+ end
95
+
96
+ def mapped_attribute(mappings, attribute, location)
97
+ case
98
+ when mappings.is_a?(Array)
99
+ value = mappings[location]
100
+ when mappings.is_a?(Hash)
101
+ if mappings[attribute]
102
+ value = mappings[attribute][location]
103
+ end
104
+ else
105
+ raise(ArgumentError, "Unknown mapping type '#{mappings.class}'")
106
+ end
107
+
108
+ value
109
+ end
110
+
111
+ def space_data
112
+ space_renamings
113
+ space_transforms
114
+ end
115
+
116
+ def space_renamings
117
+ if @name_transforms.is_a?(Hash)
118
+ new_names = {}
119
+
120
+ @name_transforms.each_pair do |key, value|
121
+ new_names[key] = []
122
+
123
+ @what.each do |attribute|
124
+ if attribute == key
125
+ new_names[key] << value.shift
126
+ else
127
+ new_names[key] << nil
128
+ end
129
+ end
130
+ end
131
+
132
+ @name_transforms = new_names
133
+ end
134
+ end
135
+
136
+ def space_transforms
137
+ if @value_transforms.is_a?(Hash)
138
+ new_values = {}
139
+
140
+ @value_transforms.each_pair do |key, value|
141
+ new_values[key] = []
142
+
143
+ @what.each do |attribute|
144
+ if attribute == key
145
+ new_values[key] << value.shift
146
+ else
147
+ new_values[key] << nil
148
+ end
149
+ end
150
+ end
151
+
152
+ @value_transforms = new_values
153
+ end
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,25 @@
1
+ module CQL
2
+
3
+ # A repository is a group of models. See the corresponding Cucumber documentation for details.
4
+
5
+ class Repository
6
+
7
+ include Queriable
8
+
9
+
10
+ # Creates a new repository object based on the passed directory path or model
11
+ def initialize(repository_root)
12
+ case
13
+ when repository_root.is_a?(String)
14
+ root = CukeModeler::Directory.new(repository_root)
15
+ when repository_root.class.to_s =~ /CukeModeler/
16
+ root = repository_root
17
+ else
18
+ raise(ArgumentError, "Don't know how to make a repository from a #{repository_root.class}")
19
+ end
20
+
21
+ @query_root = root
22
+ end
23
+
24
+ end
25
+ end
@@ -3,16 +3,20 @@ require 'cql/filters'
3
3
 
4
4
  module CQL
5
5
 
6
+ # Not a part of the public API. Subject to change at any time.
6
7
  class SsoLineCountFilter < TypeCountFilter
7
8
 
9
+ # Counts the numbers of steps on a test
8
10
  def type_count(test)
9
11
  test.steps.size
10
12
  end
11
13
 
12
14
  end
13
15
 
16
+ # Not a part of the public API. Subject to change at any time.
14
17
  class LineFilter < ContentMatchFilter
15
18
 
19
+ # Filters the input models so that only the desired ones are returned
16
20
  def execute(input, negate)
17
21
  method_for_filtering = negate ? :reject : :select
18
22
  method_for_text = Gem.loaded_specs['cuke_modeler'].version.version[/^0/] ? :base : :text
@@ -1,3 +1,4 @@
1
1
  module CQL
2
- VERSION = '1.4.0'
2
+ # The current version of the gem
3
+ VERSION = '1.6.0'
3
4
  end
@@ -2,12 +2,14 @@ Feature: 'as' clause'
2
2
 
3
3
  The *as* clause allows you to change the keys under which the model attributes specified by the *select* clause will be gathered. Key renaming can be done as a list of new names that are applied in order or as a mapping of specific keys to their new names.
4
4
 
5
- Sample usage:
6
- cql_repo.query do
7
- select name
8
- as title
9
- from features
10
- end
5
+ Sample usage:
6
+ ````
7
+ cql_repo.query do
8
+ select name
9
+ as title
10
+ from features
11
+ end
12
+ ````
11
13
 
12
14
  This will return a list of all of the feature names but under the key of 'title' instead of 'name'.
13
15
 
@@ -15,8 +17,8 @@ Feature: 'as' clause'
15
17
 
16
18
 
17
19
  Background: A sample Cucumber suite
18
- Given a directory "test_directory"
19
- And a file "test_directory/test_file_1.feature":
20
+ Given a repository to query
21
+ And the following feature has been modeled in the repository:
20
22
  """
21
23
  Feature: A test feature
22
24
 
@@ -36,7 +38,6 @@ Feature: 'as' clause'
36
38
  | param |
37
39
  | value |
38
40
  """
39
- And a repository is made from "test_directory"
40
41
 
41
42
 
42
43
  Scenario: Using 'as' to change the name under which values are returned
@@ -4,22 +4,26 @@ Feature: 'from' clause
4
4
 
5
5
  The following are some example values:
6
6
 
7
- CukeModeler::Outline (exact class)
8
- outline (singular)
9
- outlines (pluralized)
10
-
11
- Sample usage:
12
- cql_repo.query do
13
- select name
14
- from scenarios
15
- end
7
+ ````
8
+ CukeModeler::Outline # exact class
9
+ outline # singular
10
+ outlines # pluralized
11
+ ````
12
+
13
+ Sample usage:
14
+ ````
15
+ cql_repo.query do
16
+ select name
17
+ from scenarios
18
+ end
19
+ ````
16
20
 
17
21
  This clause can be repeated multiple times. The arguments for successive clauses are simply added to the previous arguments.
18
22
 
19
23
 
20
24
  Background: A sample Cucumber suite
21
- Given a directory "test_directory"
22
- And a file "test_directory/test_file_1.feature":
25
+ Given a repository to query
26
+ And the following feature has been modeled in the repository:
23
27
  """
24
28
  Feature: A test feature
25
29
 
@@ -39,7 +43,6 @@ Feature: 'from' clause
39
43
  | param |
40
44
  | value |
41
45
  """
42
- And a repository is made from "test_directory"
43
46
 
44
47
 
45
48
  Scenario: Using 'from' to specify what kind of objects from which to return attributes
@@ -116,11 +119,3 @@ Feature: 'from' clause
116
119
  from :all
117
120
  """
118
121
  Then all models are queried from
119
-
120
-
121
- # Commented out so that they aren't picked up by Relish
122
- # @wip
123
- # Scenario: Can 'from' from all type of model
124
- #
125
- # @wip
126
- # Scenario: From-ing from a collection
@@ -0,0 +1,392 @@
1
+ # todo - Rewrite the scenarios such that they use their own test specific feature files instead of setting up a large suite in the background
2
+ Feature: 'with' clause
3
+
4
+ There are several predefined filters that can be used with the *with* clause. Like regular 'block style' conditions, they can be negated using *without*, used in a targeted fashion, etc.
5
+
6
+ Sample usage:
7
+ ````
8
+ cql_repo.query do
9
+ select name, tags, description_text
10
+ from features
11
+ with tc lt 3
12
+ end
13
+ ````
14
+
15
+ The following filters are supported for models that have tags:
16
+
17
+ * tags - Filters out models that do not have the exact set of tags provided.
18
+ * tc - (tag count) Filters out models based on the number of tags that they have.
19
+
20
+ The following filters are supported for models that have names:
21
+
22
+ * name - Filters out models whose name does not match the name provided. Can be a string or regular expression.
23
+
24
+ The following filters are supported for models that have steps:
25
+
26
+ * line - Filters out models whose steps do not include the provided step (keywords and blocks are ignored). Can be a string or regular expression.
27
+ * lc - (line count) Filters out models based on the number of steps that they have.
28
+
29
+ The following filters are supported for feature models:
30
+
31
+ * sc - (scenario count) Filters out models based on the number of scenarios that they have.
32
+ * soc - (scenario outline count) Filters out models based on the number of outlines that they have.
33
+ * ssoc - (scenario and scenario outline count) Filters out models based on the total number of scenarios and outlines that they have.
34
+
35
+ For count based filters, the following operators are available:
36
+
37
+ * lt (Less than)
38
+ * lte (Less than or equals)
39
+ * gt (Greater than)
40
+ * gte (Greater than or equals)
41
+ * eq (Equals)
42
+
43
+
44
+ Background: A sample Cucumber suite
45
+ Given a repository to query
46
+ And the following feature has been modeled in the repository:
47
+ """
48
+ Feature: A test feature
49
+
50
+ @tag_1 @tag_2
51
+ Scenario: Test 1
52
+ * some steps
53
+
54
+ @special_tag @tag_2
55
+ Scenario: Test 2
56
+ * some other steps
57
+ * some other steps
58
+ * some other steps
59
+
60
+ @a @b @c
61
+ Scenario Outline: Test 3
62
+ * some steps
63
+ * some more steps
64
+ * some more steps
65
+ * some more steps
66
+ Examples: First examples
67
+ | param |
68
+ | value |
69
+ Examples: Second examples
70
+ | param |
71
+ | value |
72
+
73
+ Scenario: Test 4
74
+ """
75
+ And the following feature has been modeled in the repository:
76
+ """
77
+ Feature: A feature with lots of scenarios
78
+
79
+ Scenario: 1
80
+ * different steps
81
+ * different steps
82
+
83
+ Scenario: 2
84
+ * different steps
85
+ * different steps
86
+
87
+ Scenario: 3
88
+ * different steps
89
+ * different steps
90
+ """
91
+ And the following feature has been modeled in the repository:
92
+ """
93
+ Feature: A feature with lots of outlines
94
+
95
+ Scenario Outline: 1
96
+ * different steps
97
+ * different steps
98
+ Examples:
99
+ | param |
100
+ | value |
101
+
102
+ Scenario Outline: 2
103
+ * different steps
104
+ * different steps
105
+ Examples:
106
+ | param |
107
+ | value |
108
+
109
+ Scenario Outline: 3
110
+ * different steps
111
+ * different steps
112
+ Examples:
113
+ | param |
114
+ | value |
115
+ """
116
+ And the following feature has been modeled in the repository:
117
+ """
118
+ Feature: A feature with a mix of tests
119
+
120
+ Scenario: 4
121
+ * different steps
122
+ * different steps
123
+
124
+ Scenario Outline: 4
125
+ * different steps
126
+ * different steps
127
+ Examples:
128
+ | param |
129
+ | value |
130
+ """
131
+
132
+
133
+ Scenario: Filtering by tags
134
+ When the following query is executed:
135
+ """
136
+ select name
137
+ from scenarios
138
+ with tags '@tag_1', '@tag_2'
139
+ """
140
+ Then the following values are returned:
141
+ | name |
142
+ | Test 1 |
143
+
144
+ Scenario: Filtering by tag count
145
+ When the following query is executed:
146
+ """
147
+ select name
148
+ from scenarios, outlines
149
+ with tc gt 2
150
+ """
151
+ Then the following values are returned:
152
+ | name |
153
+ | Test 3 |
154
+
155
+ Scenario: Filtering by name (exact match)
156
+ When the following query is executed:
157
+ """
158
+ select name
159
+ from scenarios, outlines
160
+ with name 'Test 3'
161
+ """
162
+ Then the following values are returned:
163
+ | name |
164
+ | Test 3 |
165
+
166
+ Scenario: Filtering by name (regular expression)
167
+ When the following query is executed:
168
+ """
169
+ select name
170
+ from scenarios, outlines
171
+ with name /Test [12]/
172
+ """
173
+ Then the following values are returned:
174
+ | name |
175
+ | Test 1 |
176
+ | Test 2 |
177
+
178
+ Scenario: Filtering by line (exact match)
179
+ When the following query is executed:
180
+ """
181
+ select name
182
+ from scenarios, outlines
183
+ with line 'some steps'
184
+ """
185
+ Then the following values are returned:
186
+ | name |
187
+ | Test 1 |
188
+ | Test 3 |
189
+
190
+ Scenario: Filtering by line (regular expression)
191
+ When the following query is executed:
192
+ """
193
+ select name
194
+ from scenarios, outlines
195
+ with line /other/
196
+ """
197
+ Then the following values are returned:
198
+ | name |
199
+ | Test 2 |
200
+
201
+ Scenario: Filtering by line count
202
+ When the following query is executed:
203
+ """
204
+ select name
205
+ from scenarios, outlines
206
+ with lc gt 3
207
+ """
208
+ Then the following values are returned:
209
+ | name |
210
+ | Test 3 |
211
+
212
+ Scenario: Filtering by scenario count
213
+ When the following query is executed:
214
+ """
215
+ select name
216
+ from features
217
+ with sc gt 2
218
+ """
219
+ Then the following values are returned:
220
+ | name |
221
+ | A test feature |
222
+ | A feature with lots of scenarios |
223
+
224
+ Scenario: Filtering by outline count
225
+ When the following query is executed:
226
+ """
227
+ select name
228
+ from features
229
+ with soc gt 2
230
+ """
231
+ Then the following values are returned:
232
+ | name |
233
+ | A feature with lots of outlines |
234
+
235
+ Scenario: Filtering by combined test count
236
+ When the following query is executed:
237
+ """
238
+ select name
239
+ from features
240
+ with ssoc lt 3
241
+ """
242
+ Then the following values are returned:
243
+ | name |
244
+ | A feature with a mix of tests |
245
+
246
+ Scenario: Using the 'lt' count filter
247
+ When the following query is executed:
248
+ """
249
+ select name
250
+ from features
251
+ with ssoc lt 3
252
+ """
253
+ Then the following values are returned:
254
+ | name |
255
+ | A feature with a mix of tests |
256
+
257
+ Scenario: Using the 'lte' count filter
258
+ When the following query is executed:
259
+ """
260
+ select name
261
+ from scenarios, outlines
262
+ with lc lte 1
263
+ """
264
+ Then the following values are returned:
265
+ | name |
266
+ | Test 1 |
267
+ | Test 4 |
268
+
269
+ Scenario: Using the 'gt' count filter
270
+ When the following query is executed:
271
+ """
272
+ select name
273
+ from scenarios, outlines
274
+ with lc gt 3
275
+ """
276
+ Then the following values are returned:
277
+ | name |
278
+ | Test 3 |
279
+
280
+ Scenario: Using the 'gte' count filter
281
+ When the following query is executed:
282
+ """
283
+ select name
284
+ from scenarios, outlines
285
+ with lc gte 3
286
+ """
287
+ Then the following values are returned:
288
+ | name |
289
+ | Test 2 |
290
+ | Test 3 |
291
+
292
+ Scenario: Using the 'eq' count filter
293
+ When the following query is executed:
294
+ """
295
+ select name
296
+ from scenarios, outlines
297
+ with tc eq 3
298
+ """
299
+ Then the following values are returned:
300
+ | name |
301
+ | Test 3 |
302
+
303
+
304
+ Scenario: Using multiple filters
305
+ When the following query is executed:
306
+ """
307
+ select name
308
+ from scenarios
309
+ with tc eq 2
310
+ with lc gt 1
311
+ """
312
+ Then the following values are returned:
313
+ | name |
314
+ | Test 2 |
315
+
316
+ Scenario: Using the 'with' clause multiple times
317
+
318
+ Behavior is the same as combining regular, block style filters but the syntax has to become more explicit.
319
+
320
+ When the following query is executed:
321
+ """
322
+ select name
323
+ from scenarios
324
+ with tc(eq(2)),
325
+ lc(gt(1))
326
+ """
327
+ Then the result is the same as the result of the following query:
328
+ """
329
+ select name
330
+ from scenarios
331
+ with tc eq 2
332
+ with lc gt 1
333
+ """
334
+
335
+ Scenario: Selectively filtering models
336
+ When the following query is executed:
337
+ """
338
+ select name
339
+ from scenarios, features
340
+ with scenarios => lc(eq(1))
341
+ """
342
+ Then the following values are returned:
343
+ | name |
344
+ | Test 1 |
345
+ | A test feature |
346
+ | A feature with lots of scenarios |
347
+ | A feature with lots of outlines |
348
+ | A feature with a mix of tests |
349
+
350
+
351
+ Scenario: Mixing targeted and blanket filters
352
+ When the following query is executed:
353
+ """
354
+ select name
355
+ from scenarios, features
356
+ with name /test/i
357
+ with scenarios => tc(eq(0))
358
+ """
359
+ Then the following values are returned:
360
+ | name |
361
+ | Test 4 |
362
+ | A test feature |
363
+ | A feature with a mix of tests |
364
+
365
+ Scenario: Using 'without' for negation
366
+ When the following query is executed:
367
+ """
368
+ select name
369
+ from features
370
+ without ssoc lt 3
371
+ """
372
+ Then the result is the same as the result of the following query:
373
+ """
374
+ select name
375
+ from features
376
+ with ssoc gt 2
377
+ """
378
+
379
+ Scenario: Mixing predefined filters and regular filters
380
+ When the following query is executed:
381
+ """
382
+ select name
383
+ from scenarios, features
384
+ with { |element| element.name =~ /test/i }
385
+ with scenarios => tc(eq(0))
386
+ """
387
+ Then the following values are returned:
388
+ | name |
389
+ | Test 4 |
390
+ | A test feature |
391
+ | A feature with a mix of tests |
392
+