cucumber-cucumber-expressions 8.3.1
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 +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
|