cucumber-expressions 1.0.4 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b8ed8f5d21a95380e884f0c080d076e2e89d17f
4
- data.tar.gz: 7682387d1c85962fb28b6c21683bb48157ef3b49
3
+ metadata.gz: 882d05ea05136cc6af4f3bc4ef6bccfe42531645
4
+ data.tar.gz: 42c7abf6782587c48aa8e264598637a459bbdc86
5
5
  SHA512:
6
- metadata.gz: a66f2c66cc3de2a570ac6cc317f54eb4988bc4d99185b8e9649cff3c26b85670cbea82754cf8ad4483f60351494de83890068e3f970e3c7fa870e83d0e23a709
7
- data.tar.gz: c34c94c1760c2a8a264c338000a0473b0d557fffe253e6bf1793f0e006d106de1ab59f47b9e2ebed1a4565c05b4715c79fddc103e4e2b26156a8b761252e7051
6
+ metadata.gz: 598ce9757f1518d1e9eae317a3f9718efb6339001fa36a5dea1e03fd690f44abd34524652f7b195c271c8b5128082676ffbd2dfef8b2a9aa2ee4bc6cd71ba3c7
7
+ data.tar.gz: 38121b06c1d4ca408a28f407cb0ae3b0f47de3464fe20128de05667ab34fdd93b5f369dbb2d24b26b4f9f742a93744e1dbccbb0d930478c8de3633b305fa8891
@@ -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 = '1.0.4'
4
+ s.version = '2.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
@@ -2,10 +2,14 @@ I have {n} cuke(s) in my {bodypart} now
2
2
  I have 22 cukes in my belly now
3
3
  ["22","belly"]
4
4
  ---
5
- I have {n:int} cuke(s) in my {bodypart} now
5
+ I have {int} cuke(s) in my {bodypart} now
6
6
  I have 1 cuke in my belly now
7
7
  [1,"belly"]
8
8
  ---
9
+ I have {int} cuke(s) and some \[]^$.|?*+ {something}
10
+ I have 1 cuke and some \[]^$.|?*+ characters
11
+ [1,"characters"]
12
+ ---
9
13
  /I have (\d+) cukes? in my (.+) now/
10
14
  I have 22 cukes in my belly now
11
15
  [22,"belly"]
@@ -2,14 +2,14 @@ require 'cucumber/cucumber_expressions/argument'
2
2
 
3
3
  module Cucumber
4
4
  module CucumberExpressions
5
- class ArgumentMatcher
6
- def self.match_arguments(regexp, text, transforms)
5
+ class ArgumentBuilder
6
+ def self.build_arguments(regexp, text, parameters)
7
7
  m = regexp.match(text)
8
8
  return nil if m.nil?
9
9
  (1...m.length).map do |index|
10
10
  value = m[index]
11
- transform = transforms[index-1]
12
- transformed_value = transform.transform(value)
11
+ parameter = parameters[index-1]
12
+ transformed_value = parameter.transform(value)
13
13
  Argument.new(m.offset(index)[0], value, transformed_value)
14
14
  end
15
15
  end
@@ -1,22 +1,25 @@
1
- require 'cucumber/cucumber_expressions/argument_matcher'
2
- require 'cucumber/cucumber_expressions/transform'
1
+ require 'cucumber/cucumber_expressions/argument_builder'
2
+ require 'cucumber/cucumber_expressions/parameter'
3
3
 
4
4
  module Cucumber
5
5
  module CucumberExpressions
6
6
  class CucumberExpression
7
7
  PARAMETER_PATTERN = /\{([^}:]+)(:([^}]+))?}/
8
- OPTIONAL_PATTERN = /\(([^\)]+)\)/
8
+ OPTIONAL_PATTERN = /\(([^)]+)\)/
9
9
 
10
10
  attr_reader :source
11
11
 
12
- def initialize(expression, types, transform_lookup)
12
+ def initialize(expression, types, parameter_registry)
13
13
  @source = expression
14
- @transforms = []
14
+ @parameters = []
15
15
  regexp = "^"
16
16
  type_index = 0
17
17
  match = nil
18
18
  match_offset = 0
19
19
 
20
+ # Does not include (){} because they have special meaning
21
+ expression = expression.gsub(/([\\\^\[$.|?*+\]])/, '\\\\\1')
22
+
20
23
  # Create non-capturing, optional capture groups from parenthesis
21
24
  expression = expression.gsub(OPTIONAL_PATTERN, '(?:\1)?')
22
25
 
@@ -26,26 +29,30 @@ module Cucumber
26
29
 
27
30
  parameter_name = match[1]
28
31
  type_name = match[3]
32
+ if type_name
33
+ $stderr.puts("Cucumber expression parameter syntax {#{parameter_name}:#{type_name}} is deprecated. Please use {#{type_name}} instead.")
34
+ end
35
+
29
36
  type = types.length <= type_index ? nil : types[type_index]
30
37
  type_index += 1
31
38
 
32
- transform = nil
33
- if (type)
34
- transform = transform_lookup.lookup_by_type(type)
39
+ parameter = nil
40
+ if type
41
+ parameter = parameter_registry.lookup_by_type(type)
35
42
  end
36
- if (transform.nil? && type_name)
37
- transform = transform_lookup.lookup_by_type_name(type_name, false)
43
+ if parameter.nil? && type_name
44
+ parameter = parameter_registry.lookup_by_type_name(type_name)
38
45
  end
39
- if (transform.nil?)
40
- transform = transform_lookup.lookup_by_type_name(parameter_name, true)
46
+ if parameter.nil?
47
+ parameter = parameter_registry.lookup_by_type_name(parameter_name)
41
48
  end
42
- if (transform.nil?)
43
- transform = transform_lookup.create_anonymous_lookup(lambda {|s| s})
49
+ if parameter.nil?
50
+ parameter = parameter_registry.create_anonymous_lookup(lambda {|s| s})
44
51
  end
45
- @transforms.push(transform)
52
+ @parameters.push(parameter)
46
53
 
47
54
  text = expression.slice(match_offset...match.offset(0)[0])
48
- capture_regexp = capture_group_regexp(transform.capture_group_regexps)
55
+ capture_regexp = capture_group_regexp(parameter.capture_group_regexps)
49
56
  match_offset = match.offset(0)[1]
50
57
  regexp += text
51
58
  regexp += capture_regexp
@@ -56,7 +63,7 @@ module Cucumber
56
63
  end
57
64
 
58
65
  def match(text)
59
- ArgumentMatcher.match_arguments(@regexp, text, @transforms)
66
+ ArgumentBuilder.build_arguments(@regexp, text, @parameters)
60
67
  end
61
68
 
62
69
  private
@@ -1,46 +1,44 @@
1
- require 'cucumber/cucumber_expressions/transform_matcher'
1
+ require 'cucumber/cucumber_expressions/parameter_matcher'
2
2
  require 'cucumber/cucumber_expressions/generated_expression'
3
3
 
4
4
  module Cucumber
5
5
  module CucumberExpressions
6
6
  class CucumberExpressionGenerator
7
- def initialize(transform_lookup)
8
- @transform_lookup = transform_lookup
7
+ def initialize(parameter_registry)
8
+ @parameter_registry = parameter_registry
9
9
  end
10
10
 
11
- def generate_expression(text, typed)
12
- argumentNames = []
13
- transform_matchers = create_transform_matchers(text)
14
- transforms = []
11
+ def generate_expression(text)
12
+ parameter_names = []
13
+ parameter_matchers = create_parameter_matchers(text)
14
+ parameters = []
15
+ usage_by_type_name = Hash.new(0)
15
16
 
16
17
  expression = ""
17
- arg_counter = 0
18
18
  pos = 0
19
19
 
20
20
  loop do
21
- matching_transform_matchers = []
22
- transform_matchers.each do |transform_matcher|
23
- advanced_transform_matcher = transform_matcher.advance_to(pos)
24
- if advanced_transform_matcher.find
25
- matching_transform_matchers.push(advanced_transform_matcher)
21
+ matching_parameter_matchers = []
22
+ parameter_matchers.each do |parameter_matcher|
23
+ advanced_parameter_matcher = parameter_matcher.advance_to(pos)
24
+ if advanced_parameter_matcher.find
25
+ matching_parameter_matchers.push(advanced_parameter_matcher)
26
26
  end
27
27
  end
28
28
 
29
- if matching_transform_matchers.any?
30
- argumentName = "arg#{arg_counter += 1}"
31
- argumentNames.push(argumentName)
32
- matching_transform_matchers = matching_transform_matchers.sort
33
- best_transform_matcher = matching_transform_matchers[0]
34
- transforms.push(best_transform_matcher.transform)
29
+ if matching_parameter_matchers.any?
30
+ matching_parameter_matchers = matching_parameter_matchers.sort
31
+ best_parameter_matcher = matching_parameter_matchers[0]
32
+ parameter = best_parameter_matcher.parameter
33
+ parameters.push(parameter)
35
34
 
36
- expression += text.slice(pos...best_transform_matcher.start)
37
- expression += "{#{argumentName}"
35
+ parameter_name = get_parameter_name(parameter.type_name, usage_by_type_name)
36
+ parameter_names.push(parameter_name)
38
37
 
39
- if typed
40
- expression += ":#{best_transform_matcher.transform.type_name}"
41
- end
42
- expression += "}"
43
- pos = best_transform_matcher.start + best_transform_matcher.group.length
38
+ expression += text.slice(pos...best_parameter_matcher.start)
39
+ expression += "{#{parameter.type_name}}"
40
+
41
+ pos = best_parameter_matcher.start + best_parameter_matcher.group.length
44
42
  else
45
43
  break
46
44
  end
@@ -51,25 +49,30 @@ module Cucumber
51
49
  end
52
50
 
53
51
  expression += text.slice(pos..-1)
54
- GeneratedExpression.new(expression, argumentNames, transforms)
52
+ GeneratedExpression.new(expression, parameter_names, parameters)
55
53
  end
56
54
 
57
55
  private
58
56
 
59
- def create_transform_matchers(text)
60
- transform_matchers = []
61
- @transform_lookup.transforms.each do |transform|
62
- transform_matchers += create_transform_matchers2(transform, text)
57
+ def get_parameter_name(type_name, usage_by_type_name)
58
+ count = (usage_by_type_name[type_name] += 1)
59
+ count == 1 ? type_name : "#{type_name}#{count}"
60
+ end
61
+
62
+ def create_parameter_matchers(text)
63
+ parameter_matchers = []
64
+ @parameter_registry.parameters.each do |parameter|
65
+ parameter_matchers += create_parameter_matchers2(parameter, text)
63
66
  end
64
- transform_matchers
67
+ parameter_matchers
65
68
  end
66
69
 
67
- def create_transform_matchers2(transform, text)
70
+ def create_parameter_matchers2(parameter, text)
68
71
  result = []
69
- capture_group_regexps = transform.capture_group_regexps
72
+ capture_group_regexps = parameter.capture_group_regexps
70
73
  capture_group_regexps.each do |capture_group_regexp|
71
74
  regexp = Regexp.new("(#{capture_group_regexp})")
72
- result.push(TransformMatcher.new(transform, regexp, text))
75
+ result.push(ParameterMatcher.new(parameter, regexp, text))
73
76
  end
74
77
  result
75
78
  end
@@ -1,10 +1,10 @@
1
1
  module Cucumber
2
2
  module CucumberExpressions
3
3
  class GeneratedExpression
4
- attr_reader :source, :argumentNames, :transforms
4
+ attr_reader :source, :parameter_names, :parameters
5
5
 
6
- def initialize(source, argumentNames, transforms)
7
- @source, @argumentNames, @transforms = source, argumentNames, transforms
6
+ def initialize(source, parameter_names, parameters)
7
+ @source, @parameter_names, @parameters = source, parameter_names, parameters
8
8
  end
9
9
  end
10
10
  end
@@ -1,22 +1,29 @@
1
1
  module Cucumber
2
2
  module CucumberExpressions
3
- class Transform
3
+ class Parameter
4
4
  attr_reader :type_name, :type, :capture_group_regexps
5
5
 
6
- # Create a new Transform
6
+ # Create a new Parameter
7
7
  #
8
8
  # @param type_name [Array] array of class or type name to use in {arg:type_name}
9
9
  # @param capture_group_regexps [Array] list of regexps for capture groups.
10
- # @param transformer lambda that transforms a String to another type
10
+ # @param transformer lambda that transforms a String to (possibly) another type
11
11
  #
12
12
  def initialize(type_name, type, capture_group_regexps, transformer)
13
13
  @type_name, @type, @transformer = type_name, type, transformer
14
- @capture_group_regexps = capture_group_regexps.is_a?(String) ? [capture_group_regexps] : capture_group_regexps
14
+ @capture_group_regexps = string_array(capture_group_regexps)
15
15
  end
16
16
 
17
17
  def transform(value)
18
18
  @transformer.call(value)
19
19
  end
20
+
21
+ private
22
+
23
+ def string_array(capture_group_regexps)
24
+ array = capture_group_regexps.is_a?(Array) ? capture_group_regexps : [capture_group_regexps]
25
+ array.map { |r| r.is_a?(String) ? r : r.source }
26
+ end
20
27
  end
21
28
  end
22
29
  end
@@ -1,15 +1,15 @@
1
1
  module Cucumber
2
2
  module CucumberExpressions
3
- class TransformMatcher
4
- attr_reader :transform
3
+ class ParameterMatcher
4
+ attr_reader :parameter
5
5
 
6
- def initialize(transform, regexp, text, match_position=0)
7
- @transform, @regexp, @text = transform, regexp, text
6
+ def initialize(parameter, regexp, text, match_position=0)
7
+ @parameter, @regexp, @text = parameter, regexp, text
8
8
  @match = @regexp.match(@text, match_position)
9
9
  end
10
10
 
11
11
  def advance_to(new_match_position)
12
- self.class.new(@transform, @regexp, @text, new_match_position)
12
+ self.class.new(parameter, @regexp, @text, new_match_position)
13
13
  end
14
14
 
15
15
  def find
@@ -0,0 +1,64 @@
1
+ require 'cucumber/cucumber_expressions/parameter'
2
+
3
+ module Cucumber
4
+ module CucumberExpressions
5
+ class ParameterRegistry
6
+ INTEGER_REGEXPS = ['-?\d+', '\d+']
7
+ FLOATING_POINT_REGEXPS = ['-?\d*\.?\d+']
8
+
9
+ def initialize
10
+ @parameters_by_type_name = {}
11
+ @parameters_by_capture_group_regexp = {}
12
+ @parameters_by_class = {}
13
+
14
+ add_parameter(Parameter.new('int', Integer, INTEGER_REGEXPS, lambda {|s| s.to_i}))
15
+ add_parameter(Parameter.new('float', Float, FLOATING_POINT_REGEXPS, lambda {|s| s.to_f}))
16
+ end
17
+
18
+ def lookup_by_type(type)
19
+ if type.is_a?(Class)
20
+ lookup_by_class(type)
21
+ elsif type.is_a?(String)
22
+ lookup_by_type_name(type)
23
+ else
24
+ raise Exception.new("Type must be string or class, but was #{type} of type #{type.class}")
25
+ end
26
+ end
27
+
28
+ def lookup_by_class(clazz)
29
+ parameter = @parameters_by_class[clazz]
30
+ if parameter.nil?
31
+ create_anonymous_lookup(lambda {|s| clazz.new(s)})
32
+ else
33
+ parameter
34
+ end
35
+ end
36
+
37
+ def lookup_by_type_name(type_name)
38
+ @parameters_by_type_name[type_name]
39
+ end
40
+
41
+ def lookup_by_capture_group_regexp(capture_group_regexp)
42
+ @parameters_by_capture_group_regexp[capture_group_regexp]
43
+ end
44
+
45
+ def create_anonymous_lookup(proc)
46
+ Parameter.new(nil, nil, ['.+'], proc)
47
+ end
48
+
49
+ def add_parameter(parameter)
50
+ @parameters_by_type_name[parameter.type_name] = parameter
51
+ @parameters_by_class[parameter.type] = parameter
52
+
53
+ parameter.capture_group_regexps.each do |capture_group_regexp|
54
+ @parameters_by_capture_group_regexp[capture_group_regexp] = parameter
55
+ end
56
+ end
57
+
58
+ def parameters
59
+ @parameters_by_type_name.values
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -1,13 +1,13 @@
1
- require 'cucumber/cucumber_expressions/argument_matcher'
1
+ require 'cucumber/cucumber_expressions/argument_builder'
2
2
 
3
3
  module Cucumber
4
4
  module CucumberExpressions
5
5
  class RegularExpression
6
6
  CAPTURE_GROUP_PATTERN = /\(([^(]+)\)/
7
7
 
8
- def initialize(regexp, types, transform_lookup)
8
+ def initialize(regexp, types, parameter_registry)
9
9
  @regexp = regexp
10
- @transforms = []
10
+ @parameters = []
11
11
 
12
12
  type_index = 0
13
13
  match = nil
@@ -21,24 +21,24 @@ module Cucumber
21
21
  type = types.length <= type_index ? nil : types[type_index]
22
22
  type_index += 1
23
23
 
24
- transform = nil
24
+ parameter = nil
25
25
  if (type)
26
- transform = transform_lookup.lookup_by_type(type)
26
+ parameter = parameter_registry.lookup_by_type(type)
27
27
  end
28
- if (transform.nil?)
29
- transform = transform_lookup.lookup_by_capture_group_regexp(capture_group_pattern)
28
+ if (parameter.nil?)
29
+ parameter = parameter_registry.lookup_by_capture_group_regexp(capture_group_pattern)
30
30
  end
31
- if (transform.nil?)
32
- transform = transform_lookup.create_anonymous_lookup(lambda {|s| s})
31
+ if (parameter.nil?)
32
+ parameter = parameter_registry.create_anonymous_lookup(lambda {|s| s})
33
33
  end
34
34
 
35
- @transforms.push(transform)
35
+ @parameters.push(parameter)
36
36
  match_offset = match.offset(0)[1]
37
37
  end
38
38
  end
39
39
 
40
40
  def match(text)
41
- ArgumentMatcher.match_arguments(@regexp, text, @transforms)
41
+ ArgumentBuilder.build_arguments(@regexp, text, @parameters)
42
42
  end
43
43
 
44
44
  def source
@@ -1,6 +1,6 @@
1
1
  require 'cucumber/cucumber_expressions/cucumber_expression_generator'
2
- require 'cucumber/cucumber_expressions/transform'
3
- require 'cucumber/cucumber_expressions/transform_lookup'
2
+ require 'cucumber/cucumber_expressions/parameter'
3
+ require 'cucumber/cucumber_expressions/parameter_registry'
4
4
 
5
5
  module Cucumber
6
6
  module CucumberExpressions
@@ -9,69 +9,66 @@ module Cucumber
9
9
  end
10
10
 
11
11
  before do
12
- @transform_lookup = TransformLookup.new
13
- @generator = CucumberExpressionGenerator.new(@transform_lookup)
12
+ @parameter_registry = ParameterRegistry.new
13
+ @generator = CucumberExpressionGenerator.new(@parameter_registry)
14
14
  end
15
15
 
16
16
  it "documents expression generation" do
17
- transform_lookup = TransformLookup.new
17
+ parameter_registry = ParameterRegistry.new
18
18
  ### [generate-expression]
19
- generator = CucumberExpressionGenerator.new(transform_lookup)
19
+ generator = CucumberExpressionGenerator.new(parameter_registry)
20
20
  undefined_step_text = "I have 2 cucumbers and 1.5 tomato"
21
- generated_expression = generator.generate_expression(undefined_step_text, true)
22
- expect(generated_expression.source).to eq("I have {arg1:int} cucumbers and {arg2:float} tomato")
23
- expect(generated_expression.argumentNames[0]).to eq("arg1")
24
- expect(generated_expression.transforms[1].type).to eq(Float)
21
+ generated_expression = generator.generate_expression(undefined_step_text)
22
+ expect(generated_expression.source).to eq("I have {int} cucumbers and {float} tomato")
23
+ expect(generated_expression.parameters[1].type).to eq(Float)
25
24
  ### [generate-expression]
26
25
  end
27
26
 
28
27
  it "generates expression for no args" do
29
- assert_typed_expression("hello", "hello")
28
+ assert_expression("hello", [], "hello")
30
29
  end
31
30
 
32
- it "generates expression for int double arg" do
33
- assert_typed_expression(
34
- "I have {arg1:int} cukes and {arg2:float} euro",
31
+ it "generates expression for int float arg" do
32
+ assert_expression(
33
+ "I have {int} cukes and {float} euro", ["int", "float"],
35
34
  "I have 2 cukes and 1.5 euro")
36
35
  end
37
36
 
38
37
  it "generates expression for just int" do
39
- assert_typed_expression(
40
- "{arg1:int}",
38
+ assert_expression(
39
+ "{int}", ["int"],
41
40
  "99999")
42
41
  end
43
42
 
44
- it "generates expression without expression type" do
45
- assert_untyped_expression(
46
- "I have {arg1} cukes and {arg2} euro",
47
- "I have 2 cukes and 1.5 euro")
43
+ it "numbers only second argument when builtin type is not reserved keyword" do
44
+ assert_expression(
45
+ "I have {int} cukes and {int} euro", ["int", "int2"],
46
+ "I have 2 cukes and 5 euro")
48
47
  end
49
48
 
50
- it "generates expression for custom type" do
51
- @transform_lookup.add_transform(Transform.new(
49
+ it "numbers only second argument when type is not reserved keyword" do
50
+ @parameter_registry.add_parameter(Parameter.new(
52
51
  'currency',
53
52
  Currency,
54
53
  '[A-Z]{3}',
55
54
  nil
56
55
  ))
57
56
 
58
- assert_typed_expression(
59
- "I have a {arg1:currency} account",
60
- "I have a EUR account")
57
+ assert_expression(
58
+ "I have a {currency} account and a {currency} account", ["currency", "currency2"],
59
+ "I have a EUR account and a GBP account")
61
60
  end
62
61
 
63
- it "exposes transforms in generated expression" do
64
- expression = @generator.generate_expression("I have 2 cukes and 1.5 euro", true)
65
- types = expression.transforms.map(&:type)
62
+ it "exposes parameters in generated expression" do
63
+ expression = @generator.generate_expression("I have 2 cukes and 1.5 euro")
64
+ types = expression.parameters.map(&:type)
66
65
  expect(types).to eq([Integer, Float])
67
66
  end
68
67
 
69
- def assert_typed_expression(expected, text)
70
- expect(@generator.generate_expression(text, true).source).to eq(expected)
71
- end
72
-
73
- def assert_untyped_expression(expected, text)
74
- expect(@generator.generate_expression(text, false).source).to eq(expected)
68
+ def assert_expression(expected_expression, expected_argument_names, text)
69
+ generated_expression = @generator.generate_expression(text)
70
+ expect(generated_expression.parameter_names).to eq(expected_argument_names)
71
+ expect(generated_expression.source).to eq(expected_expression)
75
72
  end
76
73
  end
77
74
  end
@@ -1,12 +1,12 @@
1
1
  require 'cucumber/cucumber_expressions/cucumber_expression'
2
- require 'cucumber/cucumber_expressions/transform_lookup'
2
+ require 'cucumber/cucumber_expressions/parameter_registry'
3
3
 
4
4
  module Cucumber
5
5
  module CucumberExpressions
6
6
  describe CucumberExpression do
7
7
  context "Regexp translation" do
8
8
  def assert_regexp(expression, regexp)
9
- cucumber_expression = CucumberExpression.new(expression, [], TransformLookup.new)
9
+ cucumber_expression = CucumberExpression.new(expression, [], ParameterRegistry.new)
10
10
  expect(regexp).to eq(cucumber_expression.instance_variable_get('@regexp'))
11
11
  end
12
12
 
@@ -26,7 +26,7 @@ module Cucumber
26
26
 
27
27
  it "translates three typed arguments" do
28
28
  assert_regexp(
29
- "I have {n:float} cukes in my {bodypart} at {time:int} o'clock",
29
+ "I have {float} cukes in my {bodypart} at {int} o'clock",
30
30
  /^I have (-?\d*\.?\d+) cukes in my (.+) at ((?:-?\d+)|(?:\d+)) o'clock$/
31
31
  )
32
32
  end
@@ -1,28 +1,28 @@
1
1
  require 'cucumber/cucumber_expressions/cucumber_expression'
2
- require 'cucumber/cucumber_expressions/transform_lookup'
2
+ require 'cucumber/cucumber_expressions/parameter_registry'
3
3
 
4
4
  module Cucumber
5
5
  module CucumberExpressions
6
6
  describe CucumberExpression do
7
7
  it "documents match arguments" do
8
- transform_lookup = TransformLookup.new
8
+ parameter_registry = ParameterRegistry.new
9
9
 
10
10
  ### [capture-match-arguments]
11
11
  expr = "I have {n} cuke(s) in my {bodypart} now"
12
12
  types = ['int', nil]
13
- expression = CucumberExpression.new(expr, types, transform_lookup)
13
+ expression = CucumberExpression.new(expr, types, parameter_registry)
14
14
  args = expression.match("I have 7 cukes in my belly now")
15
15
  expect( args[0].transformed_value ).to eq(7)
16
16
  expect( args[1].transformed_value ).to eq("belly")
17
17
  ### [capture-match-arguments]
18
18
  end
19
19
 
20
- it "transforms nothing by default" do
20
+ it "does no transform by default" do
21
21
  expect( match("{what}", "22") ).to eq(["22"])
22
22
  end
23
23
 
24
- it "transforms to int by expression type" do
25
- expect( match("{what:int}", "22") ).to eq([22])
24
+ it "transforms to int by parameter type" do
25
+ expect( match("{int}", "22") ).to eq([22])
26
26
  end
27
27
 
28
28
  it "transforms to int by explicit type" do
@@ -30,17 +30,17 @@ module Cucumber
30
30
  end
31
31
 
32
32
  # Ruby-specific
33
- it "transforms to Integer by explicit type" do
33
+ it "parameters to Integer by explicit type" do
34
34
  expect( match("{what}", "22", [Integer]) ).to eq([22])
35
35
  end
36
36
 
37
- it "doesn't match a float to an int" do
38
- expect( match("{what:int}", "1.22") ).to be_nil
37
+ it "doesn't match a float with an int parameter" do
38
+ expect( match("{int}", "1.22") ).to be_nil
39
39
  end
40
40
 
41
- it "transforms to float by expression type" do
42
- expect( match("{what:float}", "0.22") ).to eq([0.22])
43
- expect( match("{what:float}", ".22") ).to eq([0.22])
41
+ it "transforms to float by parameter type" do
42
+ expect( match("{float}", "0.22") ).to eq([0.22])
43
+ expect( match("{float}", ".22") ).to eq([0.22])
44
44
  end
45
45
 
46
46
  it "transforms to float by explicit type" do
@@ -48,26 +48,41 @@ module Cucumber
48
48
  expect( match("{what}", ".22", ['float']) ).to eq([0.22])
49
49
  end
50
50
 
51
- it "doesn't transform unknown type" do
52
- expect { match("{what:unknown}", "something") }.to raise_error(
53
- 'No transform for type name "unknown"')
51
+ it "leaves unknown type untransformed" do
52
+ expect( match("{unknown}", "something") ).to eq(["something"])
53
+ end
54
+
55
+ it "supports deprecated {name:type} syntax for now" do
56
+ expect( match("{param:unknown}", "something") ).to eq(["something"])
54
57
  end
55
58
 
56
59
  it "exposes source" do
57
- expr = "I have {n:int} cuke(s) in my {bodypart} now"
58
- expect(CucumberExpression.new(expr, [], TransformLookup.new).source).to eq(expr)
60
+ expr = "I have {int} cuke(s) in my {bodypart} now"
61
+ expect(CucumberExpression.new(expr, [], ParameterRegistry.new).source).to eq(expr)
59
62
  end
60
63
 
61
64
  it "exposes offset and value" do
62
- expr = "I have {n:int} cuke(s) in my {bodypart} now"
63
- expression = CucumberExpression.new(expr, [], TransformLookup.new)
65
+ expr = "I have {int} cuke(s) in my {bodypart} now"
66
+ expression = CucumberExpression.new(expr, [], ParameterRegistry.new)
64
67
  arg1 = expression.match("I have 800 cukes in my brain now")[0]
65
68
  expect(arg1.offset).to eq(7)
66
69
  expect(arg1.value).to eq("800")
67
70
  end
68
71
 
72
+ describe "RegExp special characters" do
73
+ %w(\\ [ ] ^ $ . | ? * +).each do |character|
74
+ it "escapes #{character}" do
75
+ expr = "I have {int} cuke(s) and #{character}"
76
+ expression = CucumberExpression.new(expr, [], ParameterRegistry.new)
77
+ arg1 = expression.match("I have 800 cukes and #{character}")[0]
78
+ expect(arg1.offset).to eq(7)
79
+ expect(arg1.value).to eq("800")
80
+ end
81
+ end
82
+ end
83
+
69
84
  def match(expression, text, types = [])
70
- cucumber_expression = CucumberExpression.new(expression, types, TransformLookup.new)
85
+ cucumber_expression = CucumberExpression.new(expression, types, ParameterRegistry.new)
71
86
  args = cucumber_expression.match(text)
72
87
  return nil if args.nil?
73
88
  args.map { |arg| arg.transformed_value }
@@ -0,0 +1,87 @@
1
+ require 'cucumber/cucumber_expressions/cucumber_expression'
2
+ require 'cucumber/cucumber_expressions/regular_expression'
3
+ require 'cucumber/cucumber_expressions/parameter_registry'
4
+
5
+ module Cucumber
6
+ module CucumberExpressions
7
+ class Color
8
+ attr_reader :name
9
+
10
+ ### [color-constructor]
11
+ def initialize(name)
12
+ @name = name
13
+ end
14
+ ### [color-constructor]
15
+
16
+ def ==(other)
17
+ other.is_a?(Color) && other.name == name
18
+ end
19
+ end
20
+
21
+ describe "Custom parameter" do
22
+ before do
23
+ @parameter_registry = ParameterRegistry.new
24
+ ### [add-color-parameter]
25
+ @parameter_registry.add_parameter(Parameter.new(
26
+ 'color',
27
+ Color,
28
+ [/red|blue|yellow/, /(?:dark|light) (?:red|blue|yellow)/],
29
+ lambda { |s| Color.new(s) }
30
+ ))
31
+ ### [add-color-parameter]
32
+ end
33
+
34
+ describe CucumberExpression do
35
+ it "matches typed parameters" do
36
+ expression = CucumberExpression.new("I have a {color} ball", [], @parameter_registry)
37
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
38
+ expect( parametered_argument_value ).to eq(Color.new('red'))
39
+ end
40
+
41
+ it "matches typed parameters with optional group" do
42
+ expression = CucumberExpression.new("I have a {color} ball", [], @parameter_registry)
43
+ parametered_argument_value = expression.match("I have a dark red ball")[0].transformed_value
44
+ expect( parametered_argument_value ).to eq(Color.new('dark red'))
45
+ end
46
+
47
+ it "matches untyped parameters with explicit type" do
48
+ expression = CucumberExpression.new("I have a {color} ball", [Color], @parameter_registry)
49
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
50
+ expect( parametered_argument_value ).to eq(Color.new('red'))
51
+ end
52
+
53
+ it "matches untyped parameters with same name as type" do
54
+ expression = CucumberExpression.new("I have a {color} ball", [], @parameter_registry)
55
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
56
+ expect( parametered_argument_value ).to eq(Color.new('red'))
57
+ end
58
+
59
+ it "matches parameters with explicit type that isn't registered" do
60
+ expression = CucumberExpression.new("I have a {color} ball", [Color], ParameterRegistry.new)
61
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
62
+ expect( parametered_argument_value ).to eq(Color.new('red'))
63
+ end
64
+ end
65
+
66
+ describe RegularExpression do
67
+ it "matches parameters with explicit constructor" do
68
+ expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, [Color], @parameter_registry)
69
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
70
+ expect( parametered_argument_value ).to eq(Color.new('red'))
71
+ end
72
+
73
+ it "matches parameters without explicit constructor" do
74
+ expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, [], @parameter_registry)
75
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
76
+ expect( parametered_argument_value ).to eq(Color.new('red'))
77
+ end
78
+
79
+ it "matches parameters with explicit type that isn't registered" do
80
+ expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, [Color], ParameterRegistry.new)
81
+ parametered_argument_value = expression.match("I have a red ball")[0].transformed_value
82
+ expect( parametered_argument_value ).to eq(Color.new('red'))
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,6 +1,6 @@
1
1
  require 'cucumber/cucumber_expressions/cucumber_expression'
2
2
  require 'cucumber/cucumber_expressions/regular_expression'
3
- require 'cucumber/cucumber_expressions/transform_lookup'
3
+ require 'cucumber/cucumber_expressions/parameter_registry'
4
4
  require 'json'
5
5
 
6
6
  module Cucumber
@@ -8,8 +8,8 @@ module Cucumber
8
8
  describe 'examples.txt' do
9
9
  def match(expression_text, text)
10
10
  expression = expression_text =~ /\/(.*)\// ?
11
- RegularExpression.new(Regexp.new($1), [], TransformLookup.new) :
12
- CucumberExpression.new(expression_text, [], TransformLookup.new)
11
+ RegularExpression.new(Regexp.new($1), [], ParameterRegistry.new) :
12
+ CucumberExpression.new(expression_text, [], ParameterRegistry.new)
13
13
 
14
14
  arguments = expression.match(text)
15
15
  return nil if arguments.nil?
@@ -1,47 +1,47 @@
1
1
  require 'cucumber/cucumber_expressions/regular_expression'
2
- require 'cucumber/cucumber_expressions/transform_lookup'
2
+ require 'cucumber/cucumber_expressions/parameter_registry'
3
3
 
4
4
  module Cucumber
5
5
  module CucumberExpressions
6
6
  describe RegularExpression do
7
7
  it "documents match arguments" do
8
- transform_lookup = TransformLookup.new
8
+ parameter_registry = ParameterRegistry.new
9
9
 
10
10
  ### [capture-match-arguments]
11
11
  expr = /I have (\d+) cukes? in my (\w*) now/
12
12
  types = ['int', nil]
13
- expression = RegularExpression.new(expr, types, transform_lookup)
13
+ expression = RegularExpression.new(expr, types, parameter_registry)
14
14
  args = expression.match("I have 7 cukes in my belly now")
15
15
  expect( args[0].transformed_value ).to eq(7)
16
16
  expect( args[1].transformed_value ).to eq("belly")
17
17
  ### [capture-match-arguments]
18
18
  end
19
19
 
20
- it "transforms to string by default" do
20
+ it "does no transform by default" do
21
21
  expect( match(/(\d\d)/, "22") ).to eq(["22"])
22
22
  end
23
23
 
24
- it "transforms integer to double using explicit type name" do
24
+ it "transforms int to float by explicit type name" do
25
25
  expect( match(/(.*)/, "22", ['float']) ).to eq([22.0])
26
26
  end
27
27
 
28
- it "transforms integer to double using explicit type" do
28
+ it "transforms int to float by explicit function" do
29
29
  expect( match(/(.*)/, "22", [Float]) ).to eq([22.0])
30
30
  end
31
31
 
32
- it "transforms to int using capture group pattern" do
32
+ it "transforms int by parameter pattern" do
33
33
  expect( match(/(-?\d+)/, "22") ).to eq([22])
34
34
  end
35
35
 
36
- it "transforms to int by alternate capture group pattern" do
36
+ it "transforms int by alternate parameter pattern" do
37
37
  expect( match(/(\d+)/, "22") ).to eq([22])
38
38
  end
39
39
 
40
- it "transforms double without integer value" do
40
+ it "transforms float without integer part" do
41
41
  expect( match(/(.*)/, ".22", ['float']) ).to eq([0.22])
42
42
  end
43
43
 
44
- it "transforms double with sign" do
44
+ it "transforms float with sign" do
45
45
  expect( match(/(.*)/, "-1.22", ['float']) ).to eq([-1.22])
46
46
  end
47
47
 
@@ -57,11 +57,11 @@ module Cucumber
57
57
 
58
58
  it "exposes source" do
59
59
  expr = /I have (\d+) cukes? in my (\+) now/
60
- expect(RegularExpression.new(expr, [], TransformLookup.new).source).to eq(expr)
60
+ expect(RegularExpression.new(expr, [], ParameterRegistry.new).source).to eq(expr)
61
61
  end
62
62
 
63
63
  def match(expression, text, types = [])
64
- regular_expression = RegularExpression.new(expression, types, TransformLookup.new)
64
+ regular_expression = RegularExpression.new(expression, types, ParameterRegistry.new)
65
65
  arguments = regular_expression.match(text)
66
66
  return nil if arguments.nil?
67
67
  arguments.map { |arg| arg.transformed_value }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber-expressions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aslak Hellesøy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-20 00:00:00.000000000 Z
11
+ date: 2017-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -82,20 +82,20 @@ files:
82
82
  - cucumber-expressions.gemspec
83
83
  - examples.txt
84
84
  - lib/cucumber/cucumber_expressions/argument.rb
85
- - lib/cucumber/cucumber_expressions/argument_matcher.rb
85
+ - lib/cucumber/cucumber_expressions/argument_builder.rb
86
86
  - lib/cucumber/cucumber_expressions/cucumber_expression.rb
87
87
  - lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb
88
88
  - lib/cucumber/cucumber_expressions/generated_expression.rb
89
+ - lib/cucumber/cucumber_expressions/parameter.rb
90
+ - lib/cucumber/cucumber_expressions/parameter_matcher.rb
91
+ - lib/cucumber/cucumber_expressions/parameter_registry.rb
89
92
  - lib/cucumber/cucumber_expressions/regular_expression.rb
90
- - lib/cucumber/cucumber_expressions/transform.rb
91
- - lib/cucumber/cucumber_expressions/transform_lookup.rb
92
- - lib/cucumber/cucumber_expressions/transform_matcher.rb
93
93
  - spec/capture_warnings.rb
94
94
  - spec/coverage.rb
95
95
  - spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb
96
96
  - spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb
97
97
  - spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb
98
- - spec/cucumber/cucumber_expressions/custom_transform_spec.rb
98
+ - spec/cucumber/cucumber_expressions/custom_parameter_spec.rb
99
99
  - spec/cucumber/cucumber_expressions/expression_examples_spec.rb
100
100
  - spec/cucumber/cucumber_expressions/regular_expression_spec.rb
101
101
  homepage: https://github.com/cucumber/cucumber-expressions-ruby#readme
@@ -119,16 +119,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  version: '0'
120
120
  requirements: []
121
121
  rubyforge_project:
122
- rubygems_version: 2.4.5.2
122
+ rubygems_version: 2.5.2
123
123
  signing_key:
124
124
  specification_version: 4
125
- summary: cucumber-expressions-1.0.4
125
+ summary: cucumber-expressions-2.0.0
126
126
  test_files:
127
127
  - spec/capture_warnings.rb
128
128
  - spec/coverage.rb
129
129
  - spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb
130
130
  - spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb
131
131
  - spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb
132
- - spec/cucumber/cucumber_expressions/custom_transform_spec.rb
132
+ - spec/cucumber/cucumber_expressions/custom_parameter_spec.rb
133
133
  - spec/cucumber/cucumber_expressions/expression_examples_spec.rb
134
134
  - spec/cucumber/cucumber_expressions/regular_expression_spec.rb
@@ -1,70 +0,0 @@
1
- require 'cucumber/cucumber_expressions/transform'
2
-
3
- module Cucumber
4
- module CucumberExpressions
5
- class TransformLookup
6
- INTEGER_REGEXPS = ['-?\d+', '\d+']
7
- FLOATING_POINT_REGEXPS = ['-?\d*\.?\d+']
8
-
9
- def initialize
10
- @transforms_by_type_name = {}
11
- @transforms_by_capture_group_regexp = {}
12
- @transforms_by_class = {}
13
-
14
- add_transform(Transform.new('int', Integer, INTEGER_REGEXPS, lambda {|s| s.to_i}))
15
- add_transform(Transform.new('float', Float, FLOATING_POINT_REGEXPS, lambda {|s| s.to_f}))
16
- end
17
-
18
- def lookup_by_type(type)
19
- if type.is_a?(Class)
20
- lookup_by_class(type)
21
- elsif type.is_a?(String)
22
- lookup_by_type_name(type, false)
23
- else
24
- raise Exception.new("Type must be string or class, but was #{type} of type #{type.class}")
25
- end
26
- end
27
-
28
- def lookup_by_class(clazz)
29
- transform = @transforms_by_class[clazz]
30
- if transform.nil?
31
- create_anonymous_lookup(lambda {|s| clazz.new(s)})
32
- else
33
- transform
34
- end
35
- end
36
-
37
- def lookup_by_type_name(type_name, ignore_unknown_type_name)
38
- transform = @transforms_by_type_name[type_name]
39
- if transform.nil?
40
- return nil if ignore_unknown_type_name
41
- raise Exception.new("No transform for type name \"#{type_name}\"")
42
- else
43
- transform
44
- end
45
- end
46
-
47
- def lookup_by_capture_group_regexp(capture_group_regexp)
48
- @transforms_by_capture_group_regexp[capture_group_regexp]
49
- end
50
-
51
- def create_anonymous_lookup(proc)
52
- Transform.new(nil, nil, ['.+'], proc)
53
- end
54
-
55
- def add_transform(transform)
56
- @transforms_by_type_name[transform.type_name] = transform
57
- @transforms_by_class[transform.type] = transform
58
-
59
- transform.capture_group_regexps.each do |capture_group_regexp|
60
- @transforms_by_capture_group_regexp[capture_group_regexp] = transform
61
- end
62
- end
63
-
64
- def transforms
65
- @transforms_by_type_name.values
66
- end
67
-
68
- end
69
- end
70
- end
@@ -1,87 +0,0 @@
1
- require 'cucumber/cucumber_expressions/cucumber_expression'
2
- require 'cucumber/cucumber_expressions/regular_expression'
3
- require 'cucumber/cucumber_expressions/transform_lookup'
4
-
5
- module Cucumber
6
- module CucumberExpressions
7
- class Color
8
- attr_reader :name
9
-
10
- ### [color-constructor]
11
- def initialize(name)
12
- @name = name
13
- end
14
- ### [color-constructor]
15
-
16
- def ==(other)
17
- other.is_a?(Color) && other.name == name
18
- end
19
- end
20
-
21
- describe "Custom transform" do
22
- before do
23
- @transform_lookup = TransformLookup.new
24
- ### [add-color-transform]
25
- @transform_lookup.add_transform(Transform.new(
26
- 'color',
27
- Color,
28
- ['red|blue|yellow', '(?:dark|light) (?:red|blue|yellow)'],
29
- lambda { |s| Color.new(s) }
30
- ))
31
- ### [add-color-transform]
32
- end
33
-
34
- describe CucumberExpression do
35
- it "transforms arguments with expression type I" do
36
- expression = CucumberExpression.new("I have a {color:color} ball", [], @transform_lookup)
37
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
38
- expect( transformed_argument_value ).to eq(Color.new('red'))
39
- end
40
-
41
- it "transforms arguments with expression type II" do
42
- expression = CucumberExpression.new("I have a {color:color} ball", [], @transform_lookup)
43
- transformed_argument_value = expression.match("I have a dark red ball")[0].transformed_value
44
- expect( transformed_argument_value ).to eq(Color.new('dark red'))
45
- end
46
-
47
- it "transforms arguments with explicit type" do
48
- expression = CucumberExpression.new("I have a {color} ball", [Color], @transform_lookup)
49
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
50
- expect( transformed_argument_value ).to eq(Color.new('red'))
51
- end
52
-
53
- it "transforms arguments using argument name as type" do
54
- expression = CucumberExpression.new("I have a {color} ball", [], @transform_lookup)
55
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
56
- expect( transformed_argument_value ).to eq(Color.new('red'))
57
- end
58
-
59
- it "transforms arguments with explicit type using constructor directly" do
60
- expression = CucumberExpression.new("I have a {color} ball", [Color], TransformLookup.new)
61
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
62
- expect( transformed_argument_value ).to eq(Color.new('red'))
63
- end
64
- end
65
-
66
- describe RegularExpression do
67
- it "transforms arguments with expression type" do
68
- expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, [], @transform_lookup)
69
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
70
- expect( transformed_argument_value ).to eq(Color.new('red'))
71
- end
72
-
73
- it "transforms arguments with explicit type" do
74
- expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, ['color'], @transform_lookup)
75
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
76
- expect( transformed_argument_value ).to eq(Color.new('red'))
77
- end
78
-
79
- it "transforms arguments with explicit type using constructor directly" do
80
- expression = RegularExpression.new(/I have a (red|blue|yellow) ball/, [Color], TransformLookup.new)
81
- transformed_argument_value = expression.match("I have a red ball")[0].transformed_value
82
- expect( transformed_argument_value ).to eq(Color.new('red'))
83
- end
84
- end
85
- end
86
- end
87
- end