cucumber-expressions 3.0.0 → 4.0.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 +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
|