gherkin 1.0.30-i386-mswin32 → 2.0.0-i386-mswin32

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 (58) hide show
  1. data/.rspec +1 -0
  2. data/History.txt +19 -0
  3. data/Rakefile +4 -4
  4. data/VERSION.yml +2 -2
  5. data/features/feature_parser.feature +11 -0
  6. data/features/json_formatter.feature +238 -0
  7. data/features/pretty_formatter.feature +9 -0
  8. data/features/step_definitions/gherkin_steps.rb +1 -1
  9. data/features/step_definitions/json_formatter_steps.rb +32 -0
  10. data/features/step_definitions/pretty_formatter_steps.rb +24 -23
  11. data/features/support/env.rb +3 -3
  12. data/lib/gherkin/formatter/json_formatter.rb +82 -0
  13. data/lib/gherkin/formatter/pretty_formatter.rb +73 -78
  14. data/lib/gherkin/i18n.rb +22 -18
  15. data/lib/gherkin/i18n.yml +9 -9
  16. data/lib/gherkin/i18n_lexer.rb +2 -2
  17. data/lib/gherkin/parser/event.rb +6 -6
  18. data/lib/gherkin/parser/filter_listener.rb +5 -1
  19. data/lib/gherkin/parser/formatter_listener.rb +113 -0
  20. data/lib/gherkin/parser/json_parser.rb +102 -0
  21. data/lib/gherkin/parser/parser.rb +10 -2
  22. data/lib/gherkin/parser/row.rb +15 -0
  23. data/lib/gherkin/rubify.rb +2 -0
  24. data/lib/gherkin/tools/files.rb +1 -1
  25. data/lib/gherkin/tools/reformat.rb +1 -2
  26. data/lib/gherkin/tools/stats.rb +1 -1
  27. data/lib/gherkin/tools/stats_listener.rb +5 -5
  28. data/ragel/lexer.c.rl.erb +41 -12
  29. data/ragel/lexer.java.rl.erb +26 -17
  30. data/ragel/lexer.rb.rl.erb +10 -5
  31. data/ragel/lexer_common.rl.erb +6 -6
  32. data/spec/gherkin/c_lexer_spec.rb +2 -2
  33. data/spec/gherkin/fixtures/complex.js +105 -0
  34. data/spec/gherkin/formatter/argument_spec.rb +3 -3
  35. data/spec/gherkin/formatter/colors_spec.rb +3 -4
  36. data/spec/gherkin/formatter/pretty_formatter_spec.rb +21 -50
  37. data/spec/gherkin/i18n_lexer_spec.rb +6 -6
  38. data/spec/gherkin/i18n_spec.rb +16 -9
  39. data/spec/gherkin/java_lexer_spec.rb +1 -2
  40. data/spec/gherkin/output_stream_string_io.rb +24 -0
  41. data/spec/gherkin/parser/filter_listener_spec.rb +16 -9
  42. data/spec/gherkin/parser/formatter_listener_spec.rb +134 -0
  43. data/spec/gherkin/parser/json_parser_spec.rb +129 -0
  44. data/spec/gherkin/parser/parser_spec.rb +9 -9
  45. data/spec/gherkin/parser/tag_expression_spec.rb +8 -8
  46. data/spec/gherkin/rb_lexer_spec.rb +1 -1
  47. data/spec/gherkin/sexp_recorder.rb +21 -1
  48. data/spec/gherkin/shared/{lexer_spec.rb → lexer_group.rb} +172 -102
  49. data/spec/gherkin/shared/{py_string_spec.rb → py_string_group.rb} +21 -17
  50. data/spec/gherkin/shared/{row_spec.rb → row_group.rb} +36 -19
  51. data/spec/gherkin/shared/{tags_spec.rb → tags_group.rb} +13 -9
  52. data/spec/spec_helper.rb +18 -38
  53. data/tasks/bench.rake +3 -3
  54. data/tasks/compile.rake +13 -14
  55. data/tasks/rspec.rake +6 -11
  56. metadata +42 -28
  57. data/features/pretty_printer.feature +0 -14
  58. data/spec/gherkin/csharp_lexer_spec.rb +0 -20
@@ -1,11 +1,6 @@
1
1
  module Gherkin
2
2
  module Parser
3
3
  class Event < Array
4
- def initialize(*args)
5
- super
6
- self[1] = self[1].to_a if event == :row # Special JRuby handling
7
- end
8
-
9
4
  def event
10
5
  self[0]
11
6
  end
@@ -24,7 +19,12 @@ module Gherkin
24
19
  end
25
20
 
26
21
  def replay(listener)
27
- listener.__send__(event, *args)
22
+ begin
23
+ listener.__send__(event, *args)
24
+ rescue ArgumentError => e
25
+ e.message << "\nListener: #{listener.class}, args: #{args.inspect}"
26
+ raise e
27
+ end
28
28
  end
29
29
 
30
30
  private
@@ -31,7 +31,11 @@ module Gherkin
31
31
 
32
32
  @table_state = :step
33
33
  end
34
-
34
+
35
+ def location(uri, offset)
36
+ @listener.location(uri, offset)
37
+ end
38
+
35
39
  private
36
40
 
37
41
  def method_missing(*event_args)
@@ -0,0 +1,113 @@
1
+ require 'gherkin/native'
2
+ require 'gherkin/parser/row'
3
+
4
+ module Gherkin
5
+ module Parser
6
+ # Adapter from the "raw" Gherkin <tt>Listener</tt> API
7
+ # to the slightly more high-level <tt>Formatter</tt> API,
8
+ # which is easier to implement (less state to keep track of).
9
+ class FormatterListener
10
+ native_impl('gherkin')
11
+
12
+ def initialize(formatter)
13
+ @formatter = formatter
14
+ @comments = []
15
+ @tags = []
16
+ @table = nil
17
+ end
18
+
19
+ def location(uri, offset)
20
+ @uri = uri
21
+ @offset = offset
22
+ end
23
+
24
+ def comment(content, line)
25
+ @comments << content
26
+ end
27
+
28
+ def tag(name, line)
29
+ @tags << name
30
+ end
31
+
32
+ def feature(keyword, name, description, line)
33
+ @formatter.feature(grab_comments!, grab_tags!, keyword, name, description, @uri)
34
+ end
35
+
36
+ def background(keyword, name, description, line)
37
+ @formatter.background(grab_comments!, keyword, name, description, line)
38
+ end
39
+
40
+ def scenario(keyword, name, description, line)
41
+ replay_step_or_examples
42
+ @formatter.scenario(grab_comments!, grab_tags!, keyword, name, description, line)
43
+ end
44
+
45
+ def scenario_outline(keyword, name, description, line)
46
+ replay_step_or_examples
47
+ @formatter.scenario_outline(grab_comments!, grab_tags!, keyword, name, description, line)
48
+ end
49
+
50
+ def examples(keyword, name, description, line)
51
+ replay_step_or_examples
52
+ @examples = [grab_comments!, grab_tags!, keyword, name, description, line]
53
+ end
54
+
55
+ def step(keyword, name, line)
56
+ replay_step_or_examples
57
+ @step = [grab_comments!, keyword, name, line]
58
+ end
59
+
60
+ def row(cells, line)
61
+ @table ||= []
62
+ @table << Row.new(cells, grab_comments!, line)
63
+ end
64
+
65
+ def py_string(py_string, line)
66
+ @py_string = py_string
67
+ end
68
+
69
+ def eof
70
+ replay_step_or_examples
71
+ @formatter.eof
72
+ end
73
+
74
+ private
75
+
76
+ def grab_comments!
77
+ comments = @comments
78
+ @comments = []
79
+ comments
80
+ end
81
+
82
+ def grab_tags!
83
+ tags = @tags
84
+ @tags = []
85
+ tags
86
+ end
87
+
88
+ def grab_table!
89
+ table = @table
90
+ @table = nil
91
+ table
92
+ end
93
+
94
+ def grab_py_string!
95
+ py_string = @py_string
96
+ @py_string = nil
97
+ py_string
98
+ end
99
+
100
+ def replay_step_or_examples
101
+ if(@step)
102
+ multiline_arg = grab_py_string! || grab_table!
103
+ @formatter.step(*(@step + [multiline_arg, nil, nil, nil, nil]))
104
+ @step = nil
105
+ end
106
+ if(@examples)
107
+ @formatter.examples(*(@examples + [grab_table!]))
108
+ @examples = nil
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,102 @@
1
+ require 'json'
2
+ require 'gherkin/i18n'
3
+
4
+ module Gherkin
5
+ module Parser
6
+ class JSONParser
7
+ attr_reader :i18n_language
8
+
9
+ def initialize(listener)
10
+ @listener = listener
11
+ end
12
+
13
+ def parse(src)
14
+ feature = JSON.parse(src)
15
+
16
+ @i18n_language = Gherkin::I18n.get(feature["language"] || "en" )
17
+
18
+ tags_for(feature)
19
+ @listener.feature(keyword_for("feature", feature), feature["name"], line_for(feature)) if feature["name"]
20
+
21
+ if feature["background"]
22
+ @listener.background(keyword_for("background", feature["background"]), feature["background"]["name"], line_for(feature["background"]))
23
+ steps_for(feature["background"])
24
+ end
25
+
26
+ feature["elements"].each do |feature_element|
27
+ parse_element(feature_element)
28
+ end if feature["elements"]
29
+
30
+ @listener.eof
31
+ end
32
+
33
+ private
34
+
35
+ def parse_element(feature_element)
36
+ case feature_element["type"]
37
+ when "Scenario" then parse_scenario(feature_element)
38
+ when "Scenario Outline" then parse_outline(feature_element)
39
+ end
40
+ end
41
+
42
+ def parse_outline(scenario_outline)
43
+ tags_for(scenario_outline)
44
+ @listener.scenario_outline(keyword_for("scenario_outline", scenario_outline), scenario_outline["name"], line_for(scenario_outline) )
45
+ steps_for(scenario_outline)
46
+ scenario_outline["examples"].each do |examples|
47
+ tags_for(examples)
48
+ @listener.examples(keyword_for("examples", examples), examples["name"], line_for(examples))
49
+ rows_for(examples)
50
+ end
51
+ end
52
+
53
+ def parse_scenario(scenario)
54
+ tags_for(scenario)
55
+ @listener.scenario(keyword_for("scenario", scenario), scenario["name"], line_for(scenario))
56
+ steps_for(scenario)
57
+ end
58
+
59
+ def tags_for(element)
60
+ element["tags"].each do |tag|
61
+ @listener.tag(tag, 0)
62
+ end if element["tags"]
63
+ end
64
+
65
+ def steps_for(element)
66
+ element["steps"].each do |step|
67
+ @listener.step(keyword_for("given",step), step["name"], line_for(step))
68
+ py_string_for(step)
69
+ rows_for(step)
70
+ end
71
+ end
72
+
73
+ def py_string_for(element)
74
+ @listener.py_string(element["py_string"], 0) if element["py_string"]
75
+ end
76
+
77
+ def rows_for(element)
78
+ element["table"].each do |row|
79
+ @listener.row(cells_for(row), 0)
80
+ end if element["table"]
81
+ end
82
+
83
+ def cells_for(row)
84
+ row["cells"].inject([]) { |col, ele| col << ele["text"] }
85
+ end
86
+
87
+ def line_for(element)
88
+ if element["line"]
89
+ element["line"]
90
+ elsif element["file_colon_line"]
91
+ element["file_colon_line"].split(':')[1].to_i
92
+ else
93
+ 0
94
+ end
95
+ end
96
+
97
+ def keyword_for(gherkin_keyword, element)
98
+ element["keyword"] || i18n_language.keywords(gherkin_keyword).reject { |kw| kw == "* " }.first
99
+ end
100
+ end
101
+ end
102
+ end
@@ -20,11 +20,15 @@ module Gherkin
20
20
  push_machine(@machine_name)
21
21
  end
22
22
 
23
+ def location(uri, offset)
24
+ @listener.location(uri, offset)
25
+ end
26
+
23
27
  # Doesn't yet fall back to super
24
28
  def method_missing(method, *args)
25
29
  # TODO: Catch exception and call super
26
30
  if(event(method.to_s, args[-1]))
27
- @listener.send(method, *args)
31
+ @listener.__send__(method, *args)
28
32
  end
29
33
  if method == :eof
30
34
  pop_machine
@@ -117,7 +121,8 @@ module Gherkin
117
121
  def transition_table(name)
118
122
  state_machine_reader = StateMachineReader.new
119
123
  lexer = Gherkin::I18n.new('en').lexer(state_machine_reader)
120
- lexer.scan(File.read(File.dirname(__FILE__) + "/#{name}.txt"))
124
+ machine = File.dirname(__FILE__) + "/#{name}.txt"
125
+ lexer.scan(File.read(machine), machine, 0)
121
126
  state_machine_reader.rows
122
127
  end
123
128
 
@@ -128,6 +133,9 @@ module Gherkin
128
133
  @rows = []
129
134
  end
130
135
 
136
+ def location(uri, offset)
137
+ end
138
+
131
139
  def row(row, line_number)
132
140
  @rows << row
133
141
  end
@@ -0,0 +1,15 @@
1
+ require 'gherkin/native'
2
+
3
+ module Gherkin
4
+ module Parser
5
+ class Row
6
+ native_impl('gherkin')
7
+
8
+ attr_reader :cells, :comments, :line
9
+
10
+ def initialize(cells, comments, line)
11
+ @cells, @comments, @line = cells, comments, line
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,6 +2,8 @@ module Gherkin
2
2
  module Rubify
3
3
  if defined?(JRUBY_VERSION)
4
4
  # Translate Java objects to Ruby.
5
+ # This is especially important to convert java.util.List coming
6
+ # from Java and back to a Ruby Array.
5
7
  def rubify(o)
6
8
  if Java.java.util.Collection === o || Array === o
7
9
  o.map{|e| rubify(e)}
@@ -24,7 +24,7 @@ module Gherkin
24
24
  parser = Gherkin::Parser::Parser.new(listener, true, "root")
25
25
  lexer = Gherkin::I18nLexer.new(parser, false)
26
26
  begin
27
- lexer.scan(IO.read(file))
27
+ lexer.scan(IO.read(file), file, 0)
28
28
  rescue => e
29
29
  e.message << " (#{file})"
30
30
  raise e
@@ -10,8 +10,7 @@ module Gherkin
10
10
  purdy = StringIO.new
11
11
  listener = Formatter::PrettyFormatter.new(purdy)
12
12
  scan(file, listener)
13
- purdy.rewind
14
- File.open(file, 'w') {|io| io.write(purdy.read)}
13
+ File.open(file, 'w') {|io| io.write(purdy.string)}
15
14
  end
16
15
  end
17
16
  end
@@ -10,7 +10,7 @@ module Gherkin
10
10
  each do |f|
11
11
  parser = Gherkin::Parser::Parser.new(listener, true)
12
12
  lexer = Gherkin::I18nLexer.new(parser)
13
- lexer.scan(IO.read(f))
13
+ lexer.scan(IO.read(f), f, 0)
14
14
  end
15
15
  puts "Features: #{listener.features}"
16
16
  puts "Scenarios: #{listener.scenarios}"
@@ -20,21 +20,21 @@ module Gherkin
20
20
  def comment(content, line)
21
21
  end
22
22
 
23
- def feature(keyword, name, line)
23
+ def feature(keyword, name, description, line)
24
24
  @features += 1
25
25
  end
26
26
 
27
- def background(keyword, name, line)
27
+ def background(keyword, name, description, line)
28
28
  end
29
29
 
30
- def scenario(keyword, name, line)
30
+ def scenario(keyword, name, description, line)
31
31
  @scenarios += 1
32
32
  end
33
33
 
34
- def scenario_outline(keyword, name, line)
34
+ def scenario_outline(keyword, name, description, line)
35
35
  end
36
36
 
37
- def examples(keyword, name, line)
37
+ def examples(keyword, name, description, line)
38
38
  end
39
39
 
40
40
  def step(keyword, name, line)
@@ -66,7 +66,7 @@ static VALUE rb_eGherkinLexingError;
66
66
  #define PTR_TO(P) (data + lexer->P)
67
67
 
68
68
  #define STORE_KW_END_CON(EVENT) \
69
- store_kw_con(listener, # EVENT, \
69
+ store_multiline_kw_con(listener, # EVENT, \
70
70
  PTR_TO(keyword_start), LEN(keyword_start, PTR_TO(keyword_end - 1)), \
71
71
  PTR_TO(content_start), LEN(content_start, PTR_TO(content_end)), \
72
72
  lexer->current_line, lexer->eol); \
@@ -129,7 +129,7 @@ static VALUE rb_eGherkinLexingError;
129
129
  store_kw_con(listener, "step",
130
130
  PTR_TO(keyword_start), LEN(keyword_start, PTR_TO(keyword_end)),
131
131
  PTR_TO(content_start), LEN(content_start, p),
132
- lexer->current_line, lexer->eol);
132
+ lexer->current_line);
133
133
  }
134
134
 
135
135
  action store_comment_content {
@@ -251,31 +251,59 @@ strip_i(VALUE str, VALUE ary)
251
251
  }
252
252
 
253
253
  static VALUE
254
- multiline_strip(VALUE text, int eol)
254
+ multiline_strip(VALUE text)
255
255
  {
256
256
  VALUE map = rb_ary_new();
257
257
  VALUE split = rb_str_split(text, "\n");
258
258
 
259
259
  rb_iterate(rb_each, split, strip_i, map);
260
260
 
261
- return rb_ary_join(split, rb_str_new2( \
262
- eol == CRLF_FLAG ? CRLF : LF ));
261
+ return split;
263
262
  }
264
263
 
265
264
  static void
266
265
  store_kw_con(VALUE listener, const char * event_name,
267
266
  const char * keyword_at, size_t keyword_length,
268
267
  const char * at, size_t length,
269
- int current_line, int eol)
268
+ int current_line)
270
269
  {
271
270
  VALUE con = Qnil, kw = Qnil;
272
271
  kw = ENCODED_STR_NEW(keyword_at, keyword_length);
273
272
  con = ENCODED_STR_NEW(at, length);
274
- con = multiline_strip(con, eol);
275
273
  rb_funcall(con, rb_intern("strip!"), 0);
276
274
  rb_funcall(listener, rb_intern(event_name), 3, kw, con, INT2FIX(current_line));
277
275
  }
278
276
 
277
+ static void
278
+ store_multiline_kw_con(VALUE listener, const char * event_name,
279
+ const char * keyword_at, size_t keyword_length,
280
+ const char * at, size_t length,
281
+ int current_line, int eol)
282
+ {
283
+ VALUE con = Qnil, kw = Qnil, name = Qnil, desc = Qnil;
284
+
285
+ kw = ENCODED_STR_NEW(keyword_at, keyword_length);
286
+ con = ENCODED_STR_NEW(at, length);
287
+
288
+ VALUE split = multiline_strip(con);
289
+
290
+ name = rb_funcall(split, rb_intern("shift"), 0);
291
+ desc = rb_ary_join(split, rb_str_new2( \
292
+ eol == CRLF_FLAG ? CRLF : LF ));
293
+
294
+ if( name == Qnil )
295
+ {
296
+ name = rb_str_new2("");
297
+ }
298
+ if( rb_funcall(desc, rb_intern("size"), 0) == 0)
299
+ {
300
+ desc = rb_str_new2("");
301
+ }
302
+ rb_funcall(name, rb_intern("strip!"), 0);
303
+ rb_funcall(desc, rb_intern("strip!"), 0);
304
+ rb_funcall(listener, rb_intern(event_name), 4, kw, name, desc, INT2FIX(current_line));
305
+ }
306
+
279
307
  static void
280
308
  store_attr(VALUE listener, const char * attr_type,
281
309
  const char * at, size_t length,
@@ -307,7 +335,7 @@ store_pystring_content(VALUE listener,
307
335
  static void
308
336
  raise_lexer_error(const char * at, int line)
309
337
  {
310
- rb_raise(rb_eGherkinLexingError, "Lexing error on line %d: '%s'.", line, at);
338
+ rb_raise(rb_eGherkinLexingError, "Lexing error on line %d: '%s'. See http://wiki.github.com/aslakhellesoy/gherkin/lexingerror for more information.", line, at);
311
339
  }
312
340
 
313
341
  static int
@@ -361,12 +389,14 @@ static VALUE CLexer_init(VALUE self, VALUE listener)
361
389
  return self;
362
390
  }
363
391
 
364
- static VALUE CLexer_scan(VALUE self, VALUE input)
392
+ static VALUE CLexer_scan(VALUE self, VALUE input, VALUE uri, VALUE offset)
365
393
  {
394
+ VALUE listener = rb_iv_get(self, "@listener");
395
+ rb_funcall(listener, rb_intern("location"), 2, uri, offset);
396
+
366
397
  lexer_state *lexer = NULL;
367
398
  DATA_GET(self, lexer_state, lexer);
368
399
 
369
-
370
400
  VALUE input_copy = rb_str_dup(input);
371
401
 
372
402
  rb_str_append(input_copy, rb_str_new2("\n%_FEATURE_END_%"));
@@ -384,7 +414,6 @@ static VALUE CLexer_scan(VALUE self, VALUE input)
384
414
  const char *p, *pe, *eof;
385
415
  int cs = 0;
386
416
 
387
- VALUE listener = rb_iv_get(self, "@listener");
388
417
  VALUE current_row = Qnil;
389
418
 
390
419
  p = data;
@@ -420,6 +449,6 @@ void Init_gherkin_lexer_<%= @i18n.underscored_iso_code %>()
420
449
  cI18nLexer = rb_define_class_under(mCLexer, "<%= @i18n.underscored_iso_code.capitalize %>", rb_cObject);
421
450
  rb_define_alloc_func(cI18nLexer, CLexer_alloc);
422
451
  rb_define_method(cI18nLexer, "initialize", CLexer_init, 1);
423
- rb_define_method(cI18nLexer, "scan", CLexer_scan, 1);
452
+ rb_define_method(cI18nLexer, "scan", CLexer_scan, 3);
424
453
  }
425
454