cucumber-cucumber-expressions 8.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +5 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
  4. data/.rspec +1 -0
  5. data/.rsync +4 -0
  6. data/.subrepo +1 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +21 -0
  9. data/Makefile +1 -0
  10. data/README.md +5 -0
  11. data/Rakefile +27 -0
  12. data/cucumber-cucumber-expressions.gemspec +33 -0
  13. data/default.mk +70 -0
  14. data/examples.txt +31 -0
  15. data/lib/cucumber/cucumber_expressions/argument.rb +37 -0
  16. data/lib/cucumber/cucumber_expressions/combinatorial_generated_expression_factory.rb +49 -0
  17. data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +119 -0
  18. data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +105 -0
  19. data/lib/cucumber/cucumber_expressions/errors.rb +40 -0
  20. data/lib/cucumber/cucumber_expressions/generated_expression.rb +31 -0
  21. data/lib/cucumber/cucumber_expressions/group.rb +18 -0
  22. data/lib/cucumber/cucumber_expressions/group_builder.rb +42 -0
  23. data/lib/cucumber/cucumber_expressions/parameter_type.rb +81 -0
  24. data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +59 -0
  25. data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +70 -0
  26. data/lib/cucumber/cucumber_expressions/regular_expression.rb +48 -0
  27. data/lib/cucumber/cucumber_expressions/tree_regexp.rb +76 -0
  28. data/scripts/update-gemspec +32 -0
  29. data/spec/capture_warnings.rb +74 -0
  30. data/spec/coverage.rb +7 -0
  31. data/spec/cucumber/cucumber_expressions/argument_spec.rb +17 -0
  32. data/spec/cucumber/cucumber_expressions/combinatorial_generated_expression_factory_test.rb +43 -0
  33. data/spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb +231 -0
  34. data/spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb +57 -0
  35. data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +212 -0
  36. data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +202 -0
  37. data/spec/cucumber/cucumber_expressions/expression_examples_spec.rb +30 -0
  38. data/spec/cucumber/cucumber_expressions/parameter_type_registry_spec.rb +86 -0
  39. data/spec/cucumber/cucumber_expressions/parameter_type_spec.rb +15 -0
  40. data/spec/cucumber/cucumber_expressions/regular_expression_spec.rb +80 -0
  41. data/spec/cucumber/cucumber_expressions/tree_regexp_spec.rb +133 -0
  42. metadata +162 -0
@@ -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'
@@ -0,0 +1,5 @@
1
+ PLEASE DO NOT CREATE ISSUES IN THIS REPO.
2
+ THIS REPO IS A READ-ONLY MIRROR.
3
+
4
+ Create your issue in the Cucumber monorepo instead:
5
+ https://github.com/cucumber/cucumber/issues
@@ -0,0 +1,5 @@
1
+ PLEASE DO NOT CREATE PULL REAUESTS IN THIS REPO.
2
+ THIS REPO IS A READ-ONLY MIRROR.
3
+
4
+ Create your pull request in the Cucumber monorepo instead:
5
+ https://github.com/cucumber/cucumber/pulls
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rsync ADDED
@@ -0,0 +1,4 @@
1
+ ../../LICENSE LICENSE
2
+ ../../.templates/github/ .github/
3
+ ../../.templates/ruby/ .
4
+ ../examples.txt examples.txt
@@ -0,0 +1 @@
1
+ cucumber/cucumber-expressions-ruby
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+ gemspec
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.
@@ -0,0 +1 @@
1
+ include default.mk
@@ -0,0 +1,5 @@
1
+ # Cucumber Expressions for Ruby
2
+
3
+ [![Build Status](https://travis-ci.org/cucumber/cucumber-expressions-ruby.svg?branch=master)](https://travis-ci.org/cucumber/cucumber-expressions-ruby)
4
+
5
+ [The docs are here](https://cucumber.io/docs/cucumber/cucumber-expressions/).
@@ -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
@@ -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
@@ -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