cucumber-expressions 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/.rsync +2 -0
  3. data/Makefile +5 -9
  4. data/cucumber-expressions.gemspec +1 -1
  5. data/examples.txt +7 -11
  6. data/lib/cucumber/cucumber_expressions/argument.rb +25 -5
  7. data/lib/cucumber/cucumber_expressions/combinatorial_generated_expression_factory.rb +40 -0
  8. data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +8 -26
  9. data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +36 -19
  10. data/lib/cucumber/cucumber_expressions/errors.rb +40 -0
  11. data/lib/cucumber/cucumber_expressions/generated_expression.rb +23 -3
  12. data/lib/cucumber/cucumber_expressions/group.rb +66 -0
  13. data/lib/cucumber/cucumber_expressions/parameter_type.rb +38 -5
  14. data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +4 -4
  15. data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +34 -54
  16. data/lib/cucumber/cucumber_expressions/regular_expression.rb +27 -27
  17. data/spec/cucumber/cucumber_expressions/combinatorial_generated_expression_factory_test.rb +43 -0
  18. data/spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb +10 -2
  19. data/spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb +4 -11
  20. data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +42 -43
  21. data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +109 -94
  22. data/spec/cucumber/cucumber_expressions/expression_examples_spec.rb +3 -3
  23. data/spec/cucumber/cucumber_expressions/group_spec.rb +34 -0
  24. data/spec/cucumber/cucumber_expressions/parameter_type_registry_spec.rb +86 -0
  25. data/spec/cucumber/cucumber_expressions/regular_expression_spec.rb +17 -25
  26. metadata +14 -5
  27. data/lib/cucumber/cucumber_expressions/argument_builder.rb +0 -17
@@ -1,21 +1,54 @@
1
+ require 'cucumber/cucumber_expressions/errors'
2
+
1
3
  module Cucumber
2
4
  module CucumberExpressions
3
5
  class ParameterType
4
6
  attr_reader :name, :type, :regexps
5
7
 
8
+ def prefer_for_regexp_match?
9
+ @prefer_for_regexp_match
10
+ end
11
+
12
+ def use_for_snippets?
13
+ @use_for_snippets
14
+ end
15
+
6
16
  # Create a new Parameter
7
17
  #
8
18
  # @param name the name of the parameter type
9
- # @param regexps [Array] list of regexps for capture groups. A single regexp can also be used.
19
+ # @param regexp [Array] list of regexps for capture groups. A single regexp can also be used
20
+ # @param type the return type of the transformed
10
21
  # @param transformer lambda that transforms a String to (possibly) another type
22
+ # @param use_for_snippets true if this should be used for snippet generation
23
+ # @param prefer_for_regexp_match true if this should be preferred over similar types
11
24
  #
12
- def initialize(name, type, regexp, transformer)
13
- @name, @type, @transformer = name, type, transformer
25
+ def initialize(name, regexp, type, transformer, use_for_snippets, prefer_for_regexp_match)
26
+ raise "name can't be nil" if name.nil?
27
+ raise "regexp can't be nil" if regexp.nil?
28
+ raise "type can't be nil" if type.nil?
29
+ raise "transformer can't be nil" if transformer.nil?
30
+ raise "use_for_snippets can't be nil" if use_for_snippets.nil?
31
+ raise "prefer_for_regexp_match can't be nil" if prefer_for_regexp_match.nil?
32
+
33
+ @name, @type, @transformer, @use_for_snippets, @prefer_for_regexp_match = name, type, transformer, use_for_snippets, prefer_for_regexp_match
14
34
  @regexps = string_array(regexp)
15
35
  end
16
36
 
17
- def transform(value)
18
- @transformer ? @transformer.call(value) : value
37
+ def transform(group_values)
38
+ if @transformer.arity == 1
39
+ non_nil_group_values = group_values.compact
40
+ raise CucumberExpressionError.new(
41
+ "Single transformer unexpectedly matched 2 values - \"#{non_nil_group_values[0]}\" and \"${non_nil_group_values[1]}\""
42
+ ) if non_nil_group_values.length >= 2
43
+ return @transformer.call(non_nil_group_values[0])
44
+ end
45
+ @transformer.call(*group_values)
46
+ end
47
+
48
+ def <=>(other)
49
+ return -1 if prefer_for_regexp_match? && !other.prefer_for_regexp_match?
50
+ return 1 if other.prefer_for_regexp_match? && !prefer_for_regexp_match?
51
+ name <=> other.name
19
52
  end
20
53
 
21
54
  private
@@ -1,15 +1,15 @@
1
1
  module Cucumber
2
2
  module CucumberExpressions
3
3
  class ParameterTypeMatcher
4
- attr_reader :parameter
4
+ attr_reader :parameter_type
5
5
 
6
- def initialize(parameter, regexp, text, match_position=0)
7
- @parameter, @regexp, @text = parameter, regexp, text
6
+ def initialize(parameter_type, regexp, text, match_position=0)
7
+ @parameter_type, @regexp, @text = parameter_type, 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(parameter, @regexp, @text, new_match_position)
12
+ self.class.new(parameter_type, @regexp, @text, new_match_position)
13
13
  end
14
14
 
15
15
  def find
@@ -1,82 +1,62 @@
1
1
  require 'cucumber/cucumber_expressions/parameter_type'
2
+ require 'cucumber/cucumber_expressions/errors'
3
+ require 'cucumber/cucumber_expressions/cucumber_expression_generator'
2
4
 
3
5
  module Cucumber
4
6
  module CucumberExpressions
5
7
  class ParameterTypeRegistry
6
8
  INTEGER_REGEXPS = [/-?\d+/, /\d+/]
7
9
  FLOAT_REGEXP = /-?\d*\.?\d+/
10
+ WORD_REGEXP = /\w+/
11
+ STRING_REGEXP = /"([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'/
8
12
 
9
13
  def initialize
10
- @parameter_types_by_name = {}
11
- @parameter_types_by_regexp = {}
12
- @parameter_types_by_class = {}
14
+ @parameter_type_by_name = {}
15
+ @parameter_types_by_regexp = Hash.new {|hash, regexp| hash[regexp] = []}
13
16
 
14
- define_predefined_parameter_type(ParameterType.new('int', Integer, INTEGER_REGEXPS, lambda { |s| s.to_i }))
15
- define_predefined_parameter_type(ParameterType.new('float', Float, FLOAT_REGEXP, lambda { |s| s.to_f }))
17
+ define_parameter_type(ParameterType.new('int', INTEGER_REGEXPS, Integer, lambda {|s| s.to_i}, true, true))
18
+ define_parameter_type(ParameterType.new('float', FLOAT_REGEXP, Float, lambda {|s| s.to_f}, true, false))
19
+ define_parameter_type(ParameterType.new('word', WORD_REGEXP, String, lambda {|s| s}, false, false))
20
+ define_parameter_type(ParameterType.new('string', STRING_REGEXP, String, lambda {|s| s.gsub(/\\"/, '"').gsub(/\\'/, "'")}, true, false))
16
21
  end
17
22
 
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_name(type)
23
- else
24
- raise Exception.new("Type must be string or class, but was #{type} of type #{type.class}")
25
- end
23
+ def lookup_by_type_name(name)
24
+ @parameter_type_by_name[name]
26
25
  end
27
26
 
28
- def lookup_by_class(clazz)
29
- parameter = @parameter_types_by_class[clazz]
30
- if parameter.nil?
31
- create_anonymous_lookup(lambda { |s| clazz.new(s) })
32
- else
33
- parameter
27
+ def lookup_by_regexp(parameter_type_regexp, expression_regexp, text)
28
+ parameter_types = @parameter_types_by_regexp[parameter_type_regexp]
29
+ return nil if parameter_types.nil?
30
+ if parameter_types.length > 1 && !parameter_types[0].prefer_for_regexp_match?
31
+ # We don't do this check on insertion because we only want to restrict
32
+ # ambiguity when we look up by Regexp. Users of CucumberExpression should
33
+ # not be restricted.
34
+ generated_expressions = CucumberExpressionGenerator.new(self).generate_expressions(text)
35
+ raise AmbiguousParameterTypeError.new(parameter_type_regexp, expression_regexp, parameter_types, generated_expressions)
34
36
  end
35
- end
36
-
37
- def lookup_by_name(name)
38
- @parameter_types_by_name[name]
39
- end
40
-
41
- def lookup_by_regexp(regexp)
42
- @parameter_types_by_regexp[regexp]
43
- end
44
-
45
- def create_anonymous_lookup(proc)
46
- ParameterType.new(nil, nil, ['.+'], proc)
37
+ parameter_types.first
47
38
  end
48
39
 
49
40
  def parameter_types
50
- @parameter_types_by_name.values
41
+ @parameter_type_by_name.values
51
42
  end
52
43
 
53
44
  def define_parameter_type(parameter_type)
54
- define_parameter_type0(parameter_type, true)
55
- end
56
-
57
- private
58
-
59
- def define_predefined_parameter_type(parameter_type)
60
- define_parameter_type0(parameter_type, false)
61
- end
62
-
63
- def define_parameter_type0(parameter_type, check_conflicts)
64
- if parameter_type.type
65
- put(@parameter_types_by_class, parameter_type.type, parameter_type, "type", check_conflicts)
45
+ if @parameter_type_by_name.has_key?(parameter_type.name)
46
+ raise CucumberExpressionError.new("There is already a parameter with name #{parameter_type.name}")
66
47
  end
67
- put(@parameter_types_by_name, parameter_type.name, parameter_type, "type name", check_conflicts)
68
-
69
- parameter_type.regexps.each do |regexp|
70
- put(@parameter_types_by_regexp, regexp, parameter_type, "regexp", check_conflicts)
48
+ @parameter_type_by_name[parameter_type.name] = parameter_type
49
+
50
+ parameter_type.regexps.each do |parameter_type_regexp|
51
+ parameter_types = @parameter_types_by_regexp[parameter_type_regexp]
52
+ if parameter_types.any? && parameter_types[0].prefer_for_regexp_match? && parameter_type.prefer_for_regexp_match?
53
+ raise CucumberExpressionError.new("There can only be one prefer_for_regexp_match parameter type per regexp. The regexp /#{parameter_type_regexp}/ is used for two prefer_for_regexp_match parameter types, {#{parameter_types[0].name}} and {#{parameter_type.name}}")
54
+ end
55
+ parameter_types.push(parameter_type)
56
+ parameter_types.sort!
71
57
  end
72
58
  end
73
59
 
74
- def put(map, key, parameter, prop, check_conflicts)
75
- if check_conflicts && map.has_key?(key)
76
- raise "There is already a parameter with #{prop} #{key}"
77
- end
78
- map[key] = parameter
79
- end
80
60
  end
81
61
  end
82
62
  end
@@ -1,48 +1,48 @@
1
- require 'cucumber/cucumber_expressions/argument_builder'
1
+ require 'cucumber/cucumber_expressions/argument'
2
+ require 'cucumber/cucumber_expressions/parameter_type'
2
3
 
3
4
  module Cucumber
4
5
  module CucumberExpressions
5
6
  class RegularExpression
6
- CAPTURE_GROUP_PATTERN = /\(([^(]+)\)/
7
+ CAPTURE_GROUP_PATTERN = /\((?!\?:)([^(]+)\)/
7
8
 
8
- def initialize(regexp, types, parameter_type_registry)
9
- @regexp = regexp
10
- @parameter_types = []
9
+ def initialize(expression_regexp, parameter_type_registry)
10
+ @expression_regexp = expression_regexp
11
+ @parameter_type_registry = parameter_type_registry
12
+ end
13
+
14
+ def match(text)
15
+ parameter_types = []
11
16
 
12
- type_index = 0
13
- match = nil
14
17
  match_offset = 0
15
18
 
16
19
  loop do
17
- match = CAPTURE_GROUP_PATTERN.match(regexp.source, match_offset)
20
+ match = CAPTURE_GROUP_PATTERN.match(@expression_regexp.source, match_offset)
18
21
  break if match.nil?
22
+ match_offset = match.offset(0)[1]
19
23
 
20
- capture_group_pattern = match[1]
21
- type = types.length <= type_index ? nil : types[type_index]
22
- type_index += 1
23
-
24
- parameter_type = nil
25
- if (type)
26
- parameter_type = parameter_type_registry.lookup_by_type(type)
27
- end
28
- if (parameter_type.nil?)
29
- parameter_type = parameter_type_registry.lookup_by_regexp(capture_group_pattern)
30
- end
31
- if (parameter_type.nil?)
32
- parameter_type = parameter_type_registry.create_anonymous_lookup(lambda {|s| s})
24
+ parameter_type_regexp = match[1]
25
+
26
+ parameter_type = @parameter_type_registry.lookup_by_regexp(parameter_type_regexp, @expression_regexp, text)
27
+ if parameter_type.nil?
28
+ parameter_type = ParameterType.new(
29
+ '*',
30
+ parameter_type_regexp,
31
+ String,
32
+ lambda {|s| s},
33
+ false,
34
+ false
35
+ )
33
36
  end
34
37
 
35
- @parameter_types.push(parameter_type)
36
- match_offset = match.offset(0)[1]
38
+ parameter_types.push(parameter_type)
37
39
  end
38
- end
39
40
 
40
- def match(text)
41
- ArgumentBuilder.build_arguments(@regexp, text, @parameter_types)
41
+ Argument.build(@expression_regexp, text, parameter_types)
42
42
  end
43
43
 
44
44
  def source
45
- @regexp
45
+ @expression_regexp
46
46
  end
47
47
  end
48
48
  end
@@ -0,0 +1,43 @@
1
+ require 'cucumber/cucumber_expressions/parameter_type'
2
+ require 'cucumber/cucumber_expressions/combinatorial_generated_expression_factory'
3
+
4
+ module Cucumber
5
+ module CucumberExpressions
6
+
7
+ class Color; end
8
+ class CssColor; end
9
+ class Date; end
10
+ class DateTime; end
11
+ class Timestamp; end
12
+
13
+ describe CombinatorialGeneratedExpressionFactory do
14
+ it 'generates multiple expressions' do
15
+ parameter_type_combinations = [
16
+ [
17
+ ParameterType.new('color', /red|blue|yellow/, Color, lambda {|s| Color.new}, true, false),
18
+ ParameterType.new('csscolor', /red|blue|yellow/, CssColor, lambda {|s| CssColor.new}, true, false)
19
+ ],
20
+ [
21
+ ParameterType.new('date', /\d{4}-\d{2}-\d{2}/, Date, lambda {|s| Date.new}, true, false),
22
+ ParameterType.new('datetime', /\d{4}-\d{2}-\d{2}/, DateTime, lambda {|s| DateTime.new}, true, false),
23
+ ParameterType.new('timestamp', /\d{4}-\d{2}-\d{2}/, Timestamp, lambda {|s| Timestamp.new}, true, false)
24
+ ]
25
+ ]
26
+
27
+ factory = CombinatorialGeneratedExpressionFactory.new(
28
+ 'I bought a {%s} ball on {%s}',
29
+ parameter_type_combinations
30
+ )
31
+ expressions = factory.generate_expressions.map {|ge| ge.source}
32
+ expect(expressions).to eq([
33
+ 'I bought a {color} ball on {date}',
34
+ 'I bought a {color} ball on {datetime}',
35
+ 'I bought a {color} ball on {timestamp}',
36
+ 'I bought a {csscolor} ball on {date}',
37
+ 'I bought a {csscolor} ball on {datetime}',
38
+ 'I bought a {csscolor} ball on {timestamp}',
39
+ ])
40
+ end
41
+ end
42
+ end
43
+ end
@@ -34,6 +34,12 @@ module Cucumber
34
34
  "I have 2 cukes and 1.5 euro")
35
35
  end
36
36
 
37
+ it "generates expression for strings" do
38
+ assert_expression(
39
+ "I like {string} and {string}", ["string", "string2"],
40
+ 'I like "bangers" and \'mash\'')
41
+ end
42
+
37
43
  it "generates expression for just int" do
38
44
  assert_expression(
39
45
  "{int}", ["int"],
@@ -49,9 +55,11 @@ module Cucumber
49
55
  it "numbers only second argument when type is not reserved keyword" do
50
56
  @parameter_type_registry.define_parameter_type(ParameterType.new(
51
57
  'currency',
52
- Currency,
53
58
  '[A-Z]{3}',
54
- nil
59
+ Currency,
60
+ lambda {|s| Currency.new(s)},
61
+ true,
62
+ true
55
63
  ))
56
64
 
57
65
  assert_expression(
@@ -6,7 +6,7 @@ module Cucumber
6
6
  describe CucumberExpression do
7
7
  context "Regexp translation" do
8
8
  def assert_regexp(expression, regexp)
9
- cucumber_expression = CucumberExpression.new(expression, [], ParameterTypeRegistry.new)
9
+ cucumber_expression = CucumberExpression.new(expression, ParameterTypeRegistry.new)
10
10
  expect(regexp).to eq(cucumber_expression.instance_variable_get('@regexp'))
11
11
  end
12
12
 
@@ -24,17 +24,10 @@ module Cucumber
24
24
  )
25
25
  end
26
26
 
27
- it "translates two untyped arguments" do
27
+ it "translates parameters" do
28
28
  assert_regexp(
29
- "I have {n} cukes in my {bodypart} now",
30
- /^I have (.+) cukes in my (.+) now$/
31
- )
32
- end
33
-
34
- it "translates three typed arguments" do
35
- assert_regexp(
36
- "I have {float} cukes in my {bodypart} at {int} o'clock",
37
- /^I have (-?\d*\.?\d+) cukes in my (.+) at ((?:-?\d+)|(?:\d+)) o'clock$/
29
+ "I have {float} cukes at {int} o'clock",
30
+ /^I have (-?\d*\.?\d+) cukes at ((?:-?\d+)|(?:\d+)) o'clock$/
38
31
  )
39
32
  end
40
33
 
@@ -8,84 +8,83 @@ module Cucumber
8
8
  parameter_registry = ParameterTypeRegistry.new
9
9
 
10
10
  ### [capture-match-arguments]
11
- expr = "I have {n} cuke(s) in my {bodypart} now"
12
- types = ['int', nil]
13
- expression = CucumberExpression.new(expr, types, parameter_registry)
14
- args = expression.match("I have 7 cukes in my belly now")
15
- expect( args[0].transformed_value ).to eq(7)
16
- expect( args[1].transformed_value ).to eq("belly")
11
+ expr = "I have {int} cuke(s)"
12
+ expression = CucumberExpression.new(expr, parameter_registry)
13
+ args = expression.match("I have 7 cukes")
14
+ expect(args[0].value).to eq(7)
17
15
  ### [capture-match-arguments]
18
16
  end
19
17
 
20
- it "does no transform by default" do
21
- expect( match("{what}", "22") ).to eq(["22"])
18
+ it "matches word" do
19
+ expect(match("three {word} mice", "three blind mice")).to eq(['blind'])
22
20
  end
23
21
 
24
- it "transforms to int by parameter type" do
25
- expect( match("{int}", "22") ).to eq([22])
22
+ it('matches double quoted string') do
23
+ expect(match('three {string} mice', 'three "blind" mice')).to eq(['blind'])
26
24
  end
27
25
 
28
- it "transforms to int by explicit type" do
29
- expect( match("{what}", "22", ['int']) ).to eq([22])
26
+ it('matches single quoted string') do
27
+ expect(match('three {string} mice', "three 'blind' mice")).to eq(['blind'])
30
28
  end
31
29
 
32
- # Ruby-specific
33
- it "parameters to Integer by explicit type" do
34
- expect( match("{what}", "22", [Integer]) ).to eq([22])
30
+ it('does not match misquoted string') do
31
+ expect(match('three {string} mice', 'three "blind\' mice')).to eq(nil)
35
32
  end
36
33
 
37
- it "doesn't match a float with an int parameter" do
38
- expect( match("{int}", "1.22") ).to be_nil
34
+ it('matches single quoted string with double quotes') do
35
+ expect(match('three {string} mice', 'three \'"blind"\' mice')).to eq(['"blind"'])
39
36
  end
40
37
 
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])
38
+ it('matches double quoted string with single quotes') do
39
+ expect(match('three {string} mice', 'three "\'blind\'" mice')).to eq(["'blind'"])
44
40
  end
45
41
 
46
- it "transforms to float by explicit type" do
47
- expect( match("{what}", "0.22", ['float']) ).to eq([0.22])
48
- expect( match("{what}", ".22", ['float']) ).to eq([0.22])
42
+ it('matches double quoted string with escaped double quote') do
43
+ expect(match('three {string} mice', 'three "bl\\"nd" mice')).to eq(['bl"nd'])
49
44
  end
50
45
 
51
- it "leaves unknown type untransformed" do
52
- expect( match("{unknown}", "something") ).to eq(["something"])
46
+ it('matches single quoted string with escaped single quote') do
47
+ expect(match('three {string} mice', "three 'bl\\'nd' mice")).to eq(["bl'nd"])
53
48
  end
54
49
 
55
- it "supports deprecated {name:type} syntax for now" do
56
- expect( match("{param:unknown}", "something") ).to eq(["something"])
50
+ it "matches int" do
51
+ expect(match("{int}", "22")).to eq([22])
57
52
  end
58
53
 
59
- it "exposes source" do
60
- expr = "I have {int} cuke(s) in my {bodypart} now"
61
- expect(CucumberExpression.new(expr, [], ParameterTypeRegistry.new).source).to eq(expr)
54
+ it "doesn't match float as int" do
55
+ expect(match("{int}", "1.22")).to be_nil
56
+ end
57
+
58
+ it "matches float" do
59
+ expect(match("{float}", "0.22")).to eq([0.22])
60
+ expect(match("{float}", ".22")).to eq([0.22])
62
61
  end
63
62
 
64
- it "exposes offset and value" do
65
- expr = "I have {int} cuke(s) in my {bodypart} now"
66
- expression = CucumberExpression.new(expr, [], ParameterTypeRegistry.new)
67
- arg1 = expression.match("I have 800 cukes in my brain now")[0]
68
- expect(arg1.offset).to eq(7)
69
- expect(arg1.value).to eq("800")
63
+ it "throws unknown parameter type" do
64
+ expect {match("{unknown}", "something")}.to raise_error('Undefined parameter type {unknown}')
65
+ end
66
+
67
+ it "exposes source" do
68
+ expr = "I have {int} cuke(s)"
69
+ expect(CucumberExpression.new(expr, ParameterTypeRegistry.new).source).to eq(expr)
70
70
  end
71
71
 
72
- describe "RegExp special characters" do
72
+ describe "escapes special characters" do
73
73
  %w(\\ [ ] ^ $ . | ? * +).each do |character|
74
74
  it "escapes #{character}" do
75
75
  expr = "I have {int} cuke(s) and #{character}"
76
- expression = CucumberExpression.new(expr, [], ParameterTypeRegistry.new)
76
+ expression = CucumberExpression.new(expr, ParameterTypeRegistry.new)
77
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")
78
+ expect(arg1.value).to eq(800)
80
79
  end
81
80
  end
82
81
  end
83
82
 
84
- def match(expression, text, types = [])
85
- cucumber_expression = CucumberExpression.new(expression, types, ParameterTypeRegistry.new)
83
+ def match(expression, text)
84
+ cucumber_expression = CucumberExpression.new(expression, ParameterTypeRegistry.new)
86
85
  args = cucumber_expression.match(text)
87
86
  return nil if args.nil?
88
- args.map { |arg| arg.transformed_value }
87
+ args.map {|arg| arg.value}
89
88
  end
90
89
  end
91
90
  end