caligrafo 0.1.1 → 0.1.2

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.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  coverage
4
4
  rdoc
5
5
  pkg
6
+ test/*.txt
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{caligrafo}
8
- s.version = "0.1.1"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Lucas de Castro"]
12
- s.date = %q{2009-10-26}
12
+ s.date = %q{2009-10-30}
13
13
  s.description = %q{DSL para geração de arquivos texto.}
14
14
  s.email = %q{castro.lucas@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -25,8 +25,9 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "caligrafo.gemspec",
27
27
  "lib/caligrafo.rb",
28
- "test/arquivo_esperado.txt",
29
28
  "test/caligrafo_test.rb",
29
+ "test/formatador_test.rb",
30
+ "test/leitura_test.rb",
30
31
  "test/test_helper.rb"
31
32
  ]
32
33
  s.homepage = %q{http://github.com/lucasdecastro/caligrafo}
@@ -36,7 +37,9 @@ Gem::Specification.new do |s|
36
37
  s.summary = %q{DSL para geração de arquivos texto.}
37
38
  s.test_files = [
38
39
  "test/test_helper.rb",
39
- "test/caligrafo_test.rb"
40
+ "test/caligrafo_test.rb",
41
+ "test/formatador_test.rb",
42
+ "test/leitura_test.rb"
40
43
  ]
41
44
 
42
45
  if s.respond_to? :specification_version then
@@ -2,6 +2,7 @@ module Caligrafo
2
2
  def self.included(base)
3
3
  base.instance_eval do
4
4
  include InstanceMethods
5
+ extend ClassMethods
5
6
  end
6
7
  end
7
8
 
@@ -11,6 +12,231 @@ module Caligrafo
11
12
  arquivo.criar_arquivo
12
13
  nome_arquivo
13
14
  end
15
+
16
+ def escrever_arquivo(nome, &bloco)
17
+ estrutura = self.class.instance_variable_get "@arquivo"
18
+
19
+ File.open(nome, 'w') do |file|
20
+ file.extend ArquivoExtensao
21
+ file.estrutura = estrutura
22
+ file.linha = ''
23
+ file.numero_linha = 1
24
+ file.objeto = self
25
+ file.bloco = bloco
26
+
27
+ bloco.call file
28
+ end
29
+
30
+ nome
31
+ end
32
+
33
+ def ler_arquivo(nome, &bloco)
34
+ estrutura = self.class.instance_variable_get "@arquivo"
35
+ raise 'A estrutura nao foi definida' if estrutura.nil?
36
+
37
+ File.open(nome, 'r') do |file|
38
+ while linha = file.gets
39
+ linha.extend LinhaArquivoExtensao
40
+ linha.arquivo = estrutura
41
+ linha.descobrir_secao
42
+
43
+ bloco.call linha
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ module ArquivoExtensao
50
+ attr_accessor :objeto, :indice, :linha, :numero_linha, :bloco, :estrutura
51
+
52
+ def secao(nome, &bloco)
53
+ if self.objeto.respond_to? nome
54
+ objetos = self.objeto.send nome
55
+ objetos = [objetos] if objetos.nil? or !objetos.is_a?(Array)
56
+ else
57
+ objetos = [objeto]
58
+ end
59
+
60
+ objetos.each_with_index do |objeto, index|
61
+ self.objeto = objeto
62
+ self.indice = index
63
+
64
+ bloco.call objeto
65
+ nova_linha
66
+ end
67
+
68
+ self.objeto = self.bloco.binding.send :eval, "self"
69
+ end
70
+
71
+ def nova_linha
72
+ self.linha = ''
73
+ self.numero_linha += 1
74
+ self.print "\n"
75
+ end
76
+
77
+ def imprimir(*args)
78
+ campo = Campo.new(*args)
79
+ valor_campo = campo.valor_para(objeto)
80
+
81
+ posicao = campo.posicao
82
+ if posicao
83
+ valor_campo = valor_campo.rjust(posicao - self.linha.size + 1)
84
+ end
85
+
86
+ self.linha << valor_campo
87
+ self.print valor_campo
88
+ end
89
+ end
90
+
91
+ module LinhaArquivoExtensao
92
+ attr_accessor :arquivo
93
+
94
+ def secao
95
+ if @secao
96
+ @secao.nome
97
+ end
98
+ end
99
+
100
+ def secao?(nome_secao)
101
+ @arquivo.secoes.find {|s| s.nome == nome_secao }
102
+ end
103
+
104
+ def ler(nome_campo)
105
+ if @secao
106
+ campo = @secao.campos.find {|c| c.nome == nome_campo }
107
+ campo.ler(self) if campo
108
+ end
109
+ end
110
+
111
+ def ler_campos
112
+ if @secao
113
+ hash = {}
114
+ @secao.campos.each {|c| hash[c.nome] = c.ler(self) }
115
+ hash
116
+ end
117
+ end
118
+
119
+ def descobrir_secao
120
+ @secao = @arquivo.secoes.find {|s| s.dessa_linha?(self) }
121
+ end
122
+ end
123
+
124
+ module Helpers
125
+ def executar(bloco)
126
+ self.class.send :define_method, :executar_bloco, &bloco
127
+ self.executar_bloco
128
+ end
129
+
130
+ def extrair_opcoes(args)
131
+ args.last.is_a?(Hash) ? args.pop : {}
132
+ end
133
+ end
134
+
135
+ module ClassMethods
136
+
137
+ def arquivo(&bloco)
138
+ @arquivo = Arquivo.new
139
+ @arquivo.executar bloco
140
+ end
141
+
142
+ class Arquivo
143
+ include Caligrafo::Helpers
144
+
145
+ attr_accessor :bloco
146
+
147
+ def secoes
148
+ @secoes ||= []
149
+ end
150
+
151
+ def secao(nome, &bloco)
152
+ secao = Secao.new(nome, bloco)
153
+ secoes << secao
154
+ secao.executar bloco
155
+ end
156
+ end
157
+
158
+ class Secao
159
+ include Caligrafo::Helpers
160
+
161
+ attr_accessor :nome, :bloco
162
+
163
+ def initialize(nome, bloco)
164
+ @nome = nome
165
+ @bloco = bloco
166
+ end
167
+
168
+ def campos
169
+ @campos ||= []
170
+ end
171
+
172
+ def campo(nome, *opcoes)
173
+ campo = Campo.new(self, nome, *opcoes)
174
+ self.campos << campo
175
+ campo
176
+ end
177
+
178
+ def dessa_linha?(linha)
179
+ linha =~ /^#{campos.first.valor_padrao}/
180
+ end
181
+ end
182
+
183
+ class Campo
184
+ include Caligrafo::Helpers
185
+
186
+ attr_accessor :secao, :nome, :inicio, :fim, :formatador, :valor_padrao
187
+
188
+ def initialize(secao, nome, *args)
189
+ self.secao = secao
190
+ self.nome = nome
191
+
192
+ opcoes = extrair_opcoes(args)
193
+ self.inicio = opcoes.delete(:inicio)
194
+ self.fim = opcoes.delete(:fim)
195
+ self.formatador = Formatador.pesquisar_por_nome(opcoes.delete(:formato))
196
+
197
+ if args.first
198
+ self.valor_padrao = args.first
199
+ opcoes[:tamanho] = self.valor_padrao.to_s.size
200
+ end
201
+
202
+ if opcoes.key? :posicao
203
+ posicao = opcoes.delete(:posicao)
204
+ self.inicio = posicao.first
205
+ self.fim = posicao.last
206
+ else
207
+ ajustar_inicio_e_fim(opcoes.delete(:tamanho))
208
+ end
209
+ end
210
+
211
+ def tamanho
212
+ if self.fim and self.inicio
213
+ self.fim - self.inicio + 1 # Inclui o ultimo elemento
214
+ else
215
+ nil
216
+ end
217
+ end
218
+
219
+ def ler(linha)
220
+ fim = self.fim || 0
221
+ linha[(self.inicio - 1)..(fim - 1)]
222
+ end
223
+
224
+ private
225
+ def ajustar_inicio_e_fim(tamanho)
226
+ self.inicio ||= calcular_inicio
227
+ self.fim ||= self.inicio + tamanho - 1 if tamanho
228
+ end
229
+
230
+ def calcular_inicio
231
+ campo_anterior = self.secao.campos.last
232
+ if campo_anterior
233
+ ultima_posicao = campo_anterior.fim
234
+ ultima_posicao + 1
235
+ else
236
+ 1
237
+ end
238
+ end
239
+ end
14
240
  end
15
241
 
16
242
  module Formatador
@@ -66,6 +292,7 @@ module Caligrafo
66
292
  def tipos
67
293
  [Date]
68
294
  end
295
+
69
296
  def formatar(valor, opcoes={})
70
297
  valor.strftime('%Y%m%d')
71
298
  end
@@ -118,7 +345,7 @@ module Caligrafo
118
345
  # não estariam disponíveis. ;)
119
346
  self.class.send :define_method, :executar_bloco, &bloco
120
347
 
121
- self.objeto = bloco.binding.eval "self"
348
+ self.objeto = bloco.binding.send :eval, "self"
122
349
 
123
350
  File.open(nome, 'w') do |file|
124
351
  self.file = file
@@ -143,7 +370,7 @@ module Caligrafo
143
370
  nova_linha
144
371
  end
145
372
 
146
- self.objeto = self.bloco.binding.eval "self"
373
+ self.objeto = self.bloco.binding.send :eval, "self"
147
374
  end
148
375
 
149
376
  def nova_linha
@@ -227,5 +454,6 @@ class ::Fixnum
227
454
  def espacos
228
455
  ' ' * self
229
456
  end
457
+ alias espaco espacos
230
458
  end
231
459
 
@@ -2,68 +2,57 @@ require 'test_helper'
2
2
  require 'caligrafo'
3
3
  require 'ostruct'
4
4
 
5
- class Telefone < Caligrafo::Formatador::Base
6
- def formatar(valor, opcoes={})
7
- valor.gsub(/(\d\d)(\d\d)(\d\d\d\d)(\d\d\d\d)/,'\1 \2 \3-\4')
8
- end
9
- end
10
- Caligrafo::Formatador.registrar :fone, Telefone
11
5
 
12
6
  class Portifolio < OpenStruct
13
7
  include Caligrafo
14
8
 
15
9
  def gerar_arquivo(nome_arquivo)
16
-
17
- criar_arquivo nome_arquivo do
18
- secao :cabecalho do
19
- imprimir :nome, :tamanho => 50 # Textos são alinhados à esquerda.
20
- imprimir :idade, :tamanho => 3 # Números são alinhados à direita com zeros à esquerda.
21
- imprimir :salario # Decimais possuem duas casas decimais.
22
- imprimir 5.espacos # Quando o parâmetro não for um símbolo ele será o conteúdo.
23
- imprimir numero_linha, :posicao => 100 # 'numero_linha' é um método que guarda a linha corrente do arquivo.
10
+ escrever_arquivo nome_arquivo do |f|
11
+ f.secao :cabecalho do
12
+ f.imprimir :nome, :tamanho => 50 # Textos são alinhados à esquerda.
13
+ f.imprimir :idade, :tamanho => 3 # Números são alinhados à direita com zeros à esquerda.
14
+ f.imprimir :salario # Decimais possuem duas casas decimais.
15
+ f.imprimir 5.espacos # Quando o parâmetro não for um símbolo ele será o conteúdo.
16
+ f.imprimir f.numero_linha, :posicao => 100 # 'numero_linha' é um método que guarda a linha corrente do arquivo.
24
17
  end
25
18
 
26
- secao :telefones do |telefone| # telefones é um método. Escreve uma linha pora cada objeto retornado.
27
- imprimir :descricao, "Fone##{indice + 1}: " # Valor fixo sendo usado como 2º parâmetro.
28
- # O 1º fica sendo usado apenas para descrição.
29
- # 'indice' é um método que guarda o indice do elemento no array.
30
- imprimir telefone, :formato => :fone # Usando um formatador personalizado.
31
- imprimir numero_linha, :posicao => 100 # posicao é usado para pular para uma coluna. Até lá tudo será vazio (' ').
19
+ f.secao :telefones do |telefone| # telefones é um método. Escreve uma linha pora cada objeto retornado.
20
+ f.imprimir :descricao, "Fone##{f.indice + 1}: " # Valor fixo sendo usado como 2º parâmetro.
21
+ # O 1º fica sendo usado apenas para descrição.
22
+ # 'indice' é um método que guarda o indice do elemento no array.
23
+ f.imprimir telefone, :formato => :fone # Usando um formatador personalizado.
24
+ f.imprimir f.numero_linha, :posicao => 100 # posicao é usado para pular para uma coluna. Até lá tudo será vazio (' ').
32
25
  end
33
26
 
34
- secao :sites do |site|
35
- imprimir :downcase # Podemos chamar o método do item nas seções.
36
- imprimir numero_linha, :posicao => 100
27
+ f.secao :sites do |site|
28
+ f.imprimir :downcase # Podemos chamar o método do item nas seções.
29
+ f.imprimir f.numero_linha, :posicao => 100
37
30
  end
38
31
 
39
- secao :rodape do
40
- imprimir 'FIM' # Uma nova linha é criada sempre que saimos ou entramos numa seção.
41
- imprimir numero_linha, :posicao => 100 # Como não definimos o tamanho, ficará alinhado à esquerda, mesmo sendo número.
32
+ f.secao :rodape do
33
+ f.imprimir 'FIM' # Uma nova linha é criada sempre que saimos ou entramos numa seção.
34
+ f.imprimir f.numero_linha, :posicao => 100 # Como não definimos o tamanho, ficará alinhado à esquerda, mesmo sendo número.
42
35
  end
43
36
  end
44
37
  end
45
38
  end
46
39
 
47
- class CaligrafoTest < Test::Unit::TestCase
48
- def setup
49
- @target = 'test/arquivo_gerado.txt'
50
- end
51
-
52
- def teardown
53
- File.delete @target rescue nil
40
+ class Telefone < Caligrafo::Formatador::Base
41
+ def formatar(valor, opcoes={})
42
+ valor.gsub(/(\d\d)(\d\d)(\d\d\d\d)(\d\d\d\d)/,'\1 \2 \3-\4') if valor
54
43
  end
44
+ end
45
+ Caligrafo::Formatador.registrar :fone, Telefone
55
46
 
56
- def test_gerar_arquivo_texto
47
+ class CaligrafoTest < Test::Unit::TestCase
48
+ def test_gerar_arquivo
57
49
  pessoa = Portifolio.new :nome => 'Lucas da Silva',
58
50
  :idade => 25,
59
51
  :salario => 90_000.5,
60
52
  :telefones => ['558622223333', '558699991234'],
61
53
  :sites => ['Google.com', 'Blip.tv', 'SlideShare.net']
62
- pessoa.gerar_arquivo @target
63
-
64
- nome_arquivo_esperado = 'test/arquivo_esperado.txt'
65
- File.open(nome_arquivo_esperado, 'w') do |file|
66
- file.puts <<-EOF
54
+ pessoa.gerar_arquivo 'test/arquivo_gerado.txt'
55
+ assert_file_content 'test/arquivo_gerado.txt', <<-EOF
67
56
  Lucas da Silva 0259000050 1
68
57
  Fone#1: 55 86 2222-3333 2
69
58
  Fone#2: 55 86 9999-1234 3
@@ -72,17 +61,5 @@ blip.tv
72
61
  slideshare.net 6
73
62
  FIM 7
74
63
  EOF
75
- end
76
-
77
- assert_equal_files nome_arquivo_esperado, @target
78
- end
79
-
80
- def test_pesquisar_formatador_inexistente
81
- assert_raise Caligrafo::Formatador::FormatadorNaoEncontrado do
82
- Caligrafo::Formatador.pesquisar_por_nome! :bolinha
83
- end
84
- assert_nothing_raised do
85
- Caligrafo::Formatador.pesquisar_por_nome! :numerico
86
- end
87
64
  end
88
65
  end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+ require 'caligrafo'
3
+ require 'ostruct'
4
+
5
+
6
+ class FormatadorTest < Test::Unit::TestCase
7
+
8
+ def test_formatador_default
9
+ test_formatador Caligrafo::Formatador::Base, 12, 10, '12 '
10
+ end
11
+
12
+ def test_formatador_personalizado
13
+ test_formatador Telefone, '558632323344', 15, '55 86 3232-3344'
14
+ end
15
+
16
+ def test_pesquisar_formatador_inexistente
17
+ assert_raise Caligrafo::Formatador::FormatadorNaoEncontrado do
18
+ Caligrafo::Formatador.pesquisar_por_nome! :bolinha
19
+ end
20
+
21
+ assert_nothing_raised do
22
+ Caligrafo::Formatador.pesquisar_por_nome! :numerico
23
+ end
24
+ end
25
+
26
+ private
27
+ def test_formatador(formatador, valor_original, tamanho, valor_esperado)
28
+ f = formatador.new
29
+
30
+ valor_formatado = f.formatar valor_original
31
+ valor_preenchido = f.preencher valor_formatado, tamanho
32
+ assert_equal valor_esperado, valor_preenchido
33
+ end
34
+
35
+ end
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+ require 'caligrafo'
3
+
4
+
5
+ class LeituraTest < Test::Unit::TestCase
6
+
7
+ class Exemplo
8
+ include Caligrafo
9
+
10
+ attr_accessor :posicao, :nome, :situacao
11
+
12
+ arquivo do
13
+ secao :cabecalho do
14
+ campo :fixo, '01'
15
+ campo :com_tamanho, :tamanho => 5
16
+ campo :com_fim, :fim => 10
17
+ campo :com_range, :posicao => 11..15
18
+ campo :com_inicio, :inicio => 20, :formato => :decimal
19
+ end
20
+
21
+ secao :corpo do
22
+ campo :vazio
23
+ end
24
+ end
25
+
26
+ def smile
27
+ ':)'
28
+ end
29
+
30
+ def ler(nome_arquivo)
31
+ ler_arquivo nome_arquivo do |linha|
32
+ if linha.secao? :cabecalho
33
+ self.posicao = linha.ler(:fixo)
34
+ self.nome = linha.ler_campos[:com_tamanho]
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def test_ler_arquivo
41
+ arquivo = 'test/tmp.txt'
42
+ File.open(arquivo, 'w') do |file|
43
+ file.puts <<-EOF
44
+ 0100005:) 150
45
+ EOF
46
+ end
47
+
48
+ exemplo = Exemplo.new
49
+ exemplo.ler(arquivo)
50
+ assert_equal '01', exemplo.posicao
51
+ assert_equal '00005', exemplo.nome
52
+ end
53
+
54
+ def test_dsl_com_informacoes_do_arquivo
55
+ arquivo = Exemplo.instance_variable_get "@arquivo"
56
+ assert arquivo
57
+
58
+ cabecalho = arquivo.secoes.first
59
+ assert_equal :cabecalho, cabecalho.nome
60
+
61
+ registros = arquivo.secoes.last
62
+ assert_equal :corpo, registros.nome
63
+
64
+ campos = cabecalho.campos
65
+ assert_equal 5, campos.size
66
+
67
+ test_campo campos[0], :nome => :fixo, :inicio => 1, :fim => 2, :tamanho => 2, :valor_padrao => '01'
68
+ test_campo campos[1], :nome => :com_tamanho, :inicio => 3, :fim => 7, :tamanho => 5
69
+ test_campo campos[2], :nome => :com_fim, :inicio => 8, :fim => 10, :tamanho => 3
70
+ test_campo campos[3], :nome => :com_range, :inicio => 11, :fim => 15, :tamanho => 5
71
+ test_campo campos[4], :nome => :com_inicio, :inicio => 20, :fim => nil, :tamanho => nil
72
+
73
+ assert_equal Caligrafo::Formatador::Decimal, campos[4].formatador.class
74
+ end
75
+
76
+ private
77
+ def test_campo(campo, comparacoes = {})
78
+ for campo_do_campo, valor_esperado in comparacoes
79
+ assert_equal valor_esperado, campo.send(campo_do_campo), "campo #{campo.nome} nao bateu no #{campo_do_campo}"
80
+ end
81
+ end
82
+ end
@@ -17,20 +17,22 @@ class Test::Unit::TestCase
17
17
  expected = File.new(expected, 'r') if expected.is_a? String
18
18
  given = File.new(given, 'r') if given.is_a? String
19
19
 
20
- expected_text = expected.read
21
- given_text = given.read
20
+ assert_file_content given, expected.read
21
+ end
22
22
 
23
- lines_of_given_text = given_text.lines.to_a
24
-
25
- expected_text.each_with_index do |line, index|
23
+ def assert_file_content(file, content)
24
+ given = File.new(file, 'r') if file.is_a? String
25
+ lines_of_given_text = given.read.lines.to_a
26
+
27
+ content.each_with_index do |line, index|
26
28
  begin
27
29
  assert_equal line, lines_of_given_text[index]
28
30
  rescue Test::Unit::AssertionFailedError => e
29
31
  column = Diff::LCS.diff(line, lines_of_given_text[index]).first.first.position + 1
30
32
  raise Test::Unit::AssertionFailedError, "Line #{index + 1} dont match on column #{column}:\n#{e.message}"
31
33
  end
32
- if expected_text.to_a.size < given_text.to_a.size
33
- assert_equal expected_text, given_text
34
+ if content.to_a.size < given.read.to_a.size
35
+ assert_equal content, given.read
34
36
  end
35
37
  end
36
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caligrafo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas de Castro
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-26 00:00:00 -03:00
12
+ date: 2009-10-30 00:00:00 -03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,8 +31,9 @@ files:
31
31
  - VERSION
32
32
  - caligrafo.gemspec
33
33
  - lib/caligrafo.rb
34
- - test/arquivo_esperado.txt
35
34
  - test/caligrafo_test.rb
35
+ - test/formatador_test.rb
36
+ - test/leitura_test.rb
36
37
  - test/test_helper.rb
37
38
  has_rdoc: true
38
39
  homepage: http://github.com/lucasdecastro/caligrafo
@@ -65,3 +66,5 @@ summary: "DSL para gera\xC3\xA7\xC3\xA3o de arquivos texto."
65
66
  test_files:
66
67
  - test/test_helper.rb
67
68
  - test/caligrafo_test.rb
69
+ - test/formatador_test.rb
70
+ - test/leitura_test.rb
@@ -1,7 +0,0 @@
1
- Lucas da Silva 0259000050 1
2
- Fone#1: 55 86 2222-3333 2
3
- Fone#2: 55 86 9999-1234 3
4
- google.com 4
5
- blip.tv 5
6
- slideshare.net 6
7
- FIM 7