cucumber-expressions 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rsync +2 -0
- data/Makefile +5 -9
- data/cucumber-expressions.gemspec +1 -1
- data/examples.txt +7 -11
- data/lib/cucumber/cucumber_expressions/argument.rb +25 -5
- data/lib/cucumber/cucumber_expressions/combinatorial_generated_expression_factory.rb +40 -0
- data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +8 -26
- data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +36 -19
- data/lib/cucumber/cucumber_expressions/errors.rb +40 -0
- data/lib/cucumber/cucumber_expressions/generated_expression.rb +23 -3
- data/lib/cucumber/cucumber_expressions/group.rb +66 -0
- data/lib/cucumber/cucumber_expressions/parameter_type.rb +38 -5
- data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +4 -4
- data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +34 -54
- data/lib/cucumber/cucumber_expressions/regular_expression.rb +27 -27
- data/spec/cucumber/cucumber_expressions/combinatorial_generated_expression_factory_test.rb +43 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb +10 -2
- data/spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb +4 -11
- data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +42 -43
- data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +109 -94
- data/spec/cucumber/cucumber_expressions/expression_examples_spec.rb +3 -3
- data/spec/cucumber/cucumber_expressions/group_spec.rb +34 -0
- data/spec/cucumber/cucumber_expressions/parameter_type_registry_spec.rb +86 -0
- data/spec/cucumber/cucumber_expressions/regular_expression_spec.rb +17 -25
- metadata +14 -5
- data/lib/cucumber/cucumber_expressions/argument_builder.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f97682c81c4fc810b23bb199a01196191dba087
|
4
|
+
data.tar.gz: 7c29ceea697976f3de194014a9ff039f25478ffe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3a91aeeae161ea22e6114c17ed13b3160eec652de6927cc45241772dd7c8121d620558339db2eb992652da21fa788efd6645636629b259888d23f24acd615f3
|
7
|
+
data.tar.gz: 1f2ee3c5d76189bdf6c52c5bf86a2d485524ecaa2e8207cfe83d91ee7fafb1dda8c9f9d515ce6b8ee3326f59aa3d52a422da38dcf45be9c16e34483904b5cb1d
|
data/.rsync
ADDED
data/Makefile
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
|
2
|
-
build:
|
1
|
+
default:
|
3
2
|
bundle install
|
4
3
|
bundle exec rake
|
4
|
+
bundle exec rake install
|
5
|
+
.PHONY: default
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
bundle install
|
9
|
-
bundle exec rake build release:guard_clean release:rubygem_push
|
10
|
-
version=$$(cat *.gemspec | grep -m 1 ".version *= *" | sed "s/.*= *'\([^']*\)'.*/\1/"); \
|
11
|
-
git tag --annotate v$$version --message "Release $$version"
|
12
|
-
git push --tags
|
7
|
+
clean:
|
8
|
+
.PHONY: clean
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = 'cucumber-expressions'
|
4
|
-
s.version = '
|
4
|
+
s.version = '4.0.0'
|
5
5
|
s.authors = ["Aslak Hellesøy"]
|
6
6
|
s.description = 'Cucumber Expressions - a simpler alternative to Regular Expressions'
|
7
7
|
s.summary = "cucumber-expressions-#{s.version}"
|
data/examples.txt
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
I have {
|
2
|
-
I have 22 cukes
|
3
|
-
[
|
4
|
-
---
|
5
|
-
I have {int} cuke(s) in my {bodypart} now
|
6
|
-
I have 1 cuke in my belly now
|
7
|
-
[1,"belly"]
|
1
|
+
I have {int} cuke(s)
|
2
|
+
I have 22 cukes
|
3
|
+
[22]
|
8
4
|
---
|
9
|
-
I have {int} cuke(s) and some \[]^$.|?*+
|
10
|
-
I have 1 cuke and some \[]^$.|?*+
|
11
|
-
[1
|
5
|
+
I have {int} cuke(s) and some \[]^$.|?*+
|
6
|
+
I have 1 cuke and some \[]^$.|?*+
|
7
|
+
[1]
|
12
8
|
---
|
13
|
-
/I have (\d+) cukes? in my (
|
9
|
+
/I have (\d+) cukes? in my (\w+) now/
|
14
10
|
I have 22 cukes in my belly now
|
15
11
|
[22,"belly"]
|
16
12
|
---
|
@@ -1,14 +1,34 @@
|
|
1
|
+
require 'cucumber/cucumber_expressions/group'
|
2
|
+
require 'cucumber/cucumber_expressions/errors'
|
3
|
+
|
1
4
|
module Cucumber
|
2
5
|
module CucumberExpressions
|
3
6
|
class Argument
|
4
|
-
|
7
|
+
def self.build(regexp, text, parameter_types)
|
8
|
+
m = regexp.match(text)
|
9
|
+
return nil if m.nil?
|
10
|
+
|
11
|
+
match_group = Group.new(m)
|
12
|
+
arg_groups = match_group.children
|
13
|
+
|
14
|
+
if arg_groups.length != parameter_types.length
|
15
|
+
raise CucumberExpressionException.new(
|
16
|
+
"Expression has #{arg_groups.length} arguments, but there were #{parameter_types.length} parameter types"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
parameter_types.zip(arg_groups).map do |parameter_type, arg_group|
|
21
|
+
Argument.new(arg_group, parameter_type)
|
22
|
+
end
|
23
|
+
end
|
5
24
|
|
6
|
-
def initialize(
|
7
|
-
|
25
|
+
def initialize(group, parameter_type)
|
26
|
+
raise "WTF" if Array === group
|
27
|
+
@group, @parameter_type = group, parameter_type
|
8
28
|
end
|
9
29
|
|
10
|
-
def
|
11
|
-
@parameter_type.transform(@
|
30
|
+
def value
|
31
|
+
@parameter_type.transform(@group ? @group.values : nil)
|
12
32
|
end
|
13
33
|
end
|
14
34
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require('cucumber/cucumber_expressions/generated_expression')
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module CucumberExpressions
|
5
|
+
|
6
|
+
class CombinatorialGeneratedExpressionFactory
|
7
|
+
def initialize(expression_template, parameter_type_combinations)
|
8
|
+
@expression_template = expression_template
|
9
|
+
@parameter_type_combinations = parameter_type_combinations
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_expressions
|
13
|
+
generated_expressions = []
|
14
|
+
generate_permutations(generated_expressions, 0, [])
|
15
|
+
generated_expressions
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_permutations(generated_expressions, depth, current_parameter_types)
|
21
|
+
if depth == @parameter_type_combinations.length
|
22
|
+
generated_expression = GeneratedExpression.new(@expression_template, current_parameter_types)
|
23
|
+
generated_expressions.push(generated_expression)
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
(0...@parameter_type_combinations[depth].length).each do |i|
|
28
|
+
new_current_parameter_types = current_parameter_types.dup # clone
|
29
|
+
new_current_parameter_types.push(@parameter_type_combinations[depth][i])
|
30
|
+
generate_permutations(
|
31
|
+
generated_expressions,
|
32
|
+
depth + 1,
|
33
|
+
new_current_parameter_types
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
|
-
require 'cucumber/cucumber_expressions/
|
1
|
+
require 'cucumber/cucumber_expressions/argument'
|
2
2
|
require 'cucumber/cucumber_expressions/parameter_type'
|
3
|
+
require 'cucumber/cucumber_expressions/errors'
|
3
4
|
|
4
5
|
module Cucumber
|
5
6
|
module CucumberExpressions
|
6
7
|
class CucumberExpression
|
7
|
-
PARAMETER_REGEXP = /\{([^}
|
8
|
+
PARAMETER_REGEXP = /\{([^}]+)}/
|
8
9
|
OPTIONAL_REGEXP = /\(([^)]+)\)/
|
9
10
|
ALTERNATIVE_WORD_REGEXP = /([[:alpha:]]+)((\/[[:alpha:]]+)+)/
|
10
11
|
|
11
12
|
attr_reader :source
|
12
13
|
|
13
|
-
def initialize(expression,
|
14
|
+
def initialize(expression, parameter_type_registry)
|
14
15
|
@source = expression
|
15
16
|
@parameter_types = []
|
16
17
|
regexp = "^"
|
17
|
-
type_index = 0
|
18
18
|
match_offset = 0
|
19
19
|
|
20
20
|
# Escape Does not include (){} because they have special meaning
|
@@ -31,28 +31,10 @@ module Cucumber
|
|
31
31
|
match = PARAMETER_REGEXP.match(expression, match_offset)
|
32
32
|
break if match.nil?
|
33
33
|
|
34
|
-
|
35
|
-
parameter_type_name = match[3]
|
36
|
-
if parameter_type_name
|
37
|
-
$stderr.puts("Cucumber expression parameter syntax {#{parameter_name}:#{parameter_type_name}} is deprecated. Please use {#{parameter_type_name}} instead.")
|
38
|
-
end
|
34
|
+
type_name = match[1]
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
parameter_type = nil
|
44
|
-
if type
|
45
|
-
parameter_type = parameter_type_registry.lookup_by_type(type)
|
46
|
-
end
|
47
|
-
if parameter_type.nil? && parameter_type_name
|
48
|
-
parameter_type = parameter_type_registry.lookup_by_name(parameter_type_name)
|
49
|
-
end
|
50
|
-
if parameter_type.nil?
|
51
|
-
parameter_type = parameter_type_registry.lookup_by_name(parameter_name)
|
52
|
-
end
|
53
|
-
if parameter_type.nil?
|
54
|
-
parameter_type = parameter_type_registry.create_anonymous_lookup(lambda {|s| s})
|
55
|
-
end
|
36
|
+
parameter_type = parameter_type_registry.lookup_by_type_name(type_name)
|
37
|
+
raise UndefinedParameterTypeError.new(type_name) if parameter_type.nil?
|
56
38
|
@parameter_types.push(parameter_type)
|
57
39
|
|
58
40
|
text = expression.slice(match_offset...match.offset(0)[0])
|
@@ -67,7 +49,7 @@ module Cucumber
|
|
67
49
|
end
|
68
50
|
|
69
51
|
def match(text)
|
70
|
-
|
52
|
+
Argument.build(@regexp, text, @parameter_types)
|
71
53
|
end
|
72
54
|
|
73
55
|
private
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cucumber/cucumber_expressions/parameter_type_matcher'
|
2
2
|
require 'cucumber/cucumber_expressions/generated_expression'
|
3
|
+
require 'cucumber/cucumber_expressions/combinatorial_generated_expression_factory'
|
3
4
|
|
4
5
|
module Cucumber
|
5
6
|
module CucumberExpressions
|
@@ -9,12 +10,13 @@ module Cucumber
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def generate_expression(text)
|
12
|
-
|
13
|
-
|
14
|
-
parameter_types = []
|
15
|
-
usage_by_name = Hash.new(0)
|
13
|
+
generate_expressions(text)[0]
|
14
|
+
end
|
16
15
|
|
17
|
-
|
16
|
+
def generate_expressions(text)
|
17
|
+
parameter_type_combinations = []
|
18
|
+
parameter_type_matchers = create_parameter_type_matchers(text)
|
19
|
+
expression_template = ""
|
18
20
|
pos = 0
|
19
21
|
|
20
22
|
loop do
|
@@ -29,14 +31,28 @@ module Cucumber
|
|
29
31
|
if matching_parameter_type_matchers.any?
|
30
32
|
matching_parameter_type_matchers = matching_parameter_type_matchers.sort
|
31
33
|
best_parameter_type_matcher = matching_parameter_type_matchers[0]
|
32
|
-
|
33
|
-
|
34
|
+
best_parameter_type_matchers = matching_parameter_type_matchers.select do |m|
|
35
|
+
(m <=> best_parameter_type_matcher).zero?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Build a list of parameter types without duplicates. The reason there
|
39
|
+
# might be duplicates is that some parameter types have more than one regexp,
|
40
|
+
# which means multiple ParameterTypeMatcher objects will have a reference to the
|
41
|
+
# same ParameterType.
|
42
|
+
# We're sorting the list so prefer_for_regexp_match parameter types are listed first.
|
43
|
+
# Users are most likely to want these, so they should be listed at the top.
|
44
|
+
parameter_types = []
|
45
|
+
best_parameter_type_matchers.each do |parameter_type_matcher|
|
46
|
+
unless parameter_types.include?(parameter_type_matcher.parameter_type)
|
47
|
+
parameter_types.push(parameter_type_matcher.parameter_type)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
parameter_types.sort!
|
34
51
|
|
35
|
-
|
36
|
-
parameter_names.push(parameter_name)
|
52
|
+
parameter_type_combinations.push(parameter_types)
|
37
53
|
|
38
|
-
|
39
|
-
|
54
|
+
expression_template += text.slice(pos...best_parameter_type_matcher.start)
|
55
|
+
expression_template += "{%s}"
|
40
56
|
|
41
57
|
pos = best_parameter_type_matcher.start + best_parameter_type_matcher.group.length
|
42
58
|
else
|
@@ -48,21 +64,22 @@ module Cucumber
|
|
48
64
|
end
|
49
65
|
end
|
50
66
|
|
51
|
-
|
52
|
-
|
67
|
+
expression_template += text.slice(pos..-1)
|
68
|
+
|
69
|
+
CombinatorialGeneratedExpressionFactory.new(
|
70
|
+
expression_template,
|
71
|
+
parameter_type_combinations
|
72
|
+
).generate_expressions
|
53
73
|
end
|
54
74
|
|
55
75
|
private
|
56
76
|
|
57
|
-
def get_parameter_name(name, usage_by_name)
|
58
|
-
count = (usage_by_name[name] += 1)
|
59
|
-
count == 1 ? name : "#{name}#{count}"
|
60
|
-
end
|
61
|
-
|
62
77
|
def create_parameter_type_matchers(text)
|
63
78
|
parameter_matchers = []
|
64
79
|
@parameter_type_registry.parameter_types.each do |parameter_type|
|
65
|
-
|
80
|
+
if parameter_type.use_for_snippets?
|
81
|
+
parameter_matchers += create_parameter_type_matchers2(parameter_type, text)
|
82
|
+
end
|
66
83
|
end
|
67
84
|
parameter_matchers
|
68
85
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module CucumberExpressions
|
3
|
+
class CucumberExpressionError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class UndefinedParameterTypeError < CucumberExpressionError
|
7
|
+
def initialize(type_name)
|
8
|
+
super("Undefined parameter type {#{type_name}}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class AmbiguousParameterTypeError < CucumberExpressionError
|
13
|
+
def initialize(parameter_type_regexp, expression_regexp, parameter_types, generated_expressions)
|
14
|
+
super(<<-EOM)
|
15
|
+
Your Regular Expression /#{expression_regexp.source}/
|
16
|
+
matches multiple parameter types with regexp /#{parameter_type_regexp}/:
|
17
|
+
#{parameter_type_names(parameter_types)}
|
18
|
+
|
19
|
+
I couldn't decide which one to use. You have two options:
|
20
|
+
|
21
|
+
1) Use a Cucumber Expression instead of a Regular Expression. Try one of these:
|
22
|
+
#{expressions(generated_expressions)}
|
23
|
+
|
24
|
+
2) Make one of the parameter types prefer_for_regexp_match and continue to use a Regular Expression.
|
25
|
+
|
26
|
+
EOM
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parameter_type_names(parameter_types)
|
32
|
+
parameter_types.map{|p| "{#{p.name}}"}.join("\n ")
|
33
|
+
end
|
34
|
+
|
35
|
+
def expressions(generated_expressions)
|
36
|
+
generated_expressions.map{|ge| ge.source}.join("\n ")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,10 +1,30 @@
|
|
1
1
|
module Cucumber
|
2
2
|
module CucumberExpressions
|
3
3
|
class GeneratedExpression
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :parameter_types
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
6
|
+
def initialize(expression_template, parameters_types)
|
7
|
+
@expression_template, @parameter_types = expression_template, parameters_types
|
8
|
+
end
|
9
|
+
|
10
|
+
def source
|
11
|
+
sprintf(@expression_template, *@parameter_types.map(&:name))
|
12
|
+
end
|
13
|
+
|
14
|
+
def parameter_names
|
15
|
+
usage_by_type_name = Hash.new(0)
|
16
|
+
@parameter_types.map do |t|
|
17
|
+
get_parameter_name(t.name, usage_by_type_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def get_parameter_name(type_name, usage_by_type_name)
|
24
|
+
count = usage_by_type_name[type_name]
|
25
|
+
count += 1
|
26
|
+
usage_by_type_name[type_name] = count
|
27
|
+
count == 1 ? type_name : "#{type_name}#{count}"
|
8
28
|
end
|
9
29
|
end
|
10
30
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module CucumberExpressions
|
3
|
+
class Group
|
4
|
+
attr_reader :children, :start, :end, :value
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@children = []
|
8
|
+
|
9
|
+
if MatchData === args[0]
|
10
|
+
match_data = args[0]
|
11
|
+
parse(match_data)
|
12
|
+
else
|
13
|
+
@start = args[0] || -1
|
14
|
+
@end = args[1] || -1
|
15
|
+
@value = args[2]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def contains?(group)
|
20
|
+
group.null? || (group.start >= @start && group.end <= @end)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(group)
|
24
|
+
@children.push(group)
|
25
|
+
end
|
26
|
+
|
27
|
+
def null?
|
28
|
+
@value.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def values
|
32
|
+
(children.empty? ? [self] : children).map(&:value)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse(match_data)
|
38
|
+
if match_data.length == 1
|
39
|
+
@start = @end = -1
|
40
|
+
@value = nil
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
@start = match_data.offset(0)[0]
|
45
|
+
@end = match_data.offset(0)[1]
|
46
|
+
@value = match_data[0]
|
47
|
+
|
48
|
+
stack = []
|
49
|
+
stack.push(self)
|
50
|
+
|
51
|
+
(1...match_data.length).each do |group_index|
|
52
|
+
group = Group.new(
|
53
|
+
match_data.offset(group_index)[0],
|
54
|
+
match_data.offset(group_index)[1],
|
55
|
+
match_data[group_index]
|
56
|
+
)
|
57
|
+
while !stack.last.contains?(group)
|
58
|
+
stack.pop
|
59
|
+
end
|
60
|
+
stack.last.add(group)
|
61
|
+
stack.push(group)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|