gherkin 2.0.2-i386-mingw32 → 2.1.0-i386-mingw32

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 (61) hide show
  1. data/History.txt +12 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +2 -2
  4. data/Rakefile +2 -2
  5. data/VERSION.yml +2 -2
  6. data/features/json_formatter.feature +3 -41
  7. data/features/step_definitions/gherkin_steps.rb +5 -6
  8. data/features/step_definitions/json_formatter_steps.rb +3 -2
  9. data/features/step_definitions/json_lexer_steps.rb +5 -5
  10. data/features/step_definitions/pretty_formatter_steps.rb +9 -13
  11. data/features/support/env.rb +1 -1
  12. data/lib/gherkin/formatter/argument.rb +1 -0
  13. data/lib/gherkin/formatter/filter_formatter.rb +149 -0
  14. data/lib/gherkin/formatter/json_formatter.rb +35 -45
  15. data/lib/gherkin/formatter/line_filter.rb +26 -0
  16. data/lib/gherkin/formatter/model.rb +85 -0
  17. data/lib/gherkin/formatter/pretty_formatter.rb +36 -39
  18. data/lib/gherkin/formatter/regexp_filter.rb +17 -0
  19. data/lib/gherkin/formatter/tag_count_formatter.rb +44 -0
  20. data/lib/gherkin/i18n.rb +5 -5
  21. data/lib/gherkin/i18n.yml +13 -0
  22. data/lib/gherkin/i18n_lexer.rb +2 -2
  23. data/lib/gherkin/{json_lexer.rb → json_parser.rb} +17 -5
  24. data/lib/gherkin/{parser → listener}/event.rb +1 -1
  25. data/lib/gherkin/{parser → listener}/formatter_listener.rb +30 -23
  26. data/lib/gherkin/native/java.rb +9 -1
  27. data/lib/gherkin/parser/parser.rb +27 -14
  28. data/lib/gherkin/rubify.rb +5 -1
  29. data/lib/gherkin/tag_expression.rb +62 -0
  30. data/lib/gherkin/tools/files.rb +3 -4
  31. data/lib/gherkin/tools/reformat.rb +2 -2
  32. data/lib/gherkin/tools/stats.rb +3 -4
  33. data/lib/gherkin/tools/stats_listener.rb +1 -1
  34. data/ragel/lexer.c.rl.erb +2 -3
  35. data/ragel/lexer.java.rl.erb +1 -2
  36. data/ragel/lexer.rb.rl.erb +1 -2
  37. data/spec/gherkin/fixtures/complex_for_filtering.feature +60 -0
  38. data/spec/gherkin/fixtures/complex_with_tags.feature +61 -0
  39. data/spec/gherkin/fixtures/hantu_pisang.feature +35 -0
  40. data/spec/gherkin/formatter/filter_formatter_spec.rb +156 -0
  41. data/spec/gherkin/formatter/model_spec.rb +15 -0
  42. data/spec/gherkin/formatter/pretty_formatter_spec.rb +17 -16
  43. data/spec/gherkin/formatter/tag_count_formatter_spec.rb +31 -0
  44. data/spec/gherkin/i18n_lexer_spec.rb +3 -3
  45. data/spec/gherkin/i18n_spec.rb +2 -4
  46. data/spec/gherkin/{json_lexer_spec.rb → json_parser_spec.rb} +13 -8
  47. data/spec/gherkin/sexp_recorder.rb +10 -4
  48. data/spec/gherkin/shared/lexer_group.rb +0 -40
  49. data/spec/gherkin/shared/py_string_group.rb +0 -1
  50. data/spec/gherkin/shared/row_group.rb +1 -2
  51. data/spec/gherkin/tag_expression_spec.rb +137 -0
  52. data/spec/spec_helper.rb +5 -1
  53. data/tasks/bench.rake +5 -9
  54. metadata +33 -25
  55. data/lib/gherkin/parser/filter_listener.rb +0 -203
  56. data/lib/gherkin/parser/row.rb +0 -15
  57. data/lib/gherkin/parser/tag_expression.rb +0 -50
  58. data/spec/gherkin/parser/filter_listener_spec.rb +0 -397
  59. data/spec/gherkin/parser/formatter_listener_spec.rb +0 -134
  60. data/spec/gherkin/parser/parser_spec.rb +0 -50
  61. data/spec/gherkin/parser/tag_expression_spec.rb +0 -116
data/lib/gherkin/i18n.yml CHANGED
@@ -144,6 +144,19 @@
144
144
  then: "*|DEN"
145
145
  and: "*|AN"
146
146
  but: "*|BUT"
147
+ "en-pirate":
148
+ name: Pirate
149
+ native: Pirate
150
+ feature: Ahoy matey!
151
+ background: Yo-ho-ho
152
+ scenario: Heave to
153
+ scenario_outline: Shiver me timbers
154
+ examples: Dead men tell no tales
155
+ given: "*|Gangway!"
156
+ when: "*|Blimey!"
157
+ then: "*|Let go and haul"
158
+ and: "*|Aye"
159
+ but: "*|Avast!"
147
160
  "en-Scouse":
148
161
  name: Scouse
149
162
  native: Scouse
@@ -17,8 +17,8 @@ module Gherkin
17
17
  @force_ruby = force_ruby
18
18
  end
19
19
 
20
- def scan(source, uri, offset)
21
- create_delegate(source).scan(source, uri, offset)
20
+ def scan(source)
21
+ create_delegate(source).scan(source)
22
22
  end
23
23
 
24
24
  private
@@ -1,13 +1,27 @@
1
1
  require 'json'
2
+ require 'gherkin/listener/formatter_listener'
2
3
 
3
4
  module Gherkin
4
- class JSONLexer
5
+ class JSONParser
5
6
 
6
- def initialize(listener)
7
+ def initialize(formatter)
8
+ @formatter = formatter
9
+ end
10
+
11
+ def parse(src, feature_uri='unknown.json', line_offset=0)
12
+ @listener = Listener::FormatterListener.new(@formatter)
13
+ _parse(src, feature_uri, line_offset)
14
+ end
15
+
16
+ def parse_with_listener(src, listener)
7
17
  @listener = listener
18
+ _parse(src, 'unknown.json', 0)
8
19
  end
9
20
 
10
- def scan(src, uri='unknown.json', offset=0)
21
+ private
22
+
23
+ def _parse(src, feature_uri, line_offset)
24
+ @listener.location(feature_uri)
11
25
  feature = JSON.parse(src)
12
26
 
13
27
  comments_for(feature)
@@ -27,8 +41,6 @@ module Gherkin
27
41
  @listener.eof
28
42
  end
29
43
 
30
- private
31
-
32
44
  def parse_element(feature_element)
33
45
  comments_for(feature_element)
34
46
  tags_for(feature_element)
@@ -1,5 +1,5 @@
1
1
  module Gherkin
2
- module Parser
2
+ module Listener
3
3
  class Event < Array
4
4
  def event
5
5
  self[0]
@@ -1,8 +1,8 @@
1
1
  require 'gherkin/native'
2
- require 'gherkin/parser/row'
2
+ require 'gherkin/formatter/model'
3
3
 
4
4
  module Gherkin
5
- module Parser
5
+ module Listener
6
6
  # Adapter from the "raw" Gherkin <tt>Listener</tt> API
7
7
  # to the slightly more high-level <tt>Formatter</tt> API,
8
8
  # which is easier to implement (less state to keep track of).
@@ -16,54 +16,53 @@ module Gherkin
16
16
  @table = nil
17
17
  end
18
18
 
19
- def location(uri, offset)
20
- @uri = uri
21
- @offset = offset
19
+ def location(feature_uri)
20
+ @feature_uri = feature_uri
22
21
  end
23
22
 
24
- def comment(content, line)
25
- @comments << content
23
+ def comment(value, line)
24
+ @comments << Formatter::Model::Comment.new(value, line)
26
25
  end
27
26
 
28
27
  def tag(name, line)
29
- @tags << name
28
+ @tags << Formatter::Model::Tag.new(name, line)
30
29
  end
31
30
 
32
31
  def feature(keyword, name, description, line)
33
- @formatter.feature(grab_comments!, grab_tags!, keyword, name, description, @uri)
32
+ @formatter.feature(statement(grab_comments!, grab_tags!, keyword, name, description, line), @feature_uri)
34
33
  end
35
34
 
36
35
  def background(keyword, name, description, line)
37
- @formatter.background(grab_comments!, keyword, name, description, line)
36
+ @formatter.background(statement(grab_comments!, [], keyword, name, description, line))
38
37
  end
39
38
 
40
39
  def scenario(keyword, name, description, line)
41
40
  replay_step_or_examples
42
- @formatter.scenario(grab_comments!, grab_tags!, keyword, name, description, line)
41
+ @formatter.scenario(statement(grab_comments!, grab_tags!, keyword, name, description, line))
43
42
  end
44
43
 
45
44
  def scenario_outline(keyword, name, description, line)
46
45
  replay_step_or_examples
47
- @formatter.scenario_outline(grab_comments!, grab_tags!, keyword, name, description, line)
46
+ @formatter.scenario_outline(statement(grab_comments!, grab_tags!, keyword, name, description, line))
48
47
  end
49
48
 
50
49
  def examples(keyword, name, description, line)
51
50
  replay_step_or_examples
52
- @examples = [grab_comments!, grab_tags!, keyword, name, description, line]
51
+ @examples_statement = statement(grab_comments!, grab_tags!, keyword, name, description, line)
53
52
  end
54
53
 
55
54
  def step(keyword, name, line)
56
55
  replay_step_or_examples
57
- @step = [grab_comments!, keyword, name, line]
56
+ @step_statement = statement(grab_comments!, [], keyword, name, nil, line)
58
57
  end
59
58
 
60
59
  def row(cells, line)
61
60
  @table ||= []
62
- @table << Row.new(cells, grab_comments!, line)
61
+ @table << Formatter::Model::Row.new(grab_comments!, cells, line)
63
62
  end
64
63
 
65
- def py_string(py_string, line)
66
- @py_string = py_string
64
+ def py_string(string, line)
65
+ @py_string = Formatter::Model::PyString.new(string, line)
67
66
  end
68
67
 
69
68
  def eof
@@ -71,8 +70,16 @@ module Gherkin
71
70
  @formatter.eof
72
71
  end
73
72
 
73
+ def syntax_error(state, ev, legal_events, uri, line)
74
+ @formatter.syntax_error(state, ev, legal_events, uri, line)
75
+ end
76
+
74
77
  private
75
78
 
79
+ def statement(comments, tags, keyword, name, description, line)
80
+ Formatter::Model::Statement.new(comments, tags, keyword, name, description, line)
81
+ end
82
+
76
83
  def grab_comments!
77
84
  comments = @comments
78
85
  @comments = []
@@ -98,14 +105,14 @@ module Gherkin
98
105
  end
99
106
 
100
107
  def replay_step_or_examples
101
- if(@step)
108
+ if(@step_statement)
102
109
  multiline_arg = grab_py_string! || grab_table!
103
- @formatter.step(*(@step + [multiline_arg, nil, nil, nil, nil]))
104
- @step = nil
110
+ @formatter.step(@step_statement, multiline_arg, nil)
111
+ @step_statement = nil
105
112
  end
106
- if(@examples)
107
- @formatter.examples(*(@examples + [grab_table!]))
108
- @examples = nil
113
+ if(@examples_statement)
114
+ @formatter.examples(@examples_statement, grab_table!)
115
+ @examples_statement = nil
109
116
  end
110
117
  end
111
118
  end
@@ -26,7 +26,15 @@ class Class
26
26
  end
27
27
 
28
28
  def new(*args)
29
- java_class.new(*javaify(args))
29
+ begin
30
+ java_class.new(*javaify(args))
31
+ rescue ArgumentError => e
32
+ e.message << "\n#{java_class.name}"
33
+ raise e
34
+ rescue NameError => e
35
+ e.message << "\n args: #{args.inspect}"
36
+ raise e
37
+ end
30
38
  end
31
39
 
32
40
  def ===(object)
@@ -1,4 +1,5 @@
1
1
  require 'gherkin/native'
2
+ require 'gherkin/listener/formatter_listener'
2
3
 
3
4
  module Gherkin
4
5
  module Parser
@@ -12,24 +13,36 @@ module Gherkin
12
13
  native_impl('gherkin')
13
14
 
14
15
  # Initialize the parser. +machine_name+ refers to a state machine table.
15
- def initialize(listener, raise_on_error=true, machine_name='root')
16
- @listener = listener
16
+ def initialize(formatter, raise_on_error=true, machine_name='root', force_ruby=false)
17
+ @formatter = formatter
18
+ @listener = Listener::FormatterListener.new(@formatter)
17
19
  @raise_on_error = raise_on_error
18
- @machines = []
19
20
  @machine_name = machine_name
21
+ @machines = []
20
22
  push_machine(@machine_name)
23
+ @lexer = I18nLexer.new(self, force_ruby)
24
+ end
25
+
26
+ def parse(gherkin, feature_uri, line_offset)
27
+ @feature_uri = feature_uri
28
+ @line_offset = line_offset
29
+ @listener.location(feature_uri)
30
+ @lexer.scan(gherkin)
21
31
  end
22
32
 
23
- def location(uri, offset)
24
- @listener.location(uri, offset)
33
+ def i18n_language
34
+ @lexer.i18n_language
35
+ end
36
+
37
+ def errors
38
+ @lexer.errors
25
39
  end
26
40
 
27
41
  # Doesn't yet fall back to super
28
42
  def method_missing(method, *args)
29
43
  # TODO: Catch exception and call super
30
- if(event(method.to_s, args[-1]))
31
- @listener.__send__(method, *args)
32
- end
44
+ event(method.to_s, args[-1])
45
+ @listener.__send__(method, *args)
33
46
  if method == :eof
34
47
  pop_machine
35
48
  push_machine(@machine_name)
@@ -37,15 +50,15 @@ module Gherkin
37
50
  end
38
51
 
39
52
  def event(ev, line)
40
- machine.event(ev, line) do |state, expected|
53
+ l = line ? @line_offset+line : nil
54
+ machine.event(ev, l) do |state, legal_events|
41
55
  if @raise_on_error
42
- raise ParseError.new(state, ev, expected, line)
56
+ raise ParseError.new(state, ev, legal_events, @feature_uri, l)
43
57
  else
44
- @listener.syntax_error(state, ev, expected, line)
45
- return false
58
+ # Only used for testing
59
+ @listener.syntax_error(state, ev, legal_events, @feature_uri, l)
46
60
  end
47
61
  end
48
- true
49
62
  end
50
63
 
51
64
  def push_machine(name)
@@ -122,7 +135,7 @@ module Gherkin
122
135
  state_machine_reader = StateMachineReader.new
123
136
  lexer = Gherkin::I18n.new('en').lexer(state_machine_reader)
124
137
  machine = File.dirname(__FILE__) + "/#{name}.txt"
125
- lexer.scan(File.read(machine), machine, 0)
138
+ lexer.scan(File.read(machine))
126
139
  state_machine_reader.rows
127
140
  end
128
141
 
@@ -5,8 +5,12 @@ module Gherkin
5
5
  # This is especially important to convert java.util.List coming
6
6
  # from Java and back to a Ruby Array.
7
7
  def rubify(o)
8
- if Java.java.util.Collection === o || Array === o
8
+ case(o)
9
+ when Java.java.util.Collection, Array
9
10
  o.map{|e| rubify(e)}
11
+ when Java.gherkin.formatter.model.PyString
12
+ require 'gherkin/formatter/model'
13
+ Formatter::Model::PyString.new(o.value, o.line)
10
14
  else
11
15
  o
12
16
  end
@@ -0,0 +1,62 @@
1
+ require 'gherkin/native'
2
+
3
+ module Gherkin
4
+ class TagExpression
5
+ native_impl('gherkin')
6
+
7
+ attr_reader :limits
8
+
9
+ def initialize(tag_expressions)
10
+ @ands = []
11
+ @limits = {}
12
+ tag_expressions.each do |expr|
13
+ add(expr.strip.split(/\s*,\s*/))
14
+ end
15
+ end
16
+
17
+ def empty?
18
+ @ands.empty?
19
+ end
20
+
21
+ def eval(tags)
22
+ return true if @ands.flatten.empty?
23
+ vars = Hash[*tags.map{|tag| [tag, true]}.flatten]
24
+ !!Kernel.eval(ruby_expression)
25
+ end
26
+
27
+ private
28
+
29
+ def add(tags_with_negation_and_limits)
30
+ negatives, positives = tags_with_negation_and_limits.partition{|tag| tag =~ /^~/}
31
+ @ands << (store_and_extract_limits(negatives, true) + store_and_extract_limits(positives, false))
32
+ end
33
+
34
+ def store_and_extract_limits(tags_with_negation_and_limits, negated)
35
+ tags_with_negation = []
36
+ tags_with_negation_and_limits.each do |tag_with_negation_and_limit|
37
+ tag_with_negation, limit = tag_with_negation_and_limit.split(':')
38
+ tags_with_negation << tag_with_negation
39
+ if limit
40
+ tag_without_negation = negated ? tag_with_negation[1..-1] : tag_with_negation
41
+ if @limits[tag_without_negation] && @limits[tag_without_negation] != limit.to_i
42
+ raise "Inconsistent tag limits for #{tag_without_negation}: #{@limits[tag_without_negation]} and #{limit.to_i}"
43
+ end
44
+ @limits[tag_without_negation] = limit.to_i
45
+ end
46
+ end
47
+ tags_with_negation
48
+ end
49
+
50
+ def ruby_expression
51
+ "(" + @ands.map do |ors|
52
+ ors.map do |tag|
53
+ if tag =~ /^~(.*)/
54
+ "!vars['#{$1}']"
55
+ else
56
+ "vars['#{tag}']"
57
+ end
58
+ end.join("||")
59
+ end.join(")&&(") + ")"
60
+ end
61
+ end
62
+ end
@@ -20,11 +20,10 @@ module Gherkin
20
20
  Dir[*globs].uniq.sort.each(&proc)
21
21
  end
22
22
 
23
- def scan(file, listener)
24
- parser = Gherkin::Parser::Parser.new(listener, true, "root")
25
- lexer = Gherkin::I18nLexer.new(parser, false)
23
+ def scan(file, formatter)
24
+ parser = Gherkin::Parser::Parser.new(formatter, true, "root")
26
25
  begin
27
- lexer.scan(IO.read(file), file, 0)
26
+ parser.parse(IO.read(file), file, 0)
28
27
  rescue => e
29
28
  e.message << " (#{file})"
30
29
  raise e
@@ -1,7 +1,7 @@
1
1
  require 'stringio'
2
2
  require 'gherkin/tools/files'
3
3
  require 'gherkin/formatter/pretty_formatter'
4
- require 'gherkin/parser/formatter_listener'
4
+ require 'gherkin/listener/formatter_listener'
5
5
 
6
6
  module Gherkin
7
7
  module Tools
@@ -10,7 +10,7 @@ module Gherkin
10
10
  each do |file|
11
11
  io = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
12
12
  formatter = Formatter::PrettyFormatter.new(io, true)
13
- listener = Parser::FormatterListener.new(formatter)
13
+ listener = Listener::FormatterListener.new(formatter)
14
14
  scan(file, listener)
15
15
  string = defined?(JRUBY_VERSION) ? io.getBuffer.toString : io.string
16
16
  File.open(file, 'w') {|io| io.write(string)}
@@ -6,11 +6,10 @@ module Gherkin
6
6
  module Tools
7
7
  class Stats < Files
8
8
  def run
9
- listener = StatsListener.new
9
+ formatter = StatsFormatter.new
10
10
  each do |f|
11
- parser = Gherkin::Parser::Parser.new(listener, true)
12
- lexer = Gherkin::I18nLexer.new(parser)
13
- lexer.scan(IO.read(f), f, 0)
11
+ parser = Gherkin::Parser::Parser.new(formatter, true)
12
+ parser.parse(IO.read(f), f, 0)
14
13
  end
15
14
  puts "Features: #{listener.features}"
16
15
  puts "Scenarios: #{listener.scenarios}"
@@ -50,7 +50,7 @@ module Gherkin
50
50
  def py_string(string, line)
51
51
  end
52
52
 
53
- def syntax_error(state, event, legal_events, line)
53
+ def syntax_error(state, event, legal_events, uri, line)
54
54
  end
55
55
 
56
56
  def eof
data/ragel/lexer.c.rl.erb CHANGED
@@ -389,10 +389,9 @@ static VALUE CLexer_init(VALUE self, VALUE listener)
389
389
  return self;
390
390
  }
391
391
 
392
- static VALUE CLexer_scan(VALUE self, VALUE input, VALUE uri, VALUE offset)
392
+ static VALUE CLexer_scan(VALUE self, VALUE input)
393
393
  {
394
394
  VALUE listener = rb_iv_get(self, "@listener");
395
- rb_funcall(listener, rb_intern("location"), 2, uri, offset);
396
395
 
397
396
  lexer_state *lexer = NULL;
398
397
  DATA_GET(self, lexer_state, lexer);
@@ -449,6 +448,6 @@ void Init_gherkin_lexer_<%= @i18n.underscored_iso_code %>()
449
448
  cI18nLexer = rb_define_class_under(mCLexer, "<%= @i18n.underscored_iso_code.capitalize %>", rb_cObject);
450
449
  rb_define_alloc_func(cI18nLexer, CLexer_alloc);
451
450
  rb_define_method(cI18nLexer, "initialize", CLexer_init, 1);
452
- rb_define_method(cI18nLexer, "scan", CLexer_scan, 3);
451
+ rb_define_method(cI18nLexer, "scan", CLexer_scan, 1);
453
452
  }
454
453