caligrafo 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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