Class: Bddgenx::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/parser.rb

Overview

Parser de arquivos de história, converte .txt em estrutura de hash com elementos :como, :quero, :para, :idioma e lista de :grupos.

Class Method Summary collapse

Class Method Details

.ler_historia(caminho_arquivo) ⇒ Hash

Lê e analisa um arquivo de história, retornando um Hash com a estrutura: {

como:       String ou nil,
quero:      String ou nil,
para:      String ou nil,
idioma:     'pt' ou 'en',
grupos: [
  {
    tipo:     String (tipo de bloco),
    tag:      String ou nil (tag opcional após o bloco),
    passos:   Array<String> (linhas de passo),
    exemplos: Array<String> (linhas de exemplo)
  },
  ...
]

}

Parameters:

  • caminho_arquivo (String)

    Caminho para o arquivo .txt de história

Returns:

  • (Hash)

    Estrutura da história pronta para geração de feature

Raises:

  • (Errno::ENOENT)

    Se o arquivo não for encontrado



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/parser.rb', line 41

def self.ler_historia(caminho_arquivo)
  # Carrega linhas do arquivo, preservando linhas vazias e encoding UTF-8
  linhas = File.readlines(caminho_arquivo, encoding: 'utf-8').map(&:rstrip)
  # parser.rb (durante leitura do .txt)
  primeira_linha = File.readlines(caminho_arquivo).first
  idioma_forcado = primeira_linha&.match(/^#\s*lang(?:uage)?\s*:\s*(\w+)/i)&.captures&.first

  # Detecta idioma no topo do arquivo: suporta '# lang: <codigo>' ou '# language: <codigo>'
  idioma = 'pt'
  if linhas.first == idioma_forcado
    idioma = Regexp.last_match(1).downcase
    linhas.shift
  end

  # Extrai cabeçalho: linhas que começam com Como/Quero/Para (variações PT/EN)
  como, quero, para = nil, nil, nil
  linhas.reject! do |l|
    if l =~ /^\s*(?:como|eu como|as a)/i && como.nil?
      como = l
      true
    elsif l =~ /^\s*(?:quero|eu quero|quero que)/i && quero.nil?
      quero = l
      true
    elsif l =~ /^\s*(?:para|para eu|para que)/i && para.nil?
      para = l
      true
    else
      false
    end
  end

  # Inicializa estrutura da história
  historia = {
    como:   como,
    quero:  quero,
    para:   para,
    idioma: idioma,
    grupos: []
  }
  exemplos_mode = false

  # Processa cada linha restante para blocos e exemplos
  linhas.each do |linha|
    # Início de bloco de exemplos: [EXEMPLO], [EXEMPLOS] ou [EXAMPLES] com tag opcional
    if linha =~ /^\[(?:EXEMPLO|EXEMPLOS|EXAMPLES)\](?:@([\w\-]+))?$/i
      exemplos_mode = true
      # Cria array de exemplos no último grupo, se ainda não existir
      historia[:grupos].last[:exemplos] = []
      next
    end

    # Início de bloco com tipo definido em TIPOS_BLOCOS e tag opcional
    if linha =~ /^\[(#{TIPOS_BLOCOS.join('|')})\](?:@([\w\-]+))?$/i
      exemplos_mode = false
      tipo = Regexp.last_match(1).upcase
      tag  = Regexp.last_match(2)
      historia[:grupos] << { tipo: tipo, tag: tag, passos: [], exemplos: [] }
      next
    end

    # Atribui linha ao último bloco, como passo ou exemplo conforme modo
    next if historia[:grupos].empty?
    bloco = historia[:grupos].last
    if exemplos_mode
      bloco[:exemplos] << linha
    else
      bloco[:passos]   << linha
    end
  end

  historia
end