bddgenx 0.1.42 → 0.1.44

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.
@@ -1,4 +1,10 @@
1
1
  # lib/bddgenx/cli.rb
2
+ # encoding: utf-8
3
+ #
4
+ # Este arquivo define a classe Runner (CLI) da gem bddgenx,
5
+ # responsável por orquestrar o fluxo de leitura de histórias,
6
+ # validação, geração de features, steps, backups e exportação de PDFs.
7
+
2
8
  require 'fileutils'
3
9
  require_relative 'utils/parser'
4
10
  require_relative 'generator'
@@ -8,17 +14,23 @@ require_relative 'utils/validator'
8
14
  require_relative 'utils/backup'
9
15
 
10
16
  module Bddgenx
17
+ # Ponto de entrada da gem: coordena todo o processo de geração BDD.
11
18
  class Runner
12
- # Retorna arquivos a processar: usa ARGV ou prompt interativo
19
+ # Seleciona arquivos de entrada para processamento.
20
+ # Se houver argumentos em ARGV, usa-os como nomes de arquivos .txt;
21
+ # caso contrário, exibe prompt interativo para escolha.
22
+ #
23
+ # @param input_dir [String] Diretório onde estão os arquivos .txt de histórias
24
+ # @return [Array<String>] Lista de caminhos para os arquivos a serem processados
13
25
  def self.choose_files(input_dir)
14
- if ARGV.any?
15
- selecionar_arquivos_txt(input_dir)
16
- else
17
- choose_input(input_dir)
18
- end
26
+ ARGV.any? ? selecionar_arquivos_txt(input_dir) : choose_input(input_dir)
19
27
  end
20
28
 
21
- # Seleciona arquivos .txt via argumentos
29
+ # Mapeia ARGV para paths de arquivos .txt em input_dir.
30
+ # Adiciona extensão '.txt' se necessário e filtra arquivos inexistentes.
31
+ #
32
+ # @param input_dir [String] Diretório de entrada
33
+ # @return [Array<String>] Caminhos válidos para processamento
22
34
  def self.selecionar_arquivos_txt(input_dir)
23
35
  ARGV.map do |arg|
24
36
  nome = arg.end_with?('.txt') ? arg : "#{arg}.txt"
@@ -31,26 +43,42 @@ module Bddgenx
31
43
  end.compact
32
44
  end
33
45
 
34
- # Prompt interativo único
46
+ # Exibe prompt interativo para o usuário escolher qual arquivo processar
47
+ # entre todos os .txt disponíveis em input_dir.
48
+ #
49
+ # @param input_dir [String] Diretório de entrada
50
+ # @exit [1] Se nenhum arquivo for encontrado ou escolha inválida
51
+ # @return [Array<String>] Um único arquivo escolhido ou todos se ENTER
35
52
  def self.choose_input(input_dir)
36
53
  files = Dir.glob(File.join(input_dir, '*.txt'))
37
54
  if files.empty?
38
- warn "❌ Não há arquivos .txt no diretório #{input_dir}"
39
- exit 1
55
+ warn "❌ Não há arquivos .txt no diretório #{input_dir}"; exit 1
40
56
  end
41
57
 
42
58
  puts "Selecione o arquivo de história para processar:"
43
- files.each_with_index { |f,i| puts "#{i+1}. #{File.basename(f)}" }
59
+ files.each_with_index { |f, i| puts "#{i+1}. #{File.basename(f)}" }
44
60
  print "Digite o número correspondente (ou ENTER para todos): "
45
61
  choice = STDIN.gets.chomp
62
+
46
63
  return files if choice.empty?
47
64
  idx = choice.to_i - 1
48
- unless idx.between?(0, files.size-1)
65
+ unless idx.between?(0, files.size - 1)
49
66
  warn "❌ Escolha inválida."; exit 1
50
67
  end
51
68
  [files[idx]]
52
69
  end
53
70
 
71
+ # Executa todo o fluxo de geração BDD.
72
+ # - Cria pasta 'input' se não existir
73
+ # - Seleciona arquivos de histórias
74
+ # - Para cada arquivo:
75
+ # - Lê e valida a história
76
+ # - Gera arquivo .feature e salva backup da versão anterior
77
+ # - Gera definitions de steps
78
+ # - Exporta PDFs novos via PDFExporter
79
+ # - Exibe resumo final com estatísticas
80
+ #
81
+ # @return [void]
54
82
  def self.execute
55
83
  input_dir = 'input'
56
84
  Dir.mkdir(input_dir) unless Dir.exist?(input_dir)
@@ -60,6 +88,7 @@ module Bddgenx
60
88
  warn "❌ Nenhum arquivo de história para processar."; exit 1
61
89
  end
62
90
 
91
+ # Inicializa contadores
63
92
  total = features = steps = ignored = 0
64
93
  skipped_steps = []
65
94
  generated_pdfs = []
@@ -71,29 +100,31 @@ module Bddgenx
71
100
 
72
101
  historia = Parser.ler_historia(arquivo)
73
102
  unless Validator.validar(historia)
74
- ignored += 1; puts "❌ Arquivo inválido: #{arquivo}"; next
103
+ ignored += 1
104
+ puts "❌ História inválida: #{arquivo}"
105
+ next
75
106
  end
76
107
 
77
- # Gera feature
108
+ # Geração de feature
78
109
  feature_path, feature_content = Generator.gerar_feature(historia)
79
110
  Backup.salvar_versao_antiga(feature_path)
80
111
  features += 1 if Generator.salvar_feature(feature_path, feature_content)
81
112
 
82
- # Gera steps
113
+ # Geração de steps
83
114
  if StepsGenerator.gerar_passos(feature_path)
84
115
  steps += 1
85
116
  else
86
117
  skipped_steps << feature_path
87
118
  end
88
119
 
89
- # Exporta PDFs
120
+ # Exportação de PDF (apenas novos)
90
121
  FileUtils.mkdir_p('reports')
91
- results = PDFExporter.exportar_todos(only_new: true)
92
- generated_pdfs.concat(results[:generated])
93
- skipped_pdfs.concat(results[:skipped])
122
+ result = PDFExporter.exportar_todos(only_new: true)
123
+ generated_pdfs.concat(result[:generated])
124
+ skipped_pdfs.concat(result[:skipped])
94
125
  end
95
126
 
96
- # Resumo final
127
+ # Exibe relatório final
97
128
  puts "\n✅ Processamento concluído"
98
129
  puts "- Total de histórias: #{total}"
99
130
  puts "- Features geradas: #{features}"
@@ -101,7 +132,7 @@ module Bddgenx
101
132
  puts "- Steps ignorados: #{skipped_steps.size}"
102
133
  puts "- PDFs gerados: #{generated_pdfs.size}"
103
134
  puts "- PDFs já existentes: #{skipped_pdfs.size}"
104
- puts "- Arquivos ignorados: #{ignored}"
135
+ puts "- Histórias ignoradas: #{ignored}"
105
136
  end
106
137
  end
107
138
  end
@@ -1,108 +1,137 @@
1
- require 'fileutils'
2
- require 'strscan' # para usar ::StringScanner
3
- require_relative 'utils/tipo_param'
4
-
5
1
  # lib/bddgenx/steps_generator.rb
2
+ # encoding: utf-8
3
+ #
4
+ # Este arquivo define a classe StepsGenerator, responsável por gerar
5
+ # definições de passos do Cucumber a partir de arquivos .feature.
6
+ # Suporta palavras-chave Gherkin em Português e Inglês e parametriza
7
+ # strings e números conforme necessário.
8
+
9
+ require 'fileutils'
10
+ require 'strscan' # Para uso de StringScanner
6
11
 
7
12
  module Bddgenx
13
+ # Gera arquivos de definições de passos Ruby para Cucumber
14
+ # com base em arquivos .feature.
8
15
  class StepsGenerator
16
+ # Palavras-chave Gherkin em Português usadas em arquivos de feature
17
+ # @return [Array<String>]
9
18
  GHERKIN_KEYS_PT = %w[Dado Quando Então E Mas].freeze
19
+
20
+ # Palavras-chave Gherkin em Inglês usadas em arquivos de feature
21
+ # @return [Array<String>]
10
22
  GHERKIN_KEYS_EN = %w[Given When Then And But].freeze
11
- ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
12
23
 
13
- # Converte texto para camelCase (para nomes de argumentos)
24
+ # Conjunto de todas as palavras-chave suportadas (PT + EN)
25
+ # @return [Array<String>]
26
+ ALL_KEYS = GHERKIN_KEYS_PT + GHERKIN_KEYS_EN
27
+
28
+ # Converte uma string para camelCase, útil para nomes de argumentos
29
+ #
30
+ # @param [String] str Texto de entrada a ser convertido
31
+ # @return [String] Versão em camelCase do texto
14
32
  def self.camelize(str)
15
- parts = str.strip.split(/[^a-zA-Z0-9]+/)
16
- parts.map.with_index { |w, i| i.zero? ? w.downcase : w.capitalize }.join
33
+ partes = str.strip.split(/[^a-zA-Z0-9]+/)
34
+ partes.map.with_index { |palavra, i| i.zero? ? palavra.downcase : palavra.capitalize }.join
17
35
  end
18
36
 
19
- # Gera step definitions a partir de um arquivo .feature
20
- # - "<nome>" => {string}
21
- # - <nome> => {int}
22
- # - "texto" => {string}
23
- # - numeros inteiros ou floats soltos => {int}
24
- # Respeita idioma de entrada (pt/en) para keywords geradas
37
+ # Gera definições de passos a partir de um arquivo .feature
38
+ #
39
+ # @param [String] feature_path Caminho para o arquivo .feature
40
+ # @raise [ArgumentError] Se feature_path não for String
41
+ # @return [Boolean] Retorna true se passos foram gerados, false se não houver passos
25
42
  def self.gerar_passos(feature_path)
26
- raise ArgumentError, "Caminho esperado como String, recebeu #{feature_path.class}" unless feature_path.is_a?(String)
43
+ # Valida tipo de entrada
44
+ unless feature_path.is_a?(String)
45
+ raise ArgumentError, "Caminho esperado como String, recebeu #{feature_path.class}"
46
+ end
47
+
48
+ linhas = File.readlines(feature_path)
27
49
 
28
- lines = File.readlines(feature_path)
29
50
  # Detecta idioma no cabeçalho: "# language: pt" ou "# language: en"
30
- lang = if (m = lines.find { |l| l =~ /^#\s*language:\s*(\w+)/i })
51
+ lang = if (m = linhas.find { |l| l =~ /^#\s*language:\s*(\w+)/i })
31
52
  m[/^#\s*language:\s*(\w+)/i, 1].downcase
32
53
  else
33
54
  'pt'
34
55
  end
35
56
 
36
- pt_to_en = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
37
- en_to_pt = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
57
+ # Mapas de tradução entre PT e EN
58
+ pt_para_en = GHERKIN_KEYS_PT.zip(GHERKIN_KEYS_EN).to_h
59
+ en_para_pt = GHERKIN_KEYS_EN.zip(GHERKIN_KEYS_PT).to_h
38
60
 
39
- # Seleciona apenas linhas de passo
40
- step_lines = lines.map(&:strip).select do |l|
41
- ALL_KEYS.any? { |k| l.start_with?(k + ' ') }
61
+ # Seleciona linhas que começam com palavras-chave Gherkin
62
+ linhas_passos = linhas.map(&:strip).select do |linha|
63
+ ALL_KEYS.any? { |chave| linha.start_with?(chave + ' ') }
42
64
  end
43
- return false if step_lines.empty?
44
65
 
45
- dir = File.join(File.dirname(feature_path), 'steps')
46
- FileUtils.mkdir_p(dir)
47
- file = File.join(dir, "#{File.basename(feature_path, '.feature')}_steps.rb")
66
+ # Se não encontrar passos, retorna false
67
+ return false if linhas_passos.empty?
48
68
 
49
- content = +"# encoding: utf-8\n# Auto-generated step definitions for #{File.basename(feature_path)}\n\n"
69
+ # Cria diretório e arquivo de saída
70
+ dir_saida = File.join(File.dirname(feature_path), 'steps')
71
+ FileUtils.mkdir_p(dir_saida)
72
+ arquivo_saida = File.join(dir_saida, "#{File.basename(feature_path, '.feature')}_steps.rb")
50
73
 
51
- step_lines.each do |line|
52
- # Extrai keyword original e resto do passo
53
- orig_kw, rest = line.split(' ', 2)
54
- # Converte keyword conforme idioma de entrada
55
- kw = case lang
56
- when 'en' then pt_to_en[orig_kw] || orig_kw
57
- else en_to_pt[orig_kw] || orig_kw
58
- end
59
- raw = rest.dup
74
+ # Cabeçalho do arquivo gerado
75
+ conteudo = +"# encoding: utf-8\n"
76
+ conteudo << "# Definições de passos geradas automaticamente para #{File.basename(feature_path)}\n\n"
77
+
78
+ linhas_passos.each do |linha|
79
+ palavra_original, restante = linha.split(' ', 2)
80
+
81
+ # Define palavra-chave no idioma de saída
82
+ chave = case lang
83
+ when 'en' then pt_para_en[palavra_original] || palavra_original
84
+ else en_para_pt[palavra_original] || palavra_original
85
+ end
60
86
 
61
- scanner = ::StringScanner.new(rest)
62
- pattern = ''
63
- tokens = []
87
+ texto_bruto = restante.dup
88
+ scanner = ::StringScanner.new(restante)
89
+ padrao = ''
90
+ tokens = []
64
91
 
65
92
  until scanner.eos?
66
93
  if scanner.check(/"<([^>]+)>"/)
67
94
  scanner.scan(/"<([^>]+)>"/)
68
95
  tokens << scanner[1]
69
- pattern << '{string}'
96
+ padrao << '{string}'
70
97
  elsif scanner.check(/<([^>]+)>/)
71
98
  scanner.scan(/<([^>]+)>/)
72
99
  tokens << scanner[1]
73
- pattern << '{int}'
100
+ padrao << '{int}'
74
101
  elsif scanner.check(/"([^"<>]+)"/)
75
102
  scanner.scan(/"([^"<>]+)"/)
76
103
  tokens << scanner[1]
77
- pattern << '{string}'
104
+ padrao << '{string}'
78
105
  elsif scanner.check(/\d+(?:\.\d+)?/)
79
- num = scanner.scan(/\d+(?:\.\d+)?/)
80
- tokens << num
81
- pattern << '{int}'
106
+ numero = scanner.scan(/\d+(?:\.\d+)?/)
107
+ tokens << numero
108
+ padrao << '{int}'
82
109
  else
83
- pattern << scanner.getch
110
+ padrao << scanner.getch
84
111
  end
85
112
  end
86
113
 
87
- # Escapa aspas no padrão final
88
- safe_pattern = pattern.gsub('"', '\\"')
89
- signature = "#{kw}(\"#{safe_pattern}\")"
114
+ # Escapa aspas no padrão
115
+ padrao_seguro = padrao.gsub('"', '\\"')
116
+ assinatura = "#{chave}(\"#{padrao_seguro}\")"
90
117
 
118
+ # Adiciona parâmetros se existirem tokens
91
119
  if tokens.any?
92
- args = tokens.each_index.map { |i| "args#{i+1}" }.join(', ')
93
- signature += " do |#{args}|"
120
+ argumentos = tokens.each_index.map { |i| "arg#{i+1}" }.join(', ')
121
+ assinatura << " do |#{argumentos}|"
94
122
  else
95
- signature += ' do'
123
+ assinatura << ' do'
96
124
  end
97
125
 
98
- content << signature << "\n"
99
- content << " pending 'Implementar passo: #{raw}'\n"
100
- content << "end\n\n"
126
+ conteudo << "#{assinatura}\n"
127
+ conteudo << " pending 'Implementar passo: #{texto_bruto}'\n"
128
+ conteudo << "end\n\n"
101
129
  end
102
130
 
103
- File.write(file, content)
104
- puts "✅ Steps gerados: #{file}"
131
+ # Escreve arquivo de saída
132
+ File.write(arquivo_saida, conteudo)
133
+ puts "✅ Steps gerados: #{arquivo_saida}"
105
134
  true
106
135
  end
107
136
  end
108
- end
137
+ end
@@ -1,16 +1,31 @@
1
+ # lib/bddgenx/backup.rb
2
+ # encoding: utf-8
3
+ #
4
+ # Este arquivo define a classe Backup, responsável por criar cópias de segurança
5
+ # de arquivos .feature antes de serem sobrescritos.
6
+ # As cópias são salvas em 'reports/backup' com timestamp no nome.
7
+
1
8
  require 'fileutils'
2
9
  require 'time'
3
10
 
4
11
  module Bddgenx
12
+ # Gerencia a criação de backups de arquivos .feature
5
13
  class Backup
14
+ # Salva uma versão antiga de um arquivo .feature em reports/backup,
15
+ # adicionando um timestamp ao nome do arquivo.
16
+ #
17
+ # @param caminho [String] Caminho completo para o arquivo .feature original
18
+ # @return [void]
19
+ # @note Se o arquivo não existir, não faz nada
6
20
  def self.salvar_versao_antiga(caminho)
7
21
  return unless File.exist?(caminho)
8
22
 
9
- pasta = 'reports/backup'
23
+ pasta = 'reports/backup'
10
24
  FileUtils.mkdir_p(pasta)
11
- base = File.basename(caminho, ".feature")
12
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
13
- destino = "reports/backup/#{base}_#{timestamp}.feature"
25
+
26
+ base = File.basename(caminho, '.feature')
27
+ timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
28
+ destino = "reports/backup/#{base}_#{timestamp}.feature"
14
29
 
15
30
  FileUtils.cp(caminho, destino)
16
31
  puts "📦 Backup criado: #{destino}"
@@ -1,30 +1,48 @@
1
1
  # lib/bddgenx/font_loader.rb
2
+ # encoding: utf-8
3
+ #
4
+ # Este arquivo define a classe FontLoader, responsável por localizar e carregar
5
+ # famílias de fontes TrueType para uso com Prawn em geração de PDFs.
6
+ # Busca os arquivos de fonte no diretório assets/fonts dentro da gem.
7
+
2
8
  require 'prawn'
3
- require 'rubygems' # para Gem.loaded_specs
9
+ require 'rubygems' # para Gem.loaded_specs se necessário
4
10
 
5
11
  module Bddgenx
12
+ # Gerencia o carregamento de fontes TTF para os documentos PDF.
6
13
  class FontLoader
7
- # Retorna o diretório assets/fonts dentro da gem, mesmo em dev local
14
+ # Retorna o caminho absoluto para a pasta assets/fonts dentro da gem
15
+ #
16
+ # @return [String] Caminho completo para o diretório de fontes
8
17
  def self.fonts_path
9
- File.expand_path('../../bddgenx/assets/fonts', __dir__)
18
+ File.expand_path('../../assets/fonts', __dir__)
10
19
  end
11
20
 
12
- # Retorna famílias de fontes TrueType para Prawn; vazio se faltar arquivos
21
+ # Cria o hash de famílias de fontes para registro no Prawn
22
+ #
23
+ # Verifica se os arquivos de fonte DejaVuSansMono incluem normal, bold,
24
+ # italic e bold_italic e têm tamanho mínimo aceitável.
25
+ # Se estiverem ausentes ou corrompidas, retorna hash vazio para usar fallback.
26
+ #
27
+ # @return [Hash{String => Hash<Symbol, String>}]
28
+ # - Chave: nome da família ('DejaVuSansMono')
29
+ # - Valor: mapa de estilos (:normal, :bold, :italic, :bold_italic) para os caminhos dos arquivos
13
30
  def self.families
14
31
  base = fonts_path
15
32
  return {} unless Dir.exist?(base)
16
33
 
17
- files = {
34
+ arquivos = {
18
35
  normal: File.join(base, 'DejaVuSansMono.ttf'),
19
36
  bold: File.join(base, 'DejaVuSansMono-Bold.ttf'),
20
37
  italic: File.join(base, 'DejaVuSansMono-Oblique.ttf'),
21
38
  bold_italic: File.join(base, 'DejaVuSansMono-BoldOblique.ttf')
22
39
  }
23
40
 
24
- if files.values.all? { |path| File.file?(path) && File.size(path) > 12 }
25
- { 'DejaVuSansMono' => files }
41
+ # Verifica existência e tamanho mínimo de cada arquivo
42
+ if arquivos.values.all? { |path| File.file?(path) && File.size(path) > 12 }
43
+ { 'DejaVuSansMono' => arquivos }
26
44
  else
27
- warn "⚠️ Fontes DejaVuSansMono ausentes ou corrompidas em #{base}."
45
+ warn "⚠️ Fontes DejaVuSansMono ausentes ou corrompidas em #{base}. Usando fallback Courier."
28
46
  {}
29
47
  end
30
48
  end
@@ -1,25 +1,56 @@
1
+ # lib/bddgenx/parser.rb
2
+ # encoding: utf-8
3
+ #
4
+ # Este arquivo define a classe Parser, responsável por ler e interpretar
5
+ # arquivos de história (.txt), extraindo cabeçalho e blocos de passos e exemplos.
6
+ # Utiliza constantes para identificação de tipos de blocos e suporta idiomas
7
+ # Português e Inglês na marcação de idioma e blocos de exemplos.
8
+
1
9
  module Bddgenx
2
- # Tipos de blocos GUARDADOS, incluindo português EXEMPLO/EXEMPLOS
10
+ # Tipos de blocos reconhecidos na história (.txt), incluindo variações em Português
11
+ # e Inglês para blocos de exemplo.
12
+ # @return [Array<String>]
3
13
  TIPOS_BLOCOS = %w[
4
14
  CONTEXT SUCCESS FAILURE ERROR EXCEPTION
5
15
  VALIDATION PERMISSION EDGE_CASE PERFORMANCE
6
16
  EXEMPLO EXEMPLOS RULE
7
17
  ].freeze
8
18
 
19
+ # Parser de arquivos de história, converte .txt em estrutura de hash
20
+ # com elementos :como, :quero, :para, :idioma e lista de :grupos.
9
21
  class Parser
10
- # Lê arquivo de história (.txt) e retorna hash com atributos e grupos
22
+ # Lê e analisa um arquivo de história, retornando um Hash com a estrutura:
23
+ # {
24
+ # como: String ou nil,
25
+ # quero: String ou nil,
26
+ # para: String ou nil,
27
+ # idioma: 'pt' ou 'en',
28
+ # grupos: [
29
+ # {
30
+ # tipo: String (tipo de bloco),
31
+ # tag: String ou nil (tag opcional após o bloco),
32
+ # passos: Array<String> (linhas de passo),
33
+ # exemplos: Array<String> (linhas de exemplo)
34
+ # },
35
+ # ...
36
+ # ]
37
+ # }
38
+ #
39
+ # @param caminho_arquivo [String] Caminho para o arquivo .txt de história
40
+ # @raise [Errno::ENOENT] Se o arquivo não for encontrado
41
+ # @return [Hash] Estrutura da história pronta para geração de feature
11
42
  def self.ler_historia(caminho_arquivo)
12
- # todas as linhas, mantendo vazias
43
+ # Carrega linhas do arquivo, preservando linhas vazias e encoding UTF-8
13
44
  linhas = File.readlines(caminho_arquivo, encoding: 'utf-8').map(&:rstrip)
14
45
 
15
- # Detecta idioma: aceita "# lang: en" ou "# language: en"
46
+ # Detecta idioma no topo do arquivo: suporta '# lang: <codigo>' ou '# language: <codigo>'
16
47
  idioma = 'pt'
17
48
  if linhas.first =~ /^#\s*lang(?:uage)?\s*:\s*(\w+)/i
18
49
  idioma = Regexp.last_match(1).downcase
19
50
  linhas.shift
20
51
  end
21
52
 
22
- # Extrai Cabeçalho Como/Quero/Para
53
+ # Extrai cabeçalho: linhas que começam com Como/Quero/Para (variações PT/EN)
23
54
  como, quero, para = nil, nil, nil
24
55
  linhas.reject! do |l|
25
56
  if l =~ /^\s*(?:como|eu como|as a)/i && como.nil?
@@ -36,19 +67,27 @@ module Bddgenx
36
67
  end
37
68
  end
38
69
 
39
- historia = { como: como, quero: quero, para: para, idioma: idioma, grupos: [] }
70
+ # Inicializa estrutura da história
71
+ historia = {
72
+ como: como,
73
+ quero: quero,
74
+ para: para,
75
+ idioma: idioma,
76
+ grupos: []
77
+ }
40
78
  exemplos_mode = false
41
79
 
80
+ # Processa cada linha restante para blocos e exemplos
42
81
  linhas.each do |linha|
43
- # Início de bloco de exemplos: [EXEMPLO] ou [EXEMPLOS] ou [EXAMPLES]
82
+ # Início de bloco de exemplos: [EXEMPLO], [EXEMPLOS] ou [EXAMPLES] com tag opcional
44
83
  if linha =~ /^\[(?:EXEMPLO|EXEMPLOS|EXAMPLES)\](?:@(\w+))?$/i
45
84
  exemplos_mode = true
46
- # inicia array se for o primeiro exemplo
85
+ # Cria array de exemplos no último grupo, se ainda não existir
47
86
  historia[:grupos].last[:exemplos] = []
48
87
  next
49
88
  end
50
89
 
51
- # Início de bloco de tipo (SUCCESS, FAILURE, REGRA etc.)
90
+ # Início de bloco com tipo definido em TIPOS_BLOCOS e tag opcional
52
91
  if linha =~ /^\[(#{TIPOS_BLOCOS.join('|')})\](?:@(\w+))?$/i
53
92
  exemplos_mode = false
54
93
  tipo = Regexp.last_match(1).upcase
@@ -57,7 +96,7 @@ module Bddgenx
57
96
  next
58
97
  end
59
98
 
60
- # Atribui linhas ao último grupo
99
+ # Atribui linha ao último bloco, como passo ou exemplo conforme modo
61
100
  next if historia[:grupos].empty?
62
101
  bloco = historia[:grupos].last
63
102
  if exemplos_mode