cql 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cql/dsl.rb +5 -0
  3. data/lib/cql/version.rb +1 -1
  4. data/testing/cucumber/features/clauses/from_clause.feature +0 -8
  5. data/testing/cucumber/features/clauses/predefined_with_filters.feature +392 -0
  6. data/testing/cucumber/features/clauses/select_clause.feature +1 -5
  7. data/testing/cucumber/features/clauses/with_clause.feature +2 -164
  8. data/testing/cucumber/features/dsl.feature +0 -22
  9. data/testing/cucumber/step_definitions/verification_steps.rb +5 -6
  10. data/testing/model_helper.rb +28 -0
  11. data/testing/rspec/spec/clauses/as_clause_spec.rb +1 -0
  12. data/testing/rspec/spec/clauses/from_clause_spec.rb +146 -0
  13. data/testing/rspec/spec/clauses/select_clause_spec.rb +184 -0
  14. data/testing/rspec/spec/clauses/transform_clause_spec.rb +35 -0
  15. data/testing/rspec/spec/clauses/with_clause_spec.rb +84 -0
  16. data/testing/rspec/spec/clauses/without_clause_spec.rb +171 -0
  17. data/testing/rspec/spec/dsl_spec.rb +3 -575
  18. data/testing/rspec/spec/filter_example_spec.rb +1 -1
  19. data/testing/rspec/spec/filter_feature_dsl_spec.rb +13 -13
  20. data/testing/rspec/spec/filter_sso_spec.rb +2 -2
  21. data/testing/rspec/spec/line_filterable_specs.rb +1 -1
  22. data/testing/rspec/spec/map_reduce_spec.rb +1 -1
  23. data/testing/rspec/spec/multiple_queries_spec.rb +1 -1
  24. data/testing/rspec/spec/name_filterable_specs.rb +1 -1
  25. data/testing/rspec/spec/predefined_filters_spec.rb +284 -0
  26. data/testing/rspec/spec/repository_spec.rb +3 -3
  27. data/testing/rspec/spec/select_feature_dsl_spec.rb +8 -8
  28. data/testing/rspec/spec/select_scen_outline_dsl_spec.rb +14 -14
  29. data/testing/rspec/spec/select_scenario_dsl_spec.rb +9 -9
  30. data/testing/rspec/spec/spec_helper.rb +3 -13
  31. metadata +21 -3
@@ -1,7 +1,7 @@
1
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
2
  Feature: 'with' clause
3
3
 
4
- The *with* clause specifies filter conditions that will reduce the number of things targeted by the *from* clause. The *with* clause can take one or more blocks that will filter out any object for which the block does not evaluate to true (using 'without' instead of 'with' will have the opposite effect). Alternatively, mappings of specific *from* targets to their respective filtering blocks can be provided. The *with* clause can also take predefined filters (detailed below).
4
+ The *with* clause specifies filter conditions that will reduce the number of things targeted by the *from* clause. The *with* clause can take one or more blocks that will filter out any object for which the block does not evaluate to true (using 'without' instead of 'with' will have the opposite effect). Alternatively, mappings of specific *from* targets to their respective filtering blocks can be provided. The *with* clause can also take predefined filters (see corresponding documentation).
5
5
 
6
6
  Sample usage:
7
7
  ````
@@ -9,41 +9,12 @@ Feature: 'with' clause
9
9
  select name, tags, description_text
10
10
  from features
11
11
  with { |feature| feature.name =~ /foo/ }
12
- with tc lt 3
13
12
  end
14
13
  ````
15
14
 
16
15
  This clause can be repeated multiple times. The arguments for successive clauses are simply added to the previous arguments.
17
16
 
18
17
 
19
- The following filters are supported for models that have tags:
20
-
21
- * tags - Filters out models that do not have the exact set of tags provided.
22
- * tc - (tag count) Filters out models based on the number of tags that they have.
23
-
24
- The following filters are supported for models that have names:
25
-
26
- * name - Filters out models whose name does not match the name provided. Can be a string or regular expression.
27
-
28
- The following filters are supported for models that have steps:
29
-
30
- * line - Filters out models whose steps do not include the provided step (keywords and blocks are ignored). Can be a string or regular expression.
31
- * lc - (line count) Filters out models based on the number of steps that they have.
32
-
33
- The following filters are supported for feature models:
34
-
35
- * sc - (scenario count) Filters out models based on the number of scenarios that they have.
36
- * soc - (scenario outline count) Filters out models based on the number of outlines that they have.
37
- * ssoc - (scenario and scenario outline count) Filters out models based on the total number of scenarios and outlines that they have.
38
-
39
- For count based filters, the following operators are available:
40
-
41
- * lt (Less than)
42
- * lte (Less than or equals)
43
- * gt (Greater than)
44
- * gte (Greater than or equals)
45
-
46
-
47
18
  Background: A sample Cucumber suite
48
19
  Given a repository to query
49
20
  And the following feature has been modeled in the repository:
@@ -161,7 +132,7 @@ Feature: 'with' clause
161
132
  """
162
133
  select name
163
134
  from features, scenarios
164
- with scenarios => tags('@tag_1','@tag_2')
135
+ with scenarios => lambda { |element| element.name == 'Test 1' }
165
136
  """
166
137
  Then the following values are returned:
167
138
  | name |
@@ -216,127 +187,6 @@ Feature: 'with' clause
216
187
  | A feature with lots of scenarios |
217
188
  | A feature with lots of outlines |
218
189
 
219
- # todo - break out the predefined filters into another feature file?
220
- Scenario: Filtering by tags
221
- When the following query is executed:
222
- """
223
- select name
224
- from scenarios
225
- with tags '@tag_1', '@tag_2'
226
- """
227
- Then the following values are returned:
228
- | name |
229
- | Test 1 |
230
-
231
- Scenario: Filtering by tag count
232
- When the following query is executed:
233
- """
234
- select name
235
- from scenarios, outlines
236
- with tc gt 2
237
- """
238
- Then the following values are returned:
239
- | name |
240
- | Test 3 |
241
-
242
- Scenario: Filtering by name
243
- When the following query is executed:
244
- """
245
- select name
246
- from scenarios, outlines
247
- with name 'Test 3'
248
- """
249
- Then the following values are returned:
250
- | name |
251
- | Test 3 |
252
- When the following query is executed:
253
- """
254
- select name
255
- from scenarios, outlines
256
- with name /Test [12]/
257
- """
258
- Then the following values are returned:
259
- | name |
260
- | Test 1 |
261
- | Test 2 |
262
-
263
- Scenario: Filtering by line
264
- When the following query is executed:
265
- """
266
- select name
267
- from scenarios, outlines
268
- with line 'some steps'
269
- """
270
- Then the following values are returned:
271
- | name |
272
- | Test 1 |
273
- | Test 3 |
274
- When the following query is executed:
275
- """
276
- select name
277
- from scenarios, outlines
278
- with line /other/
279
- """
280
- Then the following values are returned:
281
- | name |
282
- | Test 2 |
283
-
284
- Scenario: Filtering by line count
285
- When the following query is executed:
286
- """
287
- select name
288
- from scenarios, outlines
289
- with lc gt 1
290
- """
291
- Then the following values are returned:
292
- | name |
293
- | Test 3 |
294
-
295
- Scenario: Filtering by scenario count
296
- When the following query is executed:
297
- """
298
- select name
299
- from features
300
- with sc gt 2
301
- """
302
- Then the following values are returned:
303
- | name |
304
- | A feature with lots of scenarios |
305
-
306
- Scenario: Filtering by outline count
307
- When the following query is executed:
308
- """
309
- select name
310
- from features
311
- with soc gt 2
312
- """
313
- Then the following values are returned:
314
- | name |
315
- | A feature with lots of outlines |
316
-
317
- Scenario: Filtering by combined test count
318
- When the following query is executed:
319
- """
320
- select name
321
- from features
322
- with ssoc lt 3
323
- """
324
- Then the following values are returned:
325
- | name |
326
- | A feature with a mix of tests |
327
-
328
- @wip
329
- Scenario: Using the 'lt' count filter
330
-
331
- @wip
332
- Scenario: Using the 'lte' count filter
333
-
334
- @wip
335
- Scenario: Using the 'gt' count filter
336
-
337
- @wip
338
- Scenario: Using the 'gte' count filter
339
-
340
190
  Scenario: Using 'without' for negation
341
191
  When the following query is executed:
342
192
  """
@@ -350,15 +200,3 @@ Feature: 'with' clause
350
200
  from scenarios
351
201
  with { |scenario| !(scenario.source_line == 8) }
352
202
  """
353
- When the following query is executed:
354
- """
355
- select name
356
- from features
357
- without ssoc lt 3
358
- """
359
- Then the result is the same as the result of the following query:
360
- """
361
- select name
362
- from features
363
- with ssoc gt 2
364
- """
@@ -56,25 +56,3 @@ Feature: DSL
56
56
  select 'name'
57
57
  from 'scenarios'
58
58
  """
59
-
60
-
61
- # Commented out so that they aren't picked up by Relish
62
- #
63
- # @wip
64
- # Scenario: Use 'order_by' to sort the results
65
- #
66
- # # 'and' is a keyword. Some other kind of repeater word would be needed
67
- # @wip
68
- # Scenario: 'And' can be used instead of repeating the previous keyword
69
- # When the following query is executed:
70
- # """
71
- # select 'name'
72
- # and 'source_line'
73
- # from CukeModeler::Scenario
74
- # and CukeModeler::Outline
75
- # """
76
- # Then the following values are returned:
77
- # | name | source_line |
78
- # | Test 1 | 3 |
79
- # | Test 2 | 6 |
80
- # | Test 3 | 9 |
@@ -2,16 +2,15 @@ Then(/^the following values are returned:$/) do |values|
2
2
  expected_keys = values.raw.first
3
3
  expected_results = values.hashes
4
4
 
5
+ # Protecting against false positives
6
+ # Key order doesn't matter and Ruby 1.8.7 does not retain hash key ordering, so sorting them for consistency
7
+ raise('Invalid result set. Attribute names cannot be repeated.') unless expected_keys.sort == expected_results.first.keys.sort
8
+
5
9
  expected_results.each do |result|
6
10
  result.each_pair { |key, value| result[key] = value.to_i if value =~ /^\d+$/ }
7
11
  end
8
12
 
9
-
10
- @query_results.each_with_index do |result, index|
11
- # Key order doesn't matter and Ruby 1.8.7 does not retain hash key ordering, so sorting them for consistency
12
- expect(result.keys.sort).to eq(expected_keys.sort)
13
- expect(result).to eq(expected_results[index])
14
- end
13
+ expect(@query_results).to match_array(expected_results)
15
14
  end
16
15
 
17
16
  # Then(/^all of them can be queried for additional information$/) do
@@ -0,0 +1,28 @@
1
+ module CQL
2
+ module ModelHelper
3
+
4
+ def directory_with(*models)
5
+ directory_model = CukeModeler::Directory.new
6
+
7
+ models.each do |model|
8
+ case
9
+ when model.is_a?(CukeModeler::Feature)
10
+ file_model = CukeModeler::FeatureFile.new
11
+
12
+ if Gem.loaded_specs['cuke_modeler'].version.version[/^0/]
13
+ file_model.features = [model]
14
+ else
15
+ file_model.feature = model
16
+ end
17
+
18
+ directory_model.feature_files << file_model
19
+ else
20
+ raise(ArgumentError, "Don't know how to handle a '#{model.class}'")
21
+ end
22
+ end
23
+
24
+ directory_model
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1 @@
1
+ #TODO - add some specs for the 'as' clause
@@ -0,0 +1,146 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+
4
+ describe 'an object that uses the DSL' do
5
+
6
+
7
+ let(:nodule) { CQL::Dsl }
8
+ let(:dsl_enabled_object) { Object.new.extend(nodule) }
9
+
10
+
11
+ describe "from" do
12
+
13
+ it 'knows from what to select attributes' do
14
+ expect(dsl_enabled_object).to respond_to(:from)
15
+ end
16
+
17
+ it 'selects from one or more things' do
18
+ expect(dsl_enabled_object.method(:from).arity).to eq(-1)
19
+ end
20
+
21
+ it "can handle an empty 'from' clause" do
22
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
23
+
24
+ result = gs.query do
25
+ select name
26
+ from
27
+ end
28
+
29
+ expect(result).to eq([])
30
+ end
31
+
32
+ describe "multiple targets" do
33
+
34
+ it 'raises an exception for inapplicable attributes' do
35
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
36
+
37
+ expect {
38
+ gs.query do
39
+ select name, steps
40
+ from features
41
+ from scenarios
42
+ end
43
+ }.to raise_error(ArgumentError)
44
+ end
45
+
46
+ end
47
+
48
+ describe 'shorthand' do
49
+
50
+ it 'should consider an exact match over a pluralization' do
51
+ plural_class_model = CukeModeler::CqlTestModels.new
52
+ singular_class_model = CukeModeler::CqlTestModel.new
53
+
54
+ plural_class_model.attribute_1 = 'plural'
55
+ singular_class_model.attribute_1 = 'singular'
56
+ plural_class_model.children << singular_class_model
57
+
58
+ repo = CQL::Repository.new(plural_class_model)
59
+
60
+ result = repo.query do
61
+ select attribute_1
62
+ from cql_test_model
63
+ end
64
+
65
+ expect(result.first['attribute_1']).to eq('singular')
66
+ end
67
+
68
+ it 'raises an exception if the shorthand form of a class cannot be mapped to a real class' do
69
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
70
+
71
+ expect {
72
+ gs.query do
73
+ select name
74
+ from not_a_real_class
75
+ end
76
+ }.to raise_error(ArgumentError, "Class 'CukeModeler::NotARealClass' does not exist")
77
+
78
+ end
79
+
80
+ it 'can freely mix shorthand and long-form names' do
81
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
82
+
83
+ # All long-form
84
+ base_result = gs.query do
85
+ select name
86
+ from CukeModeler::Scenario, CukeModeler::Feature
87
+ end
88
+
89
+ # All shorthand
90
+ expect(
91
+ gs.query do
92
+ select name
93
+ from scenarios, features
94
+ end
95
+ ).to eq(base_result)
96
+
97
+ # A mix of both
98
+ expect(
99
+ gs.query do
100
+ select name
101
+ from CukeModeler::Scenario, features
102
+ end
103
+ ).to eq(base_result)
104
+ end
105
+
106
+ end
107
+
108
+
109
+ describe 'special scopes' do
110
+
111
+ it 'understands the :all scope' do
112
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
113
+
114
+ expect { gs.query do
115
+ select :model
116
+ from :all
117
+ end
118
+ }.to_not raise_error
119
+ end
120
+
121
+ it 'queries from all models when scoped to :all' do
122
+ model_1 = CukeModeler::CqlTestModel.new
123
+ model_2 = CukeModeler::CqlTestModel.new
124
+ model_3 = CukeModeler::CqlTestModel.new
125
+
126
+ model_1.children << model_2
127
+ model_1.children << model_3
128
+
129
+ repo = CQL::Repository.new(model_1)
130
+
131
+ result = repo.query do
132
+ select :model
133
+ from :all
134
+ end
135
+
136
+ expect(result).to match_array([{:model => model_1},
137
+ {:model => model_2},
138
+ {:model => model_3}])
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+
145
+
146
+ end
@@ -0,0 +1,184 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+
4
+ describe 'an object that uses the DSL' do
5
+
6
+
7
+ let(:nodule) { CQL::Dsl }
8
+ let(:dsl_enabled_object) { Object.new.extend(nodule) }
9
+
10
+ describe "select" do
11
+
12
+ it 'knows how to select attributes' do
13
+ expect(dsl_enabled_object).to respond_to(:select)
14
+ end
15
+
16
+ it 'selects one or more attributes' do
17
+ expect(dsl_enabled_object.method(:select).arity).to eq(-1)
18
+ end
19
+
20
+ it 'correctly selects a single attribute from a model' do
21
+ model = CukeModeler::CqlTestModel.new
22
+ model.attribute_1 = 'foo'
23
+
24
+ repo = CQL::Repository.new(model)
25
+
26
+ result = repo.query do
27
+ select attribute_1
28
+ from cql_test_model
29
+ end
30
+
31
+
32
+ expect(result).to eq([{'attribute_1' => 'foo'}])
33
+ end
34
+
35
+ it 'correctly selects multiple attributes from a model' do
36
+ model = CukeModeler::CqlTestModel.new
37
+ model.attribute_1 = 'foo'
38
+ model.attribute_2 = 'bar'
39
+
40
+ repo = CQL::Repository.new(model)
41
+
42
+ result = repo.query do
43
+ select attribute_1, attribute_2
44
+ from cql_test_model
45
+ end
46
+
47
+
48
+ expect(result).to eq([{'attribute_1' => 'foo',
49
+ 'attribute_2' => 'bar'}])
50
+ end
51
+
52
+
53
+ describe 'special attributes' do
54
+
55
+ it 'understands the :model attribute' do
56
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
57
+
58
+ expect { gs.query do
59
+ select :model
60
+ from features
61
+ end
62
+ }.to_not raise_error
63
+ end
64
+
65
+ it 'interprets :model in the same manner that it interprets :self' do
66
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
67
+
68
+ self_result = gs.query do
69
+ select :self
70
+ from features
71
+ end
72
+
73
+ model_result = gs.query do
74
+ select :model
75
+ from features
76
+ end
77
+
78
+ # Only checking the values of the results because they will have different :model/:self keys
79
+ expect(model_result.collect { |result| result.values }).to eq(self_result.collect { |result| result.values })
80
+ end
81
+
82
+ it 'complains if an unknown special attribute is queried' do
83
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
84
+
85
+ expect {
86
+ gs.query do
87
+ select :foo
88
+ from scenarios
89
+ end
90
+ }.to raise_error(ArgumentError, ":foo is not a valid attribute for selection.")
91
+ end
92
+
93
+ it 'uses the :self attribute by default' do
94
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
95
+
96
+ default_result = gs.query do
97
+ select
98
+ from features
99
+ end
100
+
101
+ self_result = gs.query do
102
+ select :self
103
+ from features
104
+ end
105
+
106
+ expect(self_result).to eq(default_result)
107
+ end
108
+
109
+ end
110
+
111
+
112
+ it 'complains if an unknown normal attribute is queried' do
113
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
114
+
115
+ expect {
116
+ gs.query do
117
+ select steps
118
+ from features
119
+ end
120
+ }.to raise_error(ArgumentError, "'steps' is not a valid attribute for selection from a 'CukeModeler::Feature'.")
121
+ end
122
+
123
+
124
+ describe "multiple selections" do
125
+
126
+ it 'can freely mix empty selections and attribute selections' do
127
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
128
+
129
+ base_result = gs.query do
130
+ select :self
131
+ select name
132
+ select :self
133
+ as 'foo', 'bar', 'baz'
134
+ from scenarios
135
+ end
136
+
137
+
138
+ expect(
139
+ gs.query do
140
+ select
141
+ select name
142
+ select
143
+ as 'foo', 'bar', 'baz'
144
+ from scenarios
145
+ end
146
+ ).to eq(base_result)
147
+ end
148
+
149
+ end
150
+
151
+
152
+ describe 'duplicate selections' do
153
+
154
+ let(:warning_message) { "Multiple selections made without using an 'as' clause\n" }
155
+
156
+ it "warns if the same attribute is selected more than once without an 'as' clause being used" do
157
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
158
+
159
+ expect {
160
+ gs.query do
161
+ select :model, :model, :model
162
+ from :all
163
+ end
164
+ }.to output(warning_message).to_stderr
165
+ end
166
+
167
+ it "does not warn if the same attribute is selected more than once and an 'as' clause is used" do
168
+ gs = CQL::Repository.new("#{CQL_FEATURE_FIXTURES_DIRECTORY}/scenario/simple")
169
+
170
+ expect {
171
+ gs.query do
172
+ select :model, :model, :model
173
+ # Usage of the clause is sufficient. Not going to try and count the mappings or anything like that.
174
+ as foo
175
+ from :all
176
+ end
177
+ }.to_not output(warning_message).to_stderr
178
+ end
179
+
180
+ end
181
+
182
+ end
183
+
184
+ end