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.
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