Skip to content

Commit

Permalink
WIP on i18n: re-org of FeatureParser stuff to accommodate Language ob…
Browse files Browse the repository at this point in the history
…j; old tests pass but new do not yet
  • Loading branch information
twcamper committed Jul 3, 2011
1 parent 97c2ab7 commit 9b8687f
Show file tree
Hide file tree
Showing 17 changed files with 363 additions and 230 deletions.
19 changes: 19 additions & 0 deletions fixtures/i18n/no.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# language: no
@i18n
Egenskap: Summering
For å unngå at firmaet går konkurs
Må regnskapsførerere bruke en regnemaskin for å legge sammen tall

Scenario: to tall
Gitt at jeg har tastet inn 5
Og at jeg har tastet inn 7
Når jeg summerer
skal resultatet være 12

Scenario: tre tall
Gitt at jeg har tastet inn 5
Og at jeg har tastet inn 7
Og at jeg har tastet inn 1
Når jeg summerer
skal resultatet være 13

2 changes: 0 additions & 2 deletions lib/cuporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
require 'cuporter/formatters/csv'
require 'cuporter/filter'
require 'cuporter/feature_parser'
require 'cuporter/tag_nodes_parser'
require 'cuporter/node_parser'
require 'cuporter/document'
require 'cuporter/document/assets'
require 'cuporter/document/html_document'
Expand Down
76 changes: 6 additions & 70 deletions lib/cuporter/feature_parser.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
require 'lib/cuporter/feature_parser/language'
require 'lib/cuporter/feature_parser/parser_base'
require 'lib/cuporter/feature_parser/tag_nodes_parser'
require 'lib/cuporter/feature_parser/node_parser'

module Cuporter
class FeatureParser
FEATURE_LINE = /^\s*(Feature:[^#]*)/u
TAG_LINE = /^\s*(@\w.+)/u
SCENARIO_LINE = /^\s*(Scenario:[^#]*)$/u
SCENARIO_OUTLINE_LINE = /^\s*(Scenario Outline:[^#]*)$/u
SCENARIO_SET_LINE = /^\s*(Scenarios:[^#]*)$/u
EXAMPLE_SET_LINE = /^\s*(Examples:[^#]*)$/u
EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/u
PY_STRING_LINE = /^\s*"""\s*$/u

module FeatureParser
# adds a node to the doc for each cucumber '@' tag, populated with features and
# scenarios
def self.tag_nodes(file, report, filter, root_dir)
Expand All @@ -25,64 +20,5 @@ def self.node(file, doc, filter, root_dir)
parser.root = root_dir
parser.parse_feature
end
attr_writer :root

def initialize(file)
@file = file
@current_tags = []
@lines = File.read(@file).split(/\n/)
end

def file_relative_path
@file_relative_path ||= @file.sub(/^.*#{@root}\//,"#{@root}/")
end

def parse_feature
@open_comment_block = false

@lines.each do |line|
next if @open_comment_block && line !~ PY_STRING_LINE

case line
when PY_STRING_LINE
# toggle, to declare the multiline comment 'heredoc' open or closed
@open_comment_block = !@open_comment_block
when TAG_LINE
# may be more than one tag line
@current_tags |= clean_cuke_line($1).split(/\s+/)
when FEATURE_LINE
@feature = new_feature_node(clean_cuke_line($1), file_relative_path)
@current_tags = []
when SCENARIO_LINE
# How do we know when we have read all the lines from a "Scenario Outline:"?
# One way is when we encounter a "Scenario:"
close_scenario_outline

handle_scenario_line(clean_cuke_line($1))
@current_tags = []
when SCENARIO_OUTLINE_LINE
# ... another is when we hit a subsequent "Scenario Outline:"
close_scenario_outline

@scenario_outline = new_scenario_outline_node(clean_cuke_line($1))
@current_tags = []
when EXAMPLE_SET_LINE, SCENARIO_SET_LINE
handle_example_set_line if @example_set

@example_set = new_example_set_node(clean_cuke_line($1))
@current_tags = []
when @example_set && EXAMPLE_LINE
new_example_line(clean_cuke_line($1))
end
end

# EOF is the final way that we know we are finished with a "Scenario Outline"
close_scenario_outline
return @feature
end

def clean_cuke_line(sub_expression)
sub_expression.strip.escape_apostrophe
end
end
end
35 changes: 35 additions & 0 deletions lib/cuporter/feature_parser/language.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License

module Cuporter
module FeatureParser
class Language

def initialize(line_1)
@iso_code = line_1.to_s =~ LANGUAGE_LINE ? $1 : 'en'
end

def feature_pattern
/^\s*(Feature:[^#]*)/u
end
alias :feature_line :feature_pattern

def scenario_pattern
/^\s*(Scenario:[^#]*)$/u
end
alias :scenario_line :scenario_pattern

def scenario_outline_pattern
/^\s*(Scenario Outline:[^#]*)$/u
end
alias :scenario_outline_line :scenario_outline_pattern

def examples_pattern
/^\s*((Scenarios|Examples):[^#]*)$/u
end
alias :examples_line :examples_pattern


LANGUAGE_LINE = /^\s*#\s*language:\s*([\w-]+)/u
end
end
end
64 changes: 64 additions & 0 deletions lib/cuporter/feature_parser/node_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License

module Cuporter
module FeatureParser
class NodeParser < ParserBase

# ++sub_expression++ is the paren group in the regex, dereferenced with $1 in the caller
def new_feature_node(sub_expression, file)
f = Node.new_node(:Feature, @doc, :cuke_name => sub_expression, :tags => @current_tags, :file_path => file)
f.filter = @filter
f
end

def handle_scenario_line(sub_expression)
if @filter.pass?(@current_tags | @feature.tags)
@feature.add_child(Node.new_node(:Scenario, @doc, :cuke_name => sub_expression, :tags => @current_tags))
end
end

def new_scenario_outline_node(sub_expression)
so = Node.new_node(:ScenarioOutline, @doc, :cuke_name => sub_expression, :tags => @current_tags)
so.filter = @filter
so
end

def handle_example_set_line
if @filter.pass?(@feature.tags | @scenario_outline.tags | @example_set.tags)
@scenario_outline.add_child @example_set
end
end

def new_example_set_node(sub_expression)
es = Node.new_node(:Examples, @doc, :cuke_name => sub_expression, :tags => @current_tags)
es.filter = @filter
es
end

def new_example_line(sub_expression)
example_type = :ExampleHeader
# if the example set has a child already, then it must be the header
example_type = :Example if @example_set.has_children?
@example_set.add_child(Node.new_node(example_type, @doc, :cuke_name => sub_expression))
end

def close_scenario_outline
if @scenario_outline
if @example_set
handle_example_set_line
@example_set = nil
end
@feature.add_child(@scenario_outline) if @scenario_outline.has_children?
@scenario_outline = nil
end
end

def initialize(file, doc, filter)
super(file)
@filter = filter
@doc = doc
end

end
end
end
72 changes: 72 additions & 0 deletions lib/cuporter/feature_parser/parser_base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License

module Cuporter
module FeatureParser
class ParserBase
TAG_LINE = /^\s*(@\w.+)/u
EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/u
PY_STRING_LINE = /^\s*"""\s*$/u

def initialize(file)
@file = file
@current_tags = []
@lines = File.read(@file).split(/\n/)
@lang = Language.new(@lines.first)# =~ LANGUAGE_LINE ? $1 : 'en'
end
attr_reader :lang
attr_writer :root

def file_relative_path
@file_relative_path ||= @file.sub(/^.*#{@root}\//,"#{@root}/")
end

def parse_feature
@open_comment_block = false

@lines.each do |line|
next if @open_comment_block && line !~ PY_STRING_LINE

case line
when PY_STRING_LINE
# toggle, to declare the multiline comment 'heredoc' open or closed
@open_comment_block = !@open_comment_block
when TAG_LINE
# may be more than one tag line
@current_tags |= clean_cuke_line($1).split(/\s+/)
when @lang.feature_line
@feature = new_feature_node(clean_cuke_line($1), file_relative_path)
@current_tags = []
when @lang.scenario_line
# How do we know when we have read all the lines from a "Scenario Outline:"?
# One way is when we encounter a "Scenario:"
close_scenario_outline

handle_scenario_line(clean_cuke_line($1))
@current_tags = []
when @lang.scenario_outline_line
# ... another is when we hit a subsequent "Scenario Outline:"
close_scenario_outline

@scenario_outline = new_scenario_outline_node(clean_cuke_line($1))
@current_tags = []
when @lang.examples_line
handle_example_set_line if @example_set

@example_set = new_example_set_node(clean_cuke_line($1))
@current_tags = []
when @example_set && EXAMPLE_LINE
new_example_line(clean_cuke_line($1))
end
end

# EOF is the final way that we know we are finished with a "Scenario Outline"
close_scenario_outline
return @feature
end

def clean_cuke_line(sub_expression)
sub_expression.strip.escape_apostrophe
end
end
end
end
96 changes: 96 additions & 0 deletions lib/cuporter/feature_parser/tag_nodes_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License

module Cuporter
module FeatureParser
class TagNodesParser < ParserBase

# ++sub_expression++ is the paren group in the regex, dereferenced with $1 in the caller
def new_feature_node(sub_expression, file)
{:cuke_name => sub_expression, :tags => @current_tags, :file_path => file}
end

def handle_scenario_line(sub_expression)
if @filter.pass?(@feature[:tags] | @current_tags)
s = {:cuke_name => sub_expression, :tags => @current_tags}

(@feature[:tags] | s[:tags]).each do |tag|
next unless @filter.pass?([tag])
add_scenario(tag, @feature, s)
end
end
end

def new_scenario_outline_node(sub_expression)
{:cuke_name => sub_expression, :tags => @current_tags}
end

def handle_example_set_line
end

def new_example_set_node(sub_expression)
{:cuke_name => sub_expression.to_s.strip, :tags => @current_tags}
end

def new_example_line(sub_expression)
context_tags = (@feature[:tags] | @scenario_outline[:tags] | @example_set[:tags])
if @filter.pass?(context_tags)
e = {:cuke_name => sub_expression}

context_tags.each do |tag|
next unless @filter.pass?([tag])
add_example(tag, @feature, @scenario_outline, @example_set, e)
end
end
end

def close_scenario_outline
if @scenario_outline
if @example_set
@example_set = nil
end
@scenario_outline = nil
end
end
def add_scenario(tag, feature, scenario)
unless ( t = @report.tag_node(tag))
t = @report.add_child(Node.new_node(:tag, @doc, 'cuke_name' => tag))
end
unless ( f = t.feature_node(feature) )
f = t.add_child(Node.new_node(:Feature, @doc, feature))
end
f.add_child(Node.new_node(:Scenario, @doc, scenario))
end

def add_example(tag, feature, scenario_outline, example_set, example)
unless ( t = @report.tag_node(tag))
t = @report.add_child(Node.new_node(:Tag, @doc, 'cuke_name' => tag))
end
unless ( f = t.feature_node(feature) )
f = t.add_child(Node.new_node(:Feature, @doc, feature))
end
unless ( so = f.scenario_outline_node(scenario_outline) )
so = f.add_child(Node.new_node(:ScenarioOutline, @doc, scenario_outline))
end

# The first Example is an ExampleHeader, which does not get counted or
# numbered. If the ExampleSet is new, it has no children, and therefore
# this is the first and should be an ExampleHeader.
example_type = :Example
unless ( es = so.example_set_node(example_set) )
es = so.add_child(Node.new_node(:Examples, @doc, example_set))
example_type = :ExampleHeader
end
es.add_child(Node.new_node(example_type, @doc, example))
end


def initialize(file, report, filter)
super(file)
@filter = filter
@report = report
@doc = report.document
end

end
end
end
Loading

0 comments on commit 9b8687f

Please sign in to comment.