cucumber-cucumber-expressions 8.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE.md +5 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/.rspec +1 -0
- data/.rsync +4 -0
- data/.subrepo +1 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/Makefile +1 -0
- data/README.md +5 -0
- data/Rakefile +27 -0
- data/cucumber-cucumber-expressions.gemspec +33 -0
- data/default.mk +70 -0
- data/examples.txt +31 -0
- data/lib/cucumber/cucumber_expressions/argument.rb +37 -0
- data/lib/cucumber/cucumber_expressions/combinatorial_generated_expression_factory.rb +49 -0
- data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +119 -0
- data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +105 -0
- data/lib/cucumber/cucumber_expressions/errors.rb +40 -0
- data/lib/cucumber/cucumber_expressions/generated_expression.rb +31 -0
- data/lib/cucumber/cucumber_expressions/group.rb +18 -0
- data/lib/cucumber/cucumber_expressions/group_builder.rb +42 -0
- data/lib/cucumber/cucumber_expressions/parameter_type.rb +81 -0
- data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +59 -0
- data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +70 -0
- data/lib/cucumber/cucumber_expressions/regular_expression.rb +48 -0
- data/lib/cucumber/cucumber_expressions/tree_regexp.rb +76 -0
- data/scripts/update-gemspec +32 -0
- data/spec/capture_warnings.rb +74 -0
- data/spec/coverage.rb +7 -0
- data/spec/cucumber/cucumber_expressions/argument_spec.rb +17 -0
- data/spec/cucumber/cucumber_expressions/combinatorial_generated_expression_factory_test.rb +43 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb +231 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb +57 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +212 -0
- data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +202 -0
- data/spec/cucumber/cucumber_expressions/expression_examples_spec.rb +30 -0
- data/spec/cucumber/cucumber_expressions/parameter_type_registry_spec.rb +86 -0
- data/spec/cucumber/cucumber_expressions/parameter_type_spec.rb +15 -0
- data/spec/cucumber/cucumber_expressions/regular_expression_spec.rb +80 -0
- data/spec/cucumber/cucumber_expressions/tree_regexp_spec.rb +133 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d75b7265f9a2c2142d7733b064bb4e6ec6d317d1f32ba9e4148e42512b90a4df
|
4
|
+
data.tar.gz: c7f8fa3ce1e9eccaf40fcbfd9288ed8302ea1b13b2cb8aa385a64abaf94a2d15
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a05e6facfd23519c59b261e253a623097c892108a9b66d3fd330128b9651be4923f01b16666cf61c82abc112c6b5fcee987703b0c4198462148b6a174b51ba24
|
7
|
+
data.tar.gz: '09a81cd8ef765626765d0f269063c284266cf2153d3b80c4cdeacb464aeea41b4df9967ba256a3f8a27bd06f89c2f650cffffc903e4d5206504e05ff1daa2e50'
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rsync
ADDED
data/.subrepo
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
cucumber/cucumber-expressions-ruby
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) Cucumber Ltd
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/Makefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
include default.mk
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
7
|
+
|
8
|
+
Dir['./rake/*.rb'].each do |f|
|
9
|
+
require f
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rspec/core/rake_task"
|
13
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
14
|
+
t.ruby_opts = %w[-r./spec/coverage -w]
|
15
|
+
end
|
16
|
+
|
17
|
+
require_relative 'spec/capture_warnings'
|
18
|
+
include CaptureWarnings
|
19
|
+
namespace :spec do
|
20
|
+
task :warnings do
|
21
|
+
report_warnings do
|
22
|
+
Rake::Task['spec'].invoke
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: ['spec:warnings']
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'cucumber-cucumber-expressions'
|
4
|
+
s.version = '8.3.1'
|
5
|
+
s.authors = ["Aslak Hellesøy"]
|
6
|
+
s.description = 'Cucumber Expressions - a simpler alternative to Regular Expressions'
|
7
|
+
s.summary = "cucumber-expressions-#{s.version}"
|
8
|
+
s.email = 'cukes@googlegroups.com'
|
9
|
+
s.homepage = "https://github.com/cucumber/cucumber-expressions-ruby#readme"
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.license = "MIT"
|
12
|
+
s.required_ruby_version = ">= 2.3"
|
13
|
+
|
14
|
+
s.metadata = {
|
15
|
+
'bug_tracker_uri' => 'https://github.com/cucumber/cucumber/issues',
|
16
|
+
'changelog_uri' => 'https://github.com/cucumber/cucumber/blob/master/cucumber-expressions/CHANGELOG.md',
|
17
|
+
'documentation_uri' => 'https://cucumber.io/docs/cucumber/cucumber-expressions/',
|
18
|
+
'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/cukes',
|
19
|
+
'source_code_uri' => 'https://github.com/cucumber/cucumber/blob/master/cucumber-expressions/ruby',
|
20
|
+
}
|
21
|
+
|
22
|
+
s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.1'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.9', '>= 3.9.0'
|
24
|
+
|
25
|
+
# For coverage reports
|
26
|
+
s.add_development_dependency 'coveralls', '~> 0.8', '>= 0.8.23'
|
27
|
+
|
28
|
+
s.rubygems_version = ">= 1.6.1"
|
29
|
+
s.files = `git ls-files`.split("\n").reject {|path| path =~ /\.gitignore$/ }
|
30
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
31
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
32
|
+
s.require_path = "lib"
|
33
|
+
end
|
data/default.mk
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
SHELL := /usr/bin/env bash
|
2
|
+
RUBY_SOURCE_FILES = $(shell find . -name "*.rb")
|
3
|
+
GEMSPEC = $(shell find . -name "*.gemspec")
|
4
|
+
LIBNAME := $(shell basename $$(dirname $$(pwd)))
|
5
|
+
GEM := cucumber-$(LIBNAME)-$(NEW_VERSION).gem
|
6
|
+
|
7
|
+
default: .tested
|
8
|
+
.PHONY: default
|
9
|
+
|
10
|
+
.deps: Gemfile.lock
|
11
|
+
touch $@
|
12
|
+
|
13
|
+
Gemfile.lock: Gemfile $(GEMSPEC)
|
14
|
+
bundle install
|
15
|
+
touch $@
|
16
|
+
|
17
|
+
.tested: .deps $(RUBY_SOURCE_FILES)
|
18
|
+
bundle exec rspec --color
|
19
|
+
touch $@
|
20
|
+
|
21
|
+
update-dependencies:
|
22
|
+
./scripts/update-gemspec
|
23
|
+
.PHONY: update-dependencies
|
24
|
+
|
25
|
+
ifdef NEW_VERSION
|
26
|
+
ifneq (,$(GEMSPEC))
|
27
|
+
gem: $(GEM)
|
28
|
+
else
|
29
|
+
gem:
|
30
|
+
@echo "Not building gem because there is no gemspec"
|
31
|
+
endif
|
32
|
+
endif
|
33
|
+
.PHONY: gem
|
34
|
+
|
35
|
+
$(GEM): clean .tested
|
36
|
+
gem build $(GEMSPEC)
|
37
|
+
test -s "$(GEM)" || { echo "Gem not built: $(GEM)"; exit 1; }
|
38
|
+
|
39
|
+
pre-release: update-version update-dependencies gem
|
40
|
+
.PHONY: pre-release
|
41
|
+
|
42
|
+
update-version:
|
43
|
+
ifdef NEW_VERSION
|
44
|
+
ifneq (,$(GEMSPEC))
|
45
|
+
sed -i "s/\(s\.version *= *'\)[0-9]*\.[0-9]*\.[0-9]*\('\)/\1$(NEW_VERSION)\2/" $(GEMSPEC)
|
46
|
+
endif
|
47
|
+
else
|
48
|
+
@echo -e "\033[0;31mNEW_VERSION is not defined. Can't update version :-(\033[0m"
|
49
|
+
exit 1
|
50
|
+
endif
|
51
|
+
.PHONY: update-version
|
52
|
+
|
53
|
+
publish: gem
|
54
|
+
ifneq (,$(GEMSPEC))
|
55
|
+
gem push $(GEM)
|
56
|
+
else
|
57
|
+
@echo "Not publishing because there is no gemspec"
|
58
|
+
endif
|
59
|
+
.PHONY: publish
|
60
|
+
|
61
|
+
post-release:
|
62
|
+
@echo "No post-release needed for ruby"
|
63
|
+
.PHONY: post-release
|
64
|
+
|
65
|
+
clean: clean-ruby
|
66
|
+
.PHONY: clean
|
67
|
+
|
68
|
+
clean-ruby:
|
69
|
+
rm -f .deps .linked .tested* Gemfile.lock *.gem
|
70
|
+
.PHONY: clean-ruby
|
data/examples.txt
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
I have {int} cuke(s)
|
2
|
+
I have 22 cukes
|
3
|
+
[22]
|
4
|
+
---
|
5
|
+
I have {int} cuke(s) and some \[]^$.|?*+
|
6
|
+
I have 1 cuke and some \[]^$.|?*+
|
7
|
+
[1]
|
8
|
+
---
|
9
|
+
/I have (\d+) cukes? in my (\w+) now/
|
10
|
+
I have 22 cukes in my belly now
|
11
|
+
[22,"belly"]
|
12
|
+
---
|
13
|
+
/I have (-?\d+) cukes? in my (.*) now/
|
14
|
+
I have 1 cuke in my belly now
|
15
|
+
[1,"belly"]
|
16
|
+
---
|
17
|
+
/^Something( with an optional argument)?$/
|
18
|
+
Something
|
19
|
+
[null]
|
20
|
+
---
|
21
|
+
Привет, {word}!
|
22
|
+
Привет, Мир!
|
23
|
+
["Мир"]
|
24
|
+
---
|
25
|
+
/I have (.*) cukes? in my (.*) now/
|
26
|
+
I have 22 cukes in my belly now
|
27
|
+
["22","belly"]
|
28
|
+
---
|
29
|
+
I have {} cuke(s) in my {} now
|
30
|
+
I have 22 cukes in my belly now
|
31
|
+
["22","belly"]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'cucumber/cucumber_expressions/group'
|
2
|
+
require 'cucumber/cucumber_expressions/errors'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module CucumberExpressions
|
6
|
+
class Argument
|
7
|
+
attr_reader :group, :parameter_type
|
8
|
+
|
9
|
+
def self.build(tree_regexp, text, parameter_types)
|
10
|
+
group = tree_regexp.match(text)
|
11
|
+
return nil if group.nil?
|
12
|
+
|
13
|
+
arg_groups = group.children
|
14
|
+
|
15
|
+
if arg_groups.length != parameter_types.length
|
16
|
+
raise CucumberExpressionError.new(
|
17
|
+
"Expression #{tree_regexp.regexp.inspect} has #{arg_groups.length} capture groups (#{arg_groups.map(&:value)}), but there were #{parameter_types.length} parameter types (#{parameter_types.map(&:name)})"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
parameter_types.zip(arg_groups).map do |parameter_type, arg_group|
|
22
|
+
Argument.new(arg_group, parameter_type)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(group, parameter_type)
|
27
|
+
@group, @parameter_type = group, parameter_type
|
28
|
+
end
|
29
|
+
|
30
|
+
def value(self_obj=:nil)
|
31
|
+
raise "No self_obj" if self_obj == :nil
|
32
|
+
group_values = @group ? @group.values : nil
|
33
|
+
@parameter_type.transform(self_obj, group_values)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
# 256 generated expressions ought to be enough for anybody
|
19
|
+
MAX_EXPRESSIONS = 256
|
20
|
+
|
21
|
+
def generate_permutations(generated_expressions, depth, current_parameter_types)
|
22
|
+
if generated_expressions.length >= MAX_EXPRESSIONS
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if depth == @parameter_type_combinations.length
|
27
|
+
generated_expression = GeneratedExpression.new(@expression_template, current_parameter_types)
|
28
|
+
generated_expressions.push(generated_expression)
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
(0...@parameter_type_combinations[depth].length).each do |i|
|
33
|
+
# Avoid recursion if no elements can be added.
|
34
|
+
if generated_expressions.length >= MAX_EXPRESSIONS
|
35
|
+
return
|
36
|
+
end
|
37
|
+
new_current_parameter_types = current_parameter_types.dup # clone
|
38
|
+
new_current_parameter_types.push(@parameter_type_combinations[depth][i])
|
39
|
+
generate_permutations(
|
40
|
+
generated_expressions,
|
41
|
+
depth + 1,
|
42
|
+
new_current_parameter_types
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'cucumber/cucumber_expressions/argument'
|
2
|
+
require 'cucumber/cucumber_expressions/tree_regexp'
|
3
|
+
require 'cucumber/cucumber_expressions/errors'
|
4
|
+
|
5
|
+
module Cucumber
|
6
|
+
module CucumberExpressions
|
7
|
+
class CucumberExpression
|
8
|
+
# Does not include (){} characters because they have special meaning
|
9
|
+
ESCAPE_REGEXP = /([\\^\[$.|?*+\]])/
|
10
|
+
PARAMETER_REGEXP = /(\\\\)?{([^}]*)}/
|
11
|
+
OPTIONAL_REGEXP = /(\\\\)?\(([^)]+)\)/
|
12
|
+
ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP = /([^\s^\/]+)((\/[^\s^\/]+)+)/
|
13
|
+
DOUBLE_ESCAPE = '\\\\'
|
14
|
+
PARAMETER_TYPES_CANNOT_BE_ALTERNATIVE = 'Parameter types cannot be alternative: '
|
15
|
+
PARAMETER_TYPES_CANNOT_BE_OPTIONAL = 'Parameter types cannot be optional: '
|
16
|
+
|
17
|
+
attr_reader :source
|
18
|
+
|
19
|
+
def initialize(expression, parameter_type_registry)
|
20
|
+
@source = expression
|
21
|
+
@parameter_types = []
|
22
|
+
|
23
|
+
expression = process_escapes(expression)
|
24
|
+
expression = process_optional(expression)
|
25
|
+
expression = process_alternation(expression)
|
26
|
+
expression = process_parameters(expression, parameter_type_registry)
|
27
|
+
expression = "^#{expression}$"
|
28
|
+
|
29
|
+
@tree_regexp = TreeRegexp.new(expression)
|
30
|
+
end
|
31
|
+
|
32
|
+
def match(text)
|
33
|
+
Argument.build(@tree_regexp, text, @parameter_types)
|
34
|
+
end
|
35
|
+
|
36
|
+
def regexp
|
37
|
+
@tree_regexp.regexp
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@source.inspect
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def process_escapes(expression)
|
47
|
+
expression.gsub(ESCAPE_REGEXP, '\\\\\1')
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_optional(expression)
|
51
|
+
# Create non-capturing, optional capture groups from parenthesis
|
52
|
+
expression.gsub(OPTIONAL_REGEXP) do
|
53
|
+
g2 = $2
|
54
|
+
# When using Parameter Types, the () characters are used to represent an optional
|
55
|
+
# item such as (a ) which would be equivalent to (?:a )? in regex
|
56
|
+
#
|
57
|
+
# You cannot have optional Parameter Types i.e. ({int}) as this causes
|
58
|
+
# problems during the conversion phase to regex. So we check for that here
|
59
|
+
#
|
60
|
+
# One exclusion to this rule is if you actually want the brackets i.e. you
|
61
|
+
# want to capture (3) then we still permit this as an individual rule
|
62
|
+
# See: https://github.com/cucumber/cucumber-ruby/issues/1337 for more info
|
63
|
+
# look for double-escaped parentheses
|
64
|
+
if $1 == DOUBLE_ESCAPE
|
65
|
+
"\\(#{g2}\\)"
|
66
|
+
else
|
67
|
+
check_no_parameter_type(g2, PARAMETER_TYPES_CANNOT_BE_OPTIONAL)
|
68
|
+
"(?:#{g2})?"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def process_alternation(expression)
|
74
|
+
expression.gsub(ALTERNATIVE_NON_WHITESPACE_TEXT_REGEXP) do
|
75
|
+
# replace \/ with /
|
76
|
+
# replace / with |
|
77
|
+
replacement = $&.tr('/', '|').gsub(/\\\|/, '/')
|
78
|
+
if replacement.include?('|')
|
79
|
+
replacement.split(/\|/).each do |part|
|
80
|
+
check_no_parameter_type(part, PARAMETER_TYPES_CANNOT_BE_ALTERNATIVE)
|
81
|
+
end
|
82
|
+
"(?:#{replacement})"
|
83
|
+
else
|
84
|
+
replacement
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def process_parameters(expression, parameter_type_registry)
|
90
|
+
# Create non-capturing, optional capture groups from parenthesis
|
91
|
+
expression.gsub(PARAMETER_REGEXP) do
|
92
|
+
if ($1 == DOUBLE_ESCAPE)
|
93
|
+
"\\{#{$2}\\}"
|
94
|
+
else
|
95
|
+
type_name = $2
|
96
|
+
ParameterType.check_parameter_type_name(type_name)
|
97
|
+
parameter_type = parameter_type_registry.lookup_by_type_name(type_name)
|
98
|
+
raise UndefinedParameterTypeError.new(type_name) if parameter_type.nil?
|
99
|
+
@parameter_types.push(parameter_type)
|
100
|
+
|
101
|
+
build_capture_regexp(parameter_type.regexps)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_capture_regexp(regexps)
|
107
|
+
return "(#{regexps[0]})" if regexps.size == 1
|
108
|
+
capture_groups = regexps.map { |group| "(?:#{group})" }
|
109
|
+
"(#{capture_groups.join('|')})"
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_no_parameter_type(s, message)
|
113
|
+
if PARAMETER_REGEXP =~ s
|
114
|
+
raise CucumberExpressionError.new("#{message}#{source}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|