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.
- checksums.yaml +5 -5
- data/lib/cql.rb +4 -169
- data/lib/cql/dsl.rb +39 -5
- data/lib/cql/feature_filters.rb +2 -0
- data/lib/cql/filters.rb +25 -1
- data/lib/cql/map_reduce.rb +3 -0
- data/lib/cql/model_dsl.rb +7 -1
- data/lib/cql/queriable.rb +6 -1
- data/lib/cql/query.rb +157 -0
- data/lib/cql/repository.rb +25 -0
- data/lib/cql/sso_filters.rb +4 -0
- data/lib/cql/version.rb +2 -1
- data/testing/cucumber/features/clauses/as_clause.feature +10 -9
- data/testing/cucumber/features/clauses/from_clause.feature +15 -20
- data/testing/cucumber/features/clauses/predefined_with_filters.feature +392 -0
- data/testing/cucumber/features/clauses/select_clause.feature +11 -14
- data/testing/cucumber/features/clauses/transform_clause.feature +10 -9
- data/testing/cucumber/features/clauses/with_clause.feature +10 -170
- data/testing/cucumber/features/dsl.feature +15 -34
- data/testing/cucumber/step_definitions/setup_steps.rb +1 -13
- data/testing/cucumber/step_definitions/verification_steps.rb +6 -7
- data/testing/cucumber/support/env.rb +9 -9
- data/testing/gemfiles/cuke_modeler0.gemfile +10 -7
- data/testing/gemfiles/cuke_modeler1.gemfile +6 -7
- data/testing/gemfiles/cuke_modeler2.gemfile +33 -0
- data/testing/gemfiles/cuke_modeler3.gemfile +10 -0
- data/testing/helper_methods.rb +13 -0
- data/testing/model_helper.rb +28 -0
- data/testing/rspec/spec/clauses/as_clause_spec.rb +1 -0
- data/testing/rspec/spec/clauses/from_clause_spec.rb +146 -0
- data/testing/rspec/spec/clauses/select_clause_spec.rb +184 -0
- data/testing/rspec/spec/clauses/transform_clause_spec.rb +35 -0
- data/testing/rspec/spec/clauses/with_clause_spec.rb +84 -0
- data/testing/rspec/spec/clauses/without_clause_spec.rb +171 -0
- data/testing/rspec/spec/cql_spec.rb +25 -0
- data/testing/rspec/spec/dsl_spec.rb +3 -575
- data/testing/rspec/spec/filter_example_spec.rb +1 -1
- data/testing/rspec/spec/filter_feature_dsl_spec.rb +13 -13
- data/testing/rspec/spec/filter_sso_spec.rb +2 -2
- data/testing/rspec/spec/line_filterable_specs.rb +1 -1
- data/testing/rspec/spec/map_reduce_spec.rb +1 -1
- data/testing/rspec/spec/model_query_spec.rb +1 -1
- data/testing/rspec/spec/multiple_queries_spec.rb +1 -1
- data/testing/rspec/spec/name_filterable_specs.rb +1 -1
- data/testing/rspec/spec/predefined_filters_spec.rb +284 -0
- data/testing/rspec/spec/repository_spec.rb +3 -3
- data/testing/rspec/spec/select_feature_dsl_spec.rb +8 -8
- data/testing/rspec/spec/select_scen_outline_dsl_spec.rb +14 -14
- data/testing/rspec/spec/select_scenario_dsl_spec.rb +9 -9
- data/testing/rspec/spec/spec_helper.rb +7 -17
- metadata +52 -23
- data/testing/cucumber/support/transforms.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2cef04f054cecd8e489f46daa063be8ac02360c53f5d7d8fe8e551ce337e5d34
|
4
|
+
data.tar.gz: 6714fecd35d662a966cb82f9bda4a43e308b38597834a53d03c337fe9ad1d75d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5779cca5eef6d487d88fc9089bc4374b03ccea619443f333490217fbf2b747288b7f297078d8c148969bc53c50c00ea5fa4d0f876473cb9e22afe70bbfbbe795
|
7
|
+
data.tar.gz: 4def191c2d3dd5ee166980dde04f3f05599614797d015ca242574a467dcdb1a6bab05c3b6648e70c50d5a66f8b960da6f1790cd44117ec699ee7902b040a3909
|
data/lib/cql.rb
CHANGED
@@ -1,176 +1,11 @@
|
|
1
1
|
require 'cuke_modeler'
|
2
2
|
require 'cql/map_reduce'
|
3
3
|
require 'cql/queriable'
|
4
|
+
require 'cql/query'
|
5
|
+
require 'cql/repository'
|
4
6
|
|
5
|
-
module CQL
|
6
|
-
|
7
|
-
class Query
|
8
|
-
include Dsl
|
9
|
-
attr_reader :data, :what
|
10
|
-
|
11
|
-
def format_data data
|
12
|
-
space_data
|
13
|
-
|
14
|
-
Array.new.tap do |result_array|
|
15
|
-
data.each do |element|
|
16
|
-
result_array << Hash.new.tap do |result|
|
17
|
-
@what.each_with_index do |attribute, index|
|
18
|
-
key = determine_key(attribute, index)
|
19
|
-
value = determine_value(element, attribute, index)
|
20
|
-
|
21
|
-
result[key] = value
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize(directory, &block)
|
30
|
-
# Set root object
|
31
|
-
@data = directory
|
32
|
-
|
33
|
-
# Populate configurables from DSL block
|
34
|
-
self.instance_eval(&block)
|
35
|
-
|
36
|
-
|
37
|
-
raise(ArgumentError, "A query must specify a 'select' clause") unless @what
|
38
|
-
raise(ArgumentError, "A query must specify a 'from' clause") unless @from
|
39
|
-
|
40
|
-
warn("Multiple selections made without using an 'as' clause") unless @name_transforms || (@what.count == @what.uniq.count)
|
41
|
-
|
42
|
-
# Gather relevant objects from root object and filters
|
43
|
-
@data = CQL::MapReduce.gather_objects(@data, @from, @filters)
|
44
|
-
|
45
|
-
# Extract properties from gathered objects
|
46
|
-
@data = format_output(@data)
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
|
53
|
-
def format_output(data)
|
54
|
-
format_data(data)
|
55
|
-
end
|
56
|
-
|
57
|
-
def determine_key(attribute, index)
|
58
|
-
key = mapped_attribute(@name_transforms, attribute, index) if @name_transforms
|
59
|
-
|
60
|
-
key || attribute
|
61
|
-
end
|
62
|
-
|
63
|
-
def determine_value(element, attribute, index)
|
64
|
-
original_value = attribute.is_a?(Symbol) ? determine_special_value(element, attribute) : determine_normal_value(element, attribute)
|
65
|
-
|
66
|
-
if @value_transforms
|
67
|
-
value = mapped_attribute(@value_transforms, attribute, index)
|
68
|
-
value = value.call(original_value) if value.is_a?(Proc)
|
69
|
-
end
|
70
|
-
|
71
|
-
value || original_value
|
72
|
-
end
|
73
7
|
|
74
|
-
|
75
|
-
# todo - Not sure what other special values to have but this could be expanded upon later.
|
76
|
-
case attribute
|
77
|
-
when :self, :model
|
78
|
-
val = element
|
79
|
-
else
|
80
|
-
raise(ArgumentError, ":#{attribute} is not a valid attribute for selection.")
|
81
|
-
end
|
8
|
+
# The top level namespace for CQL related code.
|
82
9
|
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
def determine_normal_value(element, attribute)
|
87
|
-
if element.respond_to?(attribute)
|
88
|
-
element.send(attribute)
|
89
|
-
else
|
90
|
-
raise(ArgumentError, "'#{attribute}' is not a valid attribute for selection from a '#{element.class}'.")
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def mapped_attribute(mappings, attribute, location)
|
95
|
-
case
|
96
|
-
when mappings.is_a?(Array)
|
97
|
-
value = mappings[location]
|
98
|
-
when mappings.is_a?(Hash)
|
99
|
-
if mappings[attribute]
|
100
|
-
value = mappings[attribute][location]
|
101
|
-
end
|
102
|
-
else
|
103
|
-
# todo - add error message
|
104
|
-
end
|
105
|
-
|
106
|
-
value
|
107
|
-
end
|
108
|
-
|
109
|
-
def space_data
|
110
|
-
space_renamings
|
111
|
-
space_transforms
|
112
|
-
end
|
113
|
-
|
114
|
-
def space_renamings
|
115
|
-
if @name_transforms.is_a?(Hash)
|
116
|
-
new_names = {}
|
117
|
-
|
118
|
-
@name_transforms.each_pair do |key, value|
|
119
|
-
new_names[key] = []
|
120
|
-
|
121
|
-
@what.each do |attribute|
|
122
|
-
if attribute == key
|
123
|
-
new_names[key] << value.shift
|
124
|
-
else
|
125
|
-
new_names[key] << nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
@name_transforms = new_names
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def space_transforms
|
135
|
-
if @value_transforms.is_a?(Hash)
|
136
|
-
new_values = {}
|
137
|
-
|
138
|
-
@value_transforms.each_pair do |key, value|
|
139
|
-
new_values[key] = []
|
140
|
-
|
141
|
-
@what.each do |attribute|
|
142
|
-
if attribute == key
|
143
|
-
new_values[key] << value.shift
|
144
|
-
else
|
145
|
-
new_values[key] << nil
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
@value_transforms = new_values
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
|
156
|
-
|
157
|
-
class Repository
|
158
|
-
|
159
|
-
include Queriable
|
160
|
-
|
161
|
-
|
162
|
-
def initialize(repository_root)
|
163
|
-
case
|
164
|
-
when repository_root.is_a?(String)
|
165
|
-
root = CukeModeler::Directory.new(repository_root)
|
166
|
-
when repository_root.class.to_s =~ /CukeModeler/
|
167
|
-
root = repository_root
|
168
|
-
else
|
169
|
-
raise(ArgumentError, "Don't know how to make a repository from a #{repository_root.class}")
|
170
|
-
end
|
171
|
-
|
172
|
-
@query_root = root
|
173
|
-
end
|
174
|
-
|
175
|
-
end
|
10
|
+
module CQL
|
176
11
|
end
|
data/lib/cql/dsl.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
module CQL
|
2
|
+
|
3
|
+
# The Domain Specific Language used for performing queries.
|
4
|
+
|
2
5
|
module Dsl
|
3
6
|
|
7
|
+
|
8
|
+
# Any undefined method is assumed to mean its String equivalent, thus allowing a more convenient query syntax.
|
4
9
|
def method_missing(method_name)
|
5
10
|
method_name.to_s
|
6
11
|
end
|
7
12
|
|
13
|
+
# Adds a *transform* clause to the query. See the corresponding Cucumber documentation for details.
|
8
14
|
def transform(*attribute_transforms, &block)
|
9
15
|
# todo - Still feels like some as/transform code duplication but I think that it would get too meta if I
|
10
16
|
# reduced it any further. Perhaps change how the transforms are handled so that there doesn't have to be
|
@@ -16,13 +22,14 @@ module CQL
|
|
16
22
|
add_transforms(attribute_transforms, @value_transforms)
|
17
23
|
end
|
18
24
|
|
25
|
+
# Adds an *as* clause to the query. See the corresponding Cucumber documentation for details.
|
19
26
|
def as(*name_transforms)
|
20
27
|
prep_variable('name_transforms', name_transforms) unless @name_transforms
|
21
28
|
|
22
29
|
add_transforms(name_transforms, @name_transforms)
|
23
30
|
end
|
24
31
|
|
25
|
-
#
|
32
|
+
# Adds a *select* clause to the query. See the corresponding Cucumber documentation for details.
|
26
33
|
def select *what
|
27
34
|
what = [:self] if what.empty?
|
28
35
|
|
@@ -30,17 +37,19 @@ module CQL
|
|
30
37
|
@what.concat(what)
|
31
38
|
end
|
32
39
|
|
40
|
+
# Adds a *name* filter to the query. See the corresponding Cucumber documentation for details.
|
33
41
|
def name *args
|
34
42
|
return 'name' if args.size == 0
|
35
43
|
CQL::NameFilter.new args[0]
|
36
44
|
end
|
37
45
|
|
46
|
+
# Adds a *line* filter to the query. See the corresponding Cucumber documentation for details.
|
38
47
|
def line *args
|
39
48
|
return 'line' if args.size == 0
|
40
49
|
CQL::LineFilter.new args.first
|
41
50
|
end
|
42
51
|
|
43
|
-
#from clause
|
52
|
+
# Adds a *from* clause to the query. See the corresponding Cucumber documentation for details.
|
44
53
|
def from(*targets)
|
45
54
|
@from ||= []
|
46
55
|
|
@@ -49,7 +58,7 @@ module CQL
|
|
49
58
|
@from.concat(targets)
|
50
59
|
end
|
51
60
|
|
52
|
-
#with clause
|
61
|
+
# Adds a *with* clause to the query. See the corresponding Cucumber documentation for details.
|
53
62
|
def with(*conditions, &block)
|
54
63
|
@filters ||= []
|
55
64
|
|
@@ -59,7 +68,7 @@ module CQL
|
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
62
|
-
#without clause
|
71
|
+
# Adds a *without* clause to the query. See the corresponding Cucumber documentation for details.
|
63
72
|
def without(*conditions, &block)
|
64
73
|
@filters ||= []
|
65
74
|
|
@@ -69,9 +78,15 @@ module CQL
|
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
81
|
+
# Not a part of the public API. Subject to change at any time.
|
72
82
|
class Comparison
|
73
|
-
attr_accessor :operator, :amount
|
74
83
|
|
84
|
+
# the operator used for comparison
|
85
|
+
attr_accessor :operator,
|
86
|
+
# value that will be compared against
|
87
|
+
:amount
|
88
|
+
|
89
|
+
# Creates a new comparison object
|
75
90
|
def initialize operator, amount
|
76
91
|
@operator = operator
|
77
92
|
@amount = amount
|
@@ -79,48 +94,67 @@ module CQL
|
|
79
94
|
|
80
95
|
end
|
81
96
|
|
97
|
+
# Adds a *tc* filter to the query. See the corresponding Cucumber documentation for details.
|
82
98
|
def tc comparison
|
83
99
|
TagCountFilter.new 'tc', comparison
|
84
100
|
end
|
85
101
|
|
102
|
+
# Adds a *lc* filter to the query. See the corresponding Cucumber documentation for details.
|
86
103
|
def lc comparison
|
87
104
|
CQL::SsoLineCountFilter.new('lc', comparison)
|
88
105
|
end
|
89
106
|
|
107
|
+
# Adds an *ssoc* filter to the query. See the corresponding Cucumber documentation for details.
|
90
108
|
def ssoc comparison
|
91
109
|
TestCountFilter.new([CukeModeler::Scenario, CukeModeler::Outline], comparison)
|
92
110
|
end
|
93
111
|
|
112
|
+
# Adds an *sc* filter to the query. See the corresponding Cucumber documentation for details.
|
94
113
|
def sc comparison
|
95
114
|
TestCountFilter.new([CukeModeler::Scenario], comparison)
|
96
115
|
end
|
97
116
|
|
117
|
+
# Adds an *soc* filter to the query. See the corresponding Cucumber documentation for details.
|
98
118
|
def soc comparison
|
99
119
|
TestCountFilter.new([CukeModeler::Outline], comparison)
|
100
120
|
end
|
101
121
|
|
122
|
+
# Adds a *gt* filter operator to the query. See the corresponding Cucumber documentation for details.
|
102
123
|
def gt amount
|
103
124
|
Comparison.new '>', amount
|
104
125
|
end
|
105
126
|
|
127
|
+
# Adds a *gte* filter operator to the query. See the corresponding Cucumber documentation for details.
|
106
128
|
def gte amount
|
107
129
|
Comparison.new '>=', amount
|
108
130
|
end
|
109
131
|
|
132
|
+
# Adds an *lt* filter operator to the query. See the corresponding Cucumber documentation for details.
|
110
133
|
def lt amount
|
111
134
|
Comparison.new '<', amount
|
112
135
|
end
|
113
136
|
|
137
|
+
# Adds an *lte* filter operator to the query. See the corresponding Cucumber documentation for details.
|
114
138
|
def lte amount
|
115
139
|
Comparison.new '<=', amount
|
116
140
|
end
|
117
141
|
|
142
|
+
# Adds an *eq* filter operator to the query. See the corresponding Cucumber documentation for details.
|
143
|
+
def eq amount
|
144
|
+
Comparison.new '==', amount
|
145
|
+
end
|
146
|
+
|
147
|
+
# Adds a *tags* filter to the query. See the corresponding Cucumber documentation for details.
|
118
148
|
def tags *tags
|
119
149
|
return "tags" if tags.size == 0
|
120
150
|
|
121
151
|
TagFilter.new tags
|
122
152
|
end
|
123
153
|
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
|
124
158
|
def translate_shorthand(where)
|
125
159
|
where.split('_').map(&:capitalize).join
|
126
160
|
end
|
data/lib/cql/feature_filters.rb
CHANGED
@@ -3,8 +3,10 @@ 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 TestCountFilter < TypeCountFilter
|
7
8
|
|
9
|
+
# Counts the numbers of tests of a certain type
|
8
10
|
def type_count(feature)
|
9
11
|
feature.tests.find_all { |test| types.include?(test.class) }.size
|
10
12
|
end
|
data/lib/cql/filters.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
module CQL
|
2
2
|
|
3
|
+
# Not a part of the public API. Subject to change at any time.
|
3
4
|
class TagFilter
|
5
|
+
|
6
|
+
# Tags to match
|
4
7
|
attr_reader :tags
|
5
8
|
|
9
|
+
# Creates a new filter
|
6
10
|
def initialize tags
|
7
11
|
@tags = tags
|
8
12
|
end
|
9
13
|
|
14
|
+
# Returns whether or not the object has the target tags
|
10
15
|
def has_tags?(object, target_tags)
|
11
16
|
target_tags.all? { |target_tag|
|
12
17
|
tags = object.tags
|
@@ -15,6 +20,7 @@ module CQL
|
|
15
20
|
}
|
16
21
|
end
|
17
22
|
|
23
|
+
# Filters the input models so that only the desired ones are returned
|
18
24
|
def execute(objects, negate)
|
19
25
|
method = negate ? :reject : :select
|
20
26
|
|
@@ -23,15 +29,20 @@ module CQL
|
|
23
29
|
|
24
30
|
end
|
25
31
|
|
32
|
+
# Not a part of the public API. Subject to change at any time.
|
26
33
|
class ContentMatchFilter
|
34
|
+
|
35
|
+
# Pattern to match
|
27
36
|
attr_reader :pattern
|
28
37
|
|
38
|
+
# Creates a new filter
|
29
39
|
def initialize(pattern)
|
30
40
|
raise(ArgumentError, "Can only match a String or Regexp. Got #{pattern.class}.") unless pattern.is_a?(String) || pattern.is_a?(Regexp)
|
31
41
|
|
32
42
|
@pattern = pattern
|
33
43
|
end
|
34
44
|
|
45
|
+
# Returns whether or not the content matches the pattern
|
35
46
|
def content_match?(content)
|
36
47
|
if pattern.is_a?(String)
|
37
48
|
content.any? { |thing| thing == pattern }
|
@@ -42,14 +53,22 @@ module CQL
|
|
42
53
|
|
43
54
|
end
|
44
55
|
|
56
|
+
# Not a part of the public API. Subject to change at any time.
|
45
57
|
class TypeCountFilter
|
46
|
-
attr_reader :types, :comparison
|
47
58
|
|
59
|
+
# the types of object that will be filtered against
|
60
|
+
attr_reader :types,
|
61
|
+
# the comparison that will be made between the objects
|
62
|
+
:comparison
|
63
|
+
|
64
|
+
# Creates a new filter
|
48
65
|
def initialize types, comparison
|
49
66
|
@types = types
|
50
67
|
@comparison = comparison
|
51
68
|
end
|
52
69
|
|
70
|
+
# Not a part of the public API. Subject to change at any time.
|
71
|
+
# Filters the input models so that only the desired ones are returned
|
53
72
|
def execute(input, negate)
|
54
73
|
method = negate ? :reject : :select
|
55
74
|
|
@@ -60,8 +79,10 @@ module CQL
|
|
60
79
|
|
61
80
|
end
|
62
81
|
|
82
|
+
# Not a part of the public API. Subject to change at any time.
|
63
83
|
class NameFilter < ContentMatchFilter
|
64
84
|
|
85
|
+
# Filters the input models so that only the desired ones are returned
|
65
86
|
def execute(input, negate)
|
66
87
|
method = negate ? :reject : :select
|
67
88
|
|
@@ -72,8 +93,11 @@ module CQL
|
|
72
93
|
|
73
94
|
end
|
74
95
|
|
96
|
+
|
97
|
+
# Not a part of the public API. Subject to change at any time.
|
75
98
|
class TagCountFilter < TypeCountFilter
|
76
99
|
|
100
|
+
# Counts the numbers of tags on a test
|
77
101
|
def type_count(test)
|
78
102
|
test.tags.size
|
79
103
|
end
|