cql 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cql.rb +4 -169
- data/lib/cql/dsl.rb +28 -4
- data/lib/cql/feature_filters.rb +2 -0
- data/lib/cql/filters.rb +11 -0
- 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 +154 -0
- data/lib/cql/repository.rb +24 -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 -12
- data/testing/cucumber/features/clauses/select_clause.feature +10 -9
- data/testing/cucumber/features/clauses/transform_clause.feature +10 -9
- data/testing/cucumber/features/clauses/with_clause.feature +9 -7
- data/testing/cucumber/features/dsl.feature +15 -12
- data/testing/cucumber/step_definitions/setup_steps.rb +1 -13
- data/testing/cucumber/step_definitions/verification_steps.rb +1 -1
- data/testing/cucumber/support/env.rb +9 -9
- data/testing/gemfiles/cuke_modeler0.gemfile +1 -0
- data/testing/gemfiles/cuke_modeler1.gemfile +1 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba9e7d8795d805ccd50924027199c698f2365df1
|
4
|
+
data.tar.gz: 0a452146ee7d5ca32764671835de2dcc486addd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 703ed14d3880d6017ea2d0699ead3aa22ffb6ee3235071fb095300db35c7754ba857225623265a39e382d8c24b0ec4ca9e547bf04ef16b4ee5fe39037fee3c16
|
7
|
+
data.tar.gz: 9cec568a459073e2bf91bea830b8365748724091592b00eb56df32037393cdad119d6de2d5c1e1a858c564832e6948ffc2e7c82dc480861ec97f26f716e447eb
|
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,6 +78,7 @@ 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
83
|
attr_accessor :operator, :amount
|
74
84
|
|
@@ -79,48 +89,62 @@ module CQL
|
|
79
89
|
|
80
90
|
end
|
81
91
|
|
92
|
+
# Adds a *tc* filter to the query. See the corresponding Cucumber documentation for details.
|
82
93
|
def tc comparison
|
83
94
|
TagCountFilter.new 'tc', comparison
|
84
95
|
end
|
85
96
|
|
97
|
+
# Adds a *lc* filter to the query. See the corresponding Cucumber documentation for details.
|
86
98
|
def lc comparison
|
87
99
|
CQL::SsoLineCountFilter.new('lc', comparison)
|
88
100
|
end
|
89
101
|
|
102
|
+
# Adds an *ssoc* filter to the query. See the corresponding Cucumber documentation for details.
|
90
103
|
def ssoc comparison
|
91
104
|
TestCountFilter.new([CukeModeler::Scenario, CukeModeler::Outline], comparison)
|
92
105
|
end
|
93
106
|
|
107
|
+
# Adds an *sc* filter to the query. See the corresponding Cucumber documentation for details.
|
94
108
|
def sc comparison
|
95
109
|
TestCountFilter.new([CukeModeler::Scenario], comparison)
|
96
110
|
end
|
97
111
|
|
112
|
+
# Adds an *soc* filter to the query. See the corresponding Cucumber documentation for details.
|
98
113
|
def soc comparison
|
99
114
|
TestCountFilter.new([CukeModeler::Outline], comparison)
|
100
115
|
end
|
101
116
|
|
117
|
+
# Adds a *gt* filter operator to the query. See the corresponding Cucumber documentation for details.
|
102
118
|
def gt amount
|
103
119
|
Comparison.new '>', amount
|
104
120
|
end
|
105
121
|
|
122
|
+
# Adds a *gte* filter operator to the query. See the corresponding Cucumber documentation for details.
|
106
123
|
def gte amount
|
107
124
|
Comparison.new '>=', amount
|
108
125
|
end
|
109
126
|
|
127
|
+
# Adds an *lt* filter operator to the query. See the corresponding Cucumber documentation for details.
|
110
128
|
def lt amount
|
111
129
|
Comparison.new '<', amount
|
112
130
|
end
|
113
131
|
|
132
|
+
# Adds an *lte* filter operator to the query. See the corresponding Cucumber documentation for details.
|
114
133
|
def lte amount
|
115
134
|
Comparison.new '<=', amount
|
116
135
|
end
|
117
136
|
|
137
|
+
# Adds a *tags* filter to the query. See the corresponding Cucumber documentation for details.
|
118
138
|
def tags *tags
|
119
139
|
return "tags" if tags.size == 0
|
120
140
|
|
121
141
|
TagFilter.new tags
|
122
142
|
end
|
123
143
|
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
|
124
148
|
def translate_shorthand(where)
|
125
149
|
where.split('_').map(&:capitalize).join
|
126
150
|
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,5 +1,6 @@
|
|
1
1
|
module CQL
|
2
2
|
|
3
|
+
# Not a part of the public API. Subject to change at any time.
|
3
4
|
class TagFilter
|
4
5
|
attr_reader :tags
|
5
6
|
|
@@ -15,6 +16,7 @@ module CQL
|
|
15
16
|
}
|
16
17
|
end
|
17
18
|
|
19
|
+
# Filters the input models so that only the desired ones are returned
|
18
20
|
def execute(objects, negate)
|
19
21
|
method = negate ? :reject : :select
|
20
22
|
|
@@ -23,6 +25,7 @@ module CQL
|
|
23
25
|
|
24
26
|
end
|
25
27
|
|
28
|
+
# Not a part of the public API. Subject to change at any time.
|
26
29
|
class ContentMatchFilter
|
27
30
|
attr_reader :pattern
|
28
31
|
|
@@ -42,6 +45,7 @@ module CQL
|
|
42
45
|
|
43
46
|
end
|
44
47
|
|
48
|
+
# Not a part of the public API. Subject to change at any time.
|
45
49
|
class TypeCountFilter
|
46
50
|
attr_reader :types, :comparison
|
47
51
|
|
@@ -50,6 +54,8 @@ module CQL
|
|
50
54
|
@comparison = comparison
|
51
55
|
end
|
52
56
|
|
57
|
+
# Not a part of the public API. Subject to change at any time.
|
58
|
+
# Filters the input models so that only the desired ones are returned
|
53
59
|
def execute(input, negate)
|
54
60
|
method = negate ? :reject : :select
|
55
61
|
|
@@ -60,8 +66,10 @@ module CQL
|
|
60
66
|
|
61
67
|
end
|
62
68
|
|
69
|
+
# Not a part of the public API. Subject to change at any time.
|
63
70
|
class NameFilter < ContentMatchFilter
|
64
71
|
|
72
|
+
# Filters the input models so that only the desired ones are returned
|
65
73
|
def execute(input, negate)
|
66
74
|
method = negate ? :reject : :select
|
67
75
|
|
@@ -72,8 +80,11 @@ module CQL
|
|
72
80
|
|
73
81
|
end
|
74
82
|
|
83
|
+
|
84
|
+
# Not a part of the public API. Subject to change at any time.
|
75
85
|
class TagCountFilter < TypeCountFilter
|
76
86
|
|
87
|
+
# Counts the numbers of tags on a test
|
77
88
|
def type_count(test)
|
78
89
|
test.tags.size
|
79
90
|
end
|
data/lib/cql/map_reduce.rb
CHANGED
@@ -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
|
|
data/lib/cql/model_dsl.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
# todo - add some
|
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
|
|
data/lib/cql/queriable.rb
CHANGED
@@ -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
|
-
|
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
|
|
data/lib/cql/query.rb
ADDED
@@ -0,0 +1,154 @@
|
|
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
|
+
attr_reader :data, :what
|
9
|
+
|
10
|
+
|
11
|
+
def initialize(directory, &block)
|
12
|
+
# Set root object
|
13
|
+
@data = directory
|
14
|
+
|
15
|
+
# Populate configurables from DSL block
|
16
|
+
self.instance_eval(&block)
|
17
|
+
|
18
|
+
|
19
|
+
raise(ArgumentError, "A query must specify a 'select' clause") unless @what
|
20
|
+
raise(ArgumentError, "A query must specify a 'from' clause") unless @from
|
21
|
+
|
22
|
+
warn("Multiple selections made without using an 'as' clause") unless @name_transforms || (@what.count == @what.uniq.count)
|
23
|
+
|
24
|
+
# Gather relevant objects from root object and filters
|
25
|
+
@data = CQL::MapReduce.gather_objects(@data, @from, @filters)
|
26
|
+
|
27
|
+
# Extract properties from gathered objects
|
28
|
+
@data = format_output(@data)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
|
35
|
+
def format_output(data)
|
36
|
+
format_data(data)
|
37
|
+
end
|
38
|
+
|
39
|
+
def format_data data
|
40
|
+
space_data
|
41
|
+
|
42
|
+
Array.new.tap do |result_array|
|
43
|
+
data.each do |element|
|
44
|
+
result_array << Hash.new.tap do |result|
|
45
|
+
@what.each_with_index do |attribute, index|
|
46
|
+
key = determine_key(attribute, index)
|
47
|
+
value = determine_value(element, attribute, index)
|
48
|
+
|
49
|
+
result[key] = value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def determine_key(attribute, index)
|
57
|
+
key = mapped_attribute(@name_transforms, attribute, index) if @name_transforms
|
58
|
+
|
59
|
+
key || attribute
|
60
|
+
end
|
61
|
+
|
62
|
+
def determine_value(element, attribute, index)
|
63
|
+
original_value = attribute.is_a?(Symbol) ? determine_special_value(element, attribute) : determine_normal_value(element, attribute)
|
64
|
+
|
65
|
+
if @value_transforms
|
66
|
+
value = mapped_attribute(@value_transforms, attribute, index)
|
67
|
+
value = value.call(original_value) if value.is_a?(Proc)
|
68
|
+
end
|
69
|
+
|
70
|
+
value || original_value
|
71
|
+
end
|
72
|
+
|
73
|
+
def determine_special_value(element, attribute)
|
74
|
+
# todo - Not sure what other special values to have but this could be expanded upon later.
|
75
|
+
case attribute
|
76
|
+
when :self, :model
|
77
|
+
val = element
|
78
|
+
else
|
79
|
+
raise(ArgumentError, ":#{attribute} is not a valid attribute for selection.")
|
80
|
+
end
|
81
|
+
|
82
|
+
val
|
83
|
+
end
|
84
|
+
|
85
|
+
def determine_normal_value(element, attribute)
|
86
|
+
if element.respond_to?(attribute)
|
87
|
+
element.send(attribute)
|
88
|
+
else
|
89
|
+
raise(ArgumentError, "'#{attribute}' is not a valid attribute for selection from a '#{element.class}'.")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def mapped_attribute(mappings, attribute, location)
|
94
|
+
case
|
95
|
+
when mappings.is_a?(Array)
|
96
|
+
value = mappings[location]
|
97
|
+
when mappings.is_a?(Hash)
|
98
|
+
if mappings[attribute]
|
99
|
+
value = mappings[attribute][location]
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise(ArgumentError, "Unknown mapping type '#{mappings.class}'")
|
103
|
+
end
|
104
|
+
|
105
|
+
value
|
106
|
+
end
|
107
|
+
|
108
|
+
def space_data
|
109
|
+
space_renamings
|
110
|
+
space_transforms
|
111
|
+
end
|
112
|
+
|
113
|
+
def space_renamings
|
114
|
+
if @name_transforms.is_a?(Hash)
|
115
|
+
new_names = {}
|
116
|
+
|
117
|
+
@name_transforms.each_pair do |key, value|
|
118
|
+
new_names[key] = []
|
119
|
+
|
120
|
+
@what.each do |attribute|
|
121
|
+
if attribute == key
|
122
|
+
new_names[key] << value.shift
|
123
|
+
else
|
124
|
+
new_names[key] << nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
@name_transforms = new_names
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def space_transforms
|
134
|
+
if @value_transforms.is_a?(Hash)
|
135
|
+
new_values = {}
|
136
|
+
|
137
|
+
@value_transforms.each_pair do |key, value|
|
138
|
+
new_values[key] = []
|
139
|
+
|
140
|
+
@what.each do |attribute|
|
141
|
+
if attribute == key
|
142
|
+
new_values[key] << value.shift
|
143
|
+
else
|
144
|
+
new_values[key] << nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
@value_transforms = new_values
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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
|
+
def initialize(repository_root)
|
11
|
+
case
|
12
|
+
when repository_root.is_a?(String)
|
13
|
+
root = CukeModeler::Directory.new(repository_root)
|
14
|
+
when repository_root.class.to_s =~ /CukeModeler/
|
15
|
+
root = repository_root
|
16
|
+
else
|
17
|
+
raise(ArgumentError, "Don't know how to make a repository from a #{repository_root.class}")
|
18
|
+
end
|
19
|
+
|
20
|
+
@query_root = root
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/cql/sso_filters.rb
CHANGED
@@ -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
|
data/lib/cql/version.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
19
|
-
And
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
22
|
-
And
|
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
|
@@ -2,18 +2,20 @@ Feature: 'select' clause
|
|
2
2
|
|
3
3
|
The *select* clause specifies what attributes will be retrieved from the models specified by the *from* clause. Multiple values can be given and they are delimited by a comma. The *select* clause can take any method to which the objects specified by *from* know how to respond. The clause can also be given a special identifier in order to return the underlying models themselves instead of their attributes. If no attributes are specified then the underlying model will be returned instead, just as if the special identifier had been used (it is simply an alternate syntax and may look nicer in some queries).
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
Sample usage:
|
6
|
+
````
|
7
|
+
cql_repo.query do
|
8
|
+
select name, tags, description_text
|
9
|
+
from features
|
10
|
+
end
|
11
|
+
````
|
10
12
|
|
11
13
|
This clause can be repeated multiple times. The arguments for successive clauses are simply added to the previous arguments.
|
12
14
|
|
13
15
|
|
14
|
-
Background:
|
15
|
-
Given a
|
16
|
-
And
|
16
|
+
Background: Repository with models
|
17
|
+
Given a repository to query
|
18
|
+
And the following feature has been modeled in the repository:
|
17
19
|
"""
|
18
20
|
Feature: A test feature
|
19
21
|
|
@@ -33,7 +35,6 @@ Feature: 'select' clause
|
|
33
35
|
| param |
|
34
36
|
| value |
|
35
37
|
"""
|
36
|
-
And a repository is made from "test_directory"
|
37
38
|
|
38
39
|
|
39
40
|
Scenario: Using 'select' to specify which attributes of an object to return
|
@@ -2,20 +2,22 @@ Feature: 'transform' clause
|
|
2
2
|
|
3
3
|
The *transform* clause allows you to change the values of the attributes specified by the *select* clause after they are gathered. Value transforming can be done as a list of transformation blocks that are applied in order or as a mapping of specific keys and their transformations.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
Sample usage:
|
6
|
+
````
|
7
|
+
cql_repo.query do
|
8
|
+
select name
|
9
|
+
transform { |name| name.upcase }
|
10
|
+
from features
|
11
|
+
end
|
12
|
+
````
|
11
13
|
|
12
14
|
This will return a list of all of the feature names but with all of their names upcased.
|
13
15
|
|
14
16
|
This clause can be repeated multiple times. When using lists of transforms, the arguments for successive clauses are simply added to the previous arguments. When using mapped transforms, the mappings are likewise combined. If the same key is mapped more than once, the mappings are tracked separately such that they can be applied to different instances of attribute retrieval (see examples below).
|
15
17
|
|
16
18
|
Background: A sample Cucumber suite
|
17
|
-
Given a
|
18
|
-
And
|
19
|
+
Given a repository to query
|
20
|
+
And the following feature has been modeled in the repository:
|
19
21
|
"""
|
20
22
|
Feature: A test feature
|
21
23
|
|
@@ -35,7 +37,6 @@ Feature: 'transform' clause
|
|
35
37
|
| param |
|
36
38
|
| value |
|
37
39
|
"""
|
38
|
-
And a repository is made from "test_directory"
|
39
40
|
|
40
41
|
|
41
42
|
Scenario: Using 'transform' to change values after they are gathered
|
@@ -3,13 +3,15 @@ Feature: 'with' clause
|
|
3
3
|
|
4
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).
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
Sample usage:
|
7
|
+
````
|
8
|
+
cql_repo.query do
|
9
|
+
select name, tags, description_text
|
10
|
+
from features
|
11
|
+
with { |feature| feature.name =~ /foo/ }
|
12
|
+
with tc lt 3
|
13
|
+
end
|
14
|
+
````
|
13
15
|
|
14
16
|
This clause can be repeated multiple times. The arguments for successive clauses are simply added to the previous arguments.
|
15
17
|
|
@@ -2,22 +2,25 @@ Feature: DSL
|
|
2
2
|
|
3
3
|
The cql gem uses a DSL to specify queries on a repository object that holds the models which represent a Cucumber test suite. The DSL can query for any attribute that is available on the underlying models.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
Sample usage:
|
6
|
+
````
|
7
|
+
cql_repo.query do
|
8
|
+
select name, source_line
|
9
|
+
from features
|
10
|
+
end
|
11
|
+
````
|
10
12
|
|
11
13
|
Query results are returned as a list of attribute mappings for all of the models found in the repository. The sample query above might return:
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
````
|
16
|
+
[{'name' => 'Feature 1', 'source_line' => 1},
|
17
|
+
{'name' => 'Feature 2', 'source_line' => 3},
|
18
|
+
{'name' => 'Feature 3', 'source_line' => 10}]
|
19
|
+
````
|
17
20
|
|
18
21
|
Background: A sample Cucumber suite
|
19
|
-
Given a
|
20
|
-
And
|
22
|
+
Given a repository to query
|
23
|
+
And the following feature has been modeled in the repository:
|
21
24
|
"""
|
22
25
|
Feature: A test feature
|
23
26
|
|
@@ -37,7 +40,7 @@ Feature: DSL
|
|
37
40
|
| param |
|
38
41
|
| value |
|
39
42
|
"""
|
40
|
-
|
43
|
+
|
41
44
|
|
42
45
|
Scenario: Automatic string conversion
|
43
46
|
|
@@ -1,17 +1,5 @@
|
|
1
1
|
Given(/^a directory "([^"]*)"$/) do |partial_directory_path|
|
2
|
-
|
3
|
-
|
4
|
-
FileUtils.mkpath(directory_path) unless File.exists?(directory_path)
|
5
|
-
end
|
6
|
-
|
7
|
-
And(/^a file "([^"]*)":$/) do |partial_file_path, file_text|
|
8
|
-
File.open("#{@default_file_directory}/#{partial_file_path}", 'w') { |file| file.write file_text }
|
9
|
-
end
|
10
|
-
|
11
|
-
And(/^a repository is made from "([^"]*)"$/) do |partial_path|
|
12
|
-
repo_path = "#{@default_file_directory}/#{partial_path}"
|
13
|
-
|
14
|
-
@repository = CQL::Repository.new(repo_path)
|
2
|
+
create_path(partial_directory_path)
|
15
3
|
end
|
16
4
|
|
17
5
|
Given(/^the models provided by CukeModeler$/) do
|
@@ -41,7 +41,7 @@ Then(/^the result is the same as the result of the following query:$/) do |query
|
|
41
41
|
end
|
42
42
|
|
43
43
|
Then(/^the following code executes without error:$/) do |code_text|
|
44
|
-
code_text
|
44
|
+
code_text.sub!('path/to', @temp_dir)
|
45
45
|
|
46
46
|
expect { eval(code_text) }.to_not raise_error
|
47
47
|
end
|
@@ -3,22 +3,22 @@ unless RUBY_VERSION.to_s < '1.9.0'
|
|
3
3
|
SimpleCov.command_name('cql-cucumber')
|
4
4
|
end
|
5
5
|
|
6
|
+
require 'tmpdir'
|
6
7
|
|
7
8
|
require 'cql'
|
8
9
|
require 'cql/model_dsl'
|
9
10
|
|
10
11
|
|
11
|
-
Before do
|
12
|
-
@default_file_directory = "#{File.dirname(__FILE__)}/../temp_files"
|
13
|
-
|
14
|
-
FileUtils.mkdir(@default_file_directory)
|
15
|
-
end
|
16
|
-
|
17
12
|
After do
|
18
|
-
FileUtils.remove_dir(@
|
13
|
+
FileUtils.remove_dir(@temp_dir, true) if @temp_dir
|
19
14
|
end
|
20
15
|
|
21
16
|
|
22
|
-
def
|
23
|
-
|
17
|
+
def create_path(path)
|
18
|
+
@temp_dir ||= Dir.mktmpdir
|
19
|
+
path = path.sub('path/to/', "#{@temp_dir}/")
|
20
|
+
|
21
|
+
Dir.mkdir(path)
|
22
|
+
|
23
|
+
path
|
24
24
|
end
|
@@ -20,6 +20,7 @@ if RUBY_VERSION =~ /^1\./
|
|
20
20
|
gem 'tins', '< 1.7' # The 'tins' gem requires Ruby 2.x on/after this version
|
21
21
|
gem 'json', '< 2.0' # The 'json' gem drops pre-Ruby 2.x support on/after this version
|
22
22
|
gem 'term-ansicolor', '< 1.4' # The 'term-ansicolor' gem requires Ruby 2.x on/after this version
|
23
|
+
gem 'unf_ext', '< 0.0.7.3' # Requires Ruby 2.x on/after this version
|
23
24
|
|
24
25
|
if RbConfig::CONFIG['host_os'].downcase =~ /mswin|msys|mingw32/
|
25
26
|
gem 'ffi', '< 1.9.15' # The 'ffi' gem, for Windows, requires Ruby 2.x on/after this version
|
@@ -20,6 +20,7 @@ if RUBY_VERSION =~ /^1\./
|
|
20
20
|
gem 'tins', '< 1.7' # The 'tins' gem requires Ruby 2.x on/after this version
|
21
21
|
gem 'json', '< 2.0' # The 'json' gem drops pre-Ruby 2.x support on/after this version
|
22
22
|
gem 'term-ansicolor', '< 1.4' # The 'term-ansicolor' gem requires Ruby 2.x on/after this version
|
23
|
+
gem 'unf_ext', '< 0.0.7.3' # Requires Ruby 2.x on/after this version
|
23
24
|
|
24
25
|
if RbConfig::CONFIG['host_os'].downcase =~ /mswin|msys|mingw32/
|
25
26
|
gem 'ffi', '< 1.9.15' # The 'ffi' gem, for Windows, requires Ruby 2.x on/after this version
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Kessler
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-06-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cuke_modeler
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
34
|
+
version: 13.0.0
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "<"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
41
|
+
version: 13.0.0
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,6 +153,8 @@ files:
|
|
153
153
|
- lib/cql/map_reduce.rb
|
154
154
|
- lib/cql/model_dsl.rb
|
155
155
|
- lib/cql/queriable.rb
|
156
|
+
- lib/cql/query.rb
|
157
|
+
- lib/cql/repository.rb
|
156
158
|
- lib/cql/sso_filters.rb
|
157
159
|
- lib/cql/version.rb
|
158
160
|
- testing/cql_test_model.rb
|