caligrafo 0.1.3 → 0.1.4

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
@@ -1,6 +1,5 @@
1
- *.sw?
2
- .DS_Store
3
1
  coverage
4
2
  rdoc
5
3
  pkg
6
- test/*.txt
4
+ test/arquivo_gerado.txt
5
+ test/tmp.txt
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{caligrafo}
8
- s.version = "0.1.3"
8
+ s.version = "0.1.4"
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-11-03}
12
+ s.date = %q{2009-11-18}
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,9 +25,15 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "caligrafo.gemspec",
27
27
  "lib/caligrafo.rb",
28
+ "lib/caligrafo/converter.rb",
29
+ "lib/caligrafo/descriptor.rb",
30
+ "lib/caligrafo/fixnum.rb",
31
+ "lib/caligrafo/reader.rb",
32
+ "lib/caligrafo/writer.rb",
28
33
  "test/caligrafo_test.rb",
34
+ "test/example.rb",
35
+ "test/example.txt",
29
36
  "test/formatador_test.rb",
30
- "test/leitura_test.rb",
31
37
  "test/test_helper.rb"
32
38
  ]
33
39
  s.homepage = %q{http://github.com/lucasdecastro/caligrafo}
@@ -39,7 +45,7 @@ Gem::Specification.new do |s|
39
45
  "test/test_helper.rb",
40
46
  "test/caligrafo_test.rb",
41
47
  "test/formatador_test.rb",
42
- "test/leitura_test.rb"
48
+ "test/example.rb"
43
49
  ]
44
50
 
45
51
  if s.respond_to? :specification_version then
@@ -1,460 +1,18 @@
1
- module Caligrafo
2
- def self.included(base)
3
- base.instance_eval do
4
- include InstanceMethods
5
- extend ClassMethods
6
- end
7
- end
8
-
9
- module InstanceMethods
10
- def criar_arquivo(nome_arquivo, &bloco)
11
- arquivo = Arquivo.new(nome_arquivo, bloco)
12
- arquivo.criar_arquivo
13
- nome_arquivo
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
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
210
2
 
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
3
+ require 'caligrafo/descriptor'
4
+ require 'caligrafo/writer'
5
+ require 'caligrafo/reader'
6
+ require 'caligrafo/fixnum'
218
7
 
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
240
- end
241
-
242
- module Formatador
243
- def self.formatadores
244
- @@formatadores ||= {}
245
- end
246
-
247
- def self.registrar(nome, formatador)
248
- self.formatadores[nome] = formatador.new
249
- end
250
-
251
- def self.pesquisar_por_nome(nome)
252
- self.formatadores[nome]
253
- end
254
-
255
- def self.pesquisar_por_nome!(nome)
256
- self.pesquisar_por_nome(nome) or raise FormatadorNaoEncontrado, "O formatador #{nome.inspect} nao foi registrado!"
257
- end
258
-
259
- def self.pesquisar_por_tipo(tipo)
260
- formatador = self.formatadores.values.find { |f| f.tipos.include? tipo }
261
- formatador ||= self.formatadores[:default]
262
- end
263
-
264
- class FormatadorNaoEncontrado < Exception; end
265
-
266
- class Base
267
- attr_reader :tipos, :alinhamento, :preenchimento
268
-
269
- def initialize
270
- @tipos = []
271
- @alinhamento = :esquerda
272
- @preenchimento = ' '
273
- end
274
-
275
- def formatar(valor, opcoes = {})
276
- valor.to_s
277
- end
278
-
279
- def preencher(string, tamanho)
280
- if self.alinhamento == :direita
281
- string = string.rjust tamanho, self.preenchimento
282
- else
283
- string = string.ljust tamanho, self.preenchimento
284
- end
285
-
286
- string = string[0..(tamanho - 1)] if string.size > tamanho
287
- string
288
- end
289
- end
290
-
291
- class Data < Base
292
- def tipos
293
- [Date]
294
- end
295
-
296
- def formatar(valor, opcoes={})
297
- valor.strftime('%Y%m%d')
298
- end
299
- end
300
-
301
- class Numerico < Base
302
- def initialize
303
- @tipos = [Fixnum]
304
- end
305
-
306
- def alinhamento
307
- :direita
308
- end
309
-
310
- def preenchimento
311
- '0'
312
- end
313
- end
314
-
315
- class Decimal < Numerico
316
- def initialize
317
- @tipos = [Float]
318
- end
319
-
320
- def formatar(valor, opcoes = {})
321
- ('%.2f' % valor).gsub('.','')
322
- end
323
- end
324
-
325
- self.registrar :default, Base
326
- self.registrar :alpha, Base
327
- self.registrar :numerico, Numerico
328
- self.registrar :decimal, Decimal
329
- self.registrar :data, Caligrafo::Formatador::Data
330
- end
331
-
332
- class Arquivo
333
- attr_reader :nome, :bloco
334
- attr_accessor :objeto, :indice, :linha, :numero_linha, :file
335
-
336
- def initialize(nome, bloco)
337
- @nome = nome
338
- @bloco = bloco
339
- @linha = ''
340
- @numero_linha = 1
341
- end
342
-
343
- def criar_arquivo
344
- # Se eu simplesmente chamasse o bloco, os método de Arquivo
345
- # não estariam disponíveis. ;)
346
- self.class.send :define_method, :executar_bloco, &bloco
347
-
348
- self.objeto = bloco.binding.send :eval, "self"
349
-
350
- File.open(nome, 'w') do |file|
351
- self.file = file
352
- executar_bloco
353
- end
354
-
355
- nome
356
- end
357
-
358
- def secao(nome, &bloco)
359
- if self.objeto.respond_to? nome
360
- objetos = self.objeto.send nome
361
- objetos = [objetos] if objetos.nil? or !objetos.is_a?(Array)
362
- else
363
- objetos = [objeto]
364
- end
365
-
366
- objetos.each_with_index do |objeto, index|
367
- self.objeto = objeto
368
- self.indice = index
369
- bloco.call objeto
370
- nova_linha
371
- end
372
-
373
- self.objeto = self.bloco.binding.send :eval, "self"
374
- end
375
-
376
- def nova_linha
377
- self.linha = ''
378
- self.numero_linha += 1
379
- self.file.print "\n"
380
- end
381
-
382
- def imprimir(*args)
383
- campo = Campo.new(*args)
384
- valor_campo = campo.valor_para(objeto)
385
-
386
- posicao = campo.posicao
387
- if posicao
388
- valor_campo = valor_campo.rjust(posicao - self.linha.size + 1)
389
- end
390
-
391
- self.linha << valor_campo
392
- self.file.print valor_campo
393
- end
394
- end
395
-
396
- # campo :nome
397
- # campo :profisao, 'Programador'
398
- # campo :idade, 46, :posicao => 12
399
- # campo :tentativas, :alinhamento => :esquerda, :tamanho => 10
400
- # campo :salario, :formato => :decimal
401
- # campo 'FIM'
402
- class Campo
403
- attr_accessor :nome, :valor, :posicao, :formato, :tamanho, :opcoes_para_formatador
404
-
405
- def initialize(*args)
406
- opcoes = (args.last.is_a?(Hash) ? args.pop : {})
407
- @formato = opcoes.delete(:formato)
408
- @posicao = opcoes.delete(:posicao)
409
- @tamanho = opcoes.delete(:tamanho)
410
- @opcoes_para_formatador = opcoes
411
-
412
- if args.first.is_a? Symbol
413
- @nome = args.first
414
- @valor = args[1]
415
- @chamar_metodo = true if args.size == 1
416
- else
417
- @valor = args.first
418
- end
419
- end
420
-
421
- def valor_para(objeto)
422
- valor = if chamar_metodo?
423
- begin
424
- objeto.send(self.nome)
425
- rescue Exception => e
426
- raise "Erro ao chamar #{self.nome.inspect} em #{objeto}: #{e.message}"
427
- end
428
- else
429
- self.valor
430
- end
431
-
432
- formatar(valor)
433
- end
434
-
435
- private
436
- def chamar_metodo?
437
- @chamar_metodo
438
- end
439
- def formatar(valor)
440
- if self.formato
441
- formatador = Formatador.pesquisar_por_nome self.formato
442
- else
443
- formatador = Formatador.pesquisar_por_tipo valor.class
444
- end
445
-
446
- string = formatador.formatar(valor, self.opcoes_para_formatador)
447
- string = formatador.preencher(string, self.tamanho) if self.tamanho
448
- string
449
- end
450
- end
451
- end
8
+ module Caligrafo
9
+ include Writer
10
+ include Reader
452
11
 
453
- # TODO criar arquivo de extensoes.
454
- class ::Fixnum
455
- def espacos
456
- ' ' * self
12
+ def self.included(base)
13
+ base.extend Descriptor
14
+ base.extend Reader
457
15
  end
458
- alias espaco espacos
459
16
  end
460
17
 
18
+ Fixnum.send :include, Caligrafo::Fixnum
@@ -0,0 +1,105 @@
1
+ module Caligrafo
2
+ module Converter
3
+ def self.formatadores
4
+ @@formatadores ||= {}
5
+ end
6
+
7
+ def self.registrar(nome, formatador)
8
+ self.formatadores[nome] = formatador.new
9
+ end
10
+
11
+ def self.pesquisar_por_nome(nome)
12
+ self.formatadores[nome]
13
+ end
14
+
15
+ def self.pesquisar_por_nome!(nome)
16
+ resultado = self.pesquisar_por_nome(nome)
17
+ raise ConverterNotFound, "O formatador #{nome.inspect} nao foi registrado!" unless resultado
18
+ resultado
19
+ end
20
+
21
+ def self.pesquisar_por_tipo(tipo)
22
+ formatador = self.formatadores.values.find { |f| f.tipos.include? tipo }
23
+ formatador ||= self.formatadores[:default]
24
+ end
25
+
26
+ class ConverterNotFound < Exception; end
27
+
28
+ class Base
29
+ attr_reader :tipos, :alinhamento, :preenchimento
30
+
31
+ def initialize
32
+ @tipos = []
33
+ @alinhamento = :esquerda
34
+ @preenchimento = ' '
35
+ end
36
+
37
+ def value_to_string(valor)
38
+ valor.to_s
39
+ end
40
+
41
+ def string_to_value(string)
42
+ string.strip
43
+ end
44
+
45
+ def preencher(string, tamanho)
46
+ if self.alinhamento == :direita
47
+ string = string.rjust tamanho, self.preenchimento
48
+ else
49
+ string = string.ljust tamanho, self.preenchimento
50
+ end
51
+
52
+ string = string[0..(tamanho - 1)] if string.size > tamanho
53
+ string
54
+ end
55
+ end
56
+
57
+ class Data < Base
58
+ def tipos
59
+ [Date]
60
+ end
61
+
62
+ def value_to_string(valor)
63
+ valor.strftime('%Y%m%d')
64
+ end
65
+
66
+ def string_to_value(string)
67
+ Date.strptime string, '%Y%m%d'
68
+ end
69
+ end
70
+
71
+ class Numerico < Base
72
+ def initialize
73
+ @tipos = [Fixnum]
74
+ end
75
+
76
+ def string_to_value(string)
77
+ string.to_i
78
+ end
79
+
80
+ def alinhamento
81
+ :direita
82
+ end
83
+
84
+ def preenchimento
85
+ '0'
86
+ end
87
+ end
88
+
89
+ class Decimal < Numerico
90
+ def initialize
91
+ @tipos = [Float]
92
+ end
93
+
94
+ def value_to_string(valor)
95
+ ('%.2f' % valor).gsub('.','')
96
+ end
97
+ end
98
+
99
+ self.registrar :default, Base
100
+ self.registrar :alpha, Base
101
+ self.registrar :numerico, Numerico
102
+ self.registrar :decimal, Decimal
103
+ self.registrar :data, Data
104
+ end
105
+ end
@@ -0,0 +1,176 @@
1
+ module Caligrafo
2
+ module Descriptor
3
+ def arquivo(&bloco)
4
+ @@arquivo = Arquivo.new
5
+ @@arquivo.executar bloco
6
+ end
7
+
8
+ def estrutura
9
+ @@arquivo
10
+ end
11
+
12
+ module Helpers
13
+ def executar(bloco)
14
+ self.class.send :define_method, :executar_bloco, &bloco
15
+ self.executar_bloco
16
+ end
17
+
18
+ def extrair_opcoes(args)
19
+ args.last.is_a?(Hash) ? args.pop : {}
20
+ end
21
+ end
22
+
23
+ class Arquivo
24
+ include Helpers
25
+
26
+ attr_accessor :bloco
27
+
28
+ def secoes
29
+ @secoes ||= []
30
+ end
31
+
32
+ def secao(nome, &bloco)
33
+ secao = Secao.new(nome, bloco)
34
+ secoes << secao
35
+ secao.executar bloco
36
+ end
37
+ end
38
+
39
+ class Secao
40
+ include Helpers
41
+
42
+ attr_accessor :nome, :bloco
43
+
44
+ def initialize(nome, bloco)
45
+ @nome = nome
46
+ @bloco = bloco
47
+ end
48
+
49
+ def campos
50
+ @campos ||= []
51
+ end
52
+
53
+ def campo(nome, *opcoes)
54
+ campo = Campo.new(self, nome, *opcoes)
55
+ self.campos << campo
56
+ campo
57
+ end
58
+
59
+ def dessa_linha?(linha)
60
+ linha =~ /^#{campos.first.valor_padrao}/
61
+ end
62
+ end
63
+
64
+ class Campo
65
+ include Helpers
66
+
67
+ attr_accessor :secao, :nome, :inicio, :fim, :formatador, :valor_padrao
68
+
69
+ def initialize(secao, nome, *args)
70
+ self.secao = secao
71
+ self.nome = nome
72
+ configura(args)
73
+ end
74
+
75
+ def configura(args)
76
+ opcoes = extrair_opcoes(args)
77
+ self.inicio = opcoes.delete(:inicio)
78
+ self.fim = opcoes.delete(:fim)
79
+ self.formatador = Converter.pesquisar_por_nome(opcoes.delete(:formato))
80
+
81
+ valor_padrao = args.first
82
+ if valor_padrao
83
+ self.valor_padrao = valor_padrao
84
+ opcoes[:tamanho] = self.valor_padrao.to_s.size
85
+ else
86
+ @chamar_metodo = true
87
+ end
88
+
89
+ if opcoes.key? :posicao
90
+ posicao = opcoes.delete(:posicao)
91
+ case posicao
92
+ when Range:
93
+ self.inicio = posicao.first
94
+ self.fim = posicao.last
95
+ when Fixnum:
96
+ self.inicio = calcular_inicio - posicao
97
+ self.fim = self.inicio + posicao
98
+ else
99
+ raise ArgumentError, 'use um Range ou Fixnum para definir a posicao do campo.'
100
+ end
101
+ else
102
+ ajustar_inicio_e_fim(opcoes.delete(:tamanho))
103
+ end
104
+ end
105
+
106
+ def tamanho
107
+ if self.fim and self.inicio
108
+ self.fim - self.inicio + 1 # Inclui o ultimo elemento
109
+ else
110
+ nil
111
+ end
112
+ end
113
+
114
+ def ler(linha)
115
+ fim = self.fim || 0
116
+ substring = linha[(self.inicio - 1)..(fim - 1)]
117
+
118
+ if formatador
119
+ formatador.string_to_value substring
120
+ else
121
+ Converter.formatadores[:default].string_to_value substring
122
+ end
123
+ end
124
+
125
+ def guarda_valor_para(objeto, valor)
126
+ @valor_guardado = formatar(valor)
127
+ end
128
+
129
+ def valor_guardado
130
+ @valor_guardado
131
+ end
132
+
133
+ def valor_para(objeto)
134
+ valor = if objeto.respond_to? self.nome
135
+ objeto.send(self.nome)
136
+ else
137
+ self.valor_padrao
138
+ end
139
+
140
+ formatar(valor)
141
+ rescue => e
142
+ raise "Erro ao preencher valor para #{objeto.inspect} no campo #{self.inspect}: #{e.message}"
143
+ end
144
+
145
+ private
146
+
147
+ def chamar_metodo?
148
+ @chamar_metodo
149
+ end
150
+
151
+ def formatar(valor)
152
+ formatador = self.formatador
153
+ formatador ||= Converter.pesquisar_por_tipo valor.class
154
+
155
+ string = formatador.value_to_string(valor)
156
+ string = formatador.preencher(string, self.tamanho) if self.tamanho
157
+ string
158
+ end
159
+
160
+ def ajustar_inicio_e_fim(tamanho)
161
+ self.inicio ||= calcular_inicio
162
+ self.fim ||= self.inicio + tamanho - 1 if tamanho
163
+ end
164
+
165
+ def calcular_inicio
166
+ campo_anterior = self.secao.campos.last
167
+ if campo_anterior
168
+ ultima_posicao = campo_anterior.fim
169
+ (ultima_posicao ? ultima_posicao + 1 : 1)
170
+ else
171
+ 1
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,8 @@
1
+ module Caligrafo
2
+ module Fixnum
3
+ def espacos
4
+ ' ' * self
5
+ end
6
+ alias espaco espacos
7
+ end
8
+ end
@@ -0,0 +1,52 @@
1
+ module Caligrafo
2
+ module Reader
3
+ def ler_arquivo(nome, &bloco)
4
+ estrutura = (self.is_a?(Class) ? self.estrutura : self.class.estrutura)
5
+ raise 'A estrutura nao foi definida' unless estrutura
6
+
7
+ File.open(nome, 'r') do |file|
8
+ while linha = file.gets
9
+ linha.extend LineExtension
10
+ linha.arquivo = estrutura
11
+ linha.descobrir_secao
12
+ linha.numero = file.lineno
13
+
14
+ bloco.call linha
15
+ end
16
+ end
17
+ end
18
+
19
+ module LineExtension
20
+ attr_accessor :arquivo, :numero
21
+
22
+ def secao
23
+ if @secao
24
+ @secao.nome
25
+ end
26
+ end
27
+
28
+ def secao?(nome_secao)
29
+ @arquivo.secoes.find {|s| s.nome == nome_secao }
30
+ end
31
+
32
+ def ler(nome_campo)
33
+ if @secao
34
+ campo = @secao.campos.find {|c| c.nome == nome_campo }
35
+ campo.ler(self) if campo
36
+ end
37
+ end
38
+
39
+ def ler_campos
40
+ if @secao
41
+ hash = {}
42
+ @secao.campos.each {|c| hash[c.nome] = c.ler(self) }
43
+ hash
44
+ end
45
+ end
46
+
47
+ def descobrir_secao
48
+ @secao = @arquivo.secoes.find {|s| s.dessa_linha?(self) }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,65 @@
1
+ require 'caligrafo/converter'
2
+
3
+ module Caligrafo
4
+ module Writer
5
+ def escrever_arquivo(nome, &bloco)
6
+ raise 'A estrutura nao foi definida' unless self.class.estrutura
7
+
8
+ File.open(nome, 'w') do |file|
9
+ file.extend FileExtension
10
+ file.estrutura = self.class.estrutura
11
+ file.linha = ''
12
+ file.numero_linha = 1
13
+ file.objeto = self
14
+ file.bloco = bloco
15
+
16
+ bloco.call file
17
+ end
18
+
19
+ nome
20
+ end
21
+
22
+ module FileExtension
23
+ attr_accessor :objeto, :indice, :linha, :numero_linha, :bloco, :estrutura, :secao_corrente
24
+
25
+ def secao(nome, &bloco)
26
+ self.secao_corrente = self.estrutura.secoes.find {|secao| secao.nome == nome }
27
+
28
+ if self.objeto.respond_to? nome
29
+ objetos = self.objeto.send nome
30
+ objetos = [objetos] if objetos.nil? or !objetos.is_a?(Array)
31
+ else
32
+ objetos = [objeto]
33
+ end
34
+
35
+ objetos.each_with_index do |objeto, index|
36
+ self.objeto = objeto
37
+ self.indice = index
38
+
39
+ bloco.call objeto
40
+ if self.estrutura
41
+ for campo in self.secao_corrente.campos
42
+ self.print(campo.valor_guardado || campo.valor_para(self.objeto))
43
+ end
44
+ end
45
+
46
+ nova_linha
47
+ end
48
+
49
+ self.objeto = self.bloco.binding.send :eval, "self"
50
+ end
51
+
52
+ def nova_linha
53
+ self.linha = ''
54
+ self.numero_linha += 1
55
+ self.print "\n"
56
+ end
57
+
58
+ # Sobrescreve a definição do arquivo.
59
+ def imprimir(nome_campo, valor)
60
+ campo = self.secao_corrente.campos.find {|campo_pre_definido| campo_pre_definido.nome == nome_campo}
61
+ campo.guarda_valor_para(self.objeto, valor)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,65 +1,85 @@
1
1
  require 'test_helper'
2
- require 'caligrafo'
3
- require 'ostruct'
2
+ require 'example'
4
3
 
4
+ class CaligrafoTest < Test::Unit::TestCase
5
+ def setup
6
+ @portifolio = Portifolio.new :nome => 'Lucas da Silva',
7
+ :idade => 25,
8
+ :salario => 90_000.5,
9
+ :telefones => ['558622223333', '558699991234'],
10
+ :sites => ['Google.com', 'Blip.tv', 'SlideShare.net']
11
+ end
5
12
 
6
- class Portifolio < OpenStruct
7
- include Caligrafo
13
+ def test_writing
14
+ @portifolio.gerar_arquivo 'test/arquivo_gerado.txt'
15
+ assert_file_content 'test/arquivo_gerado.txt', <<-EOF
16
+ 01Lucas da Silva 0259000050 1
17
+ 02Fone#1: 55 86 2222-3333 2
18
+ 02Fone#2: 55 86 9999-1234 3
19
+ 03google.com 4
20
+ 03blip.tv 5
21
+ 03slideshare.net 6
22
+ 04FIM 7
23
+ EOF
24
+ end
8
25
 
9
- def gerar_arquivo(nome_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 1º 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.
17
- end
26
+ def test_numero_linha
27
+ counter = 1
28
+ @portifolio.ler_arquivo 'test/example.txt' do |linha|
29
+ assert counter, linha.numero
30
+ counter += 1
31
+ end
32
+ assert 7, counter
33
+ end
18
34
 
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 (' ').
25
- end
35
+ def test_ler_da_classe
36
+ Portifolio.ler_arquivo 'test/example.txt' do |linha|
37
+ assert true
38
+ break
39
+ end
40
+ end
26
41
 
27
- f.secao :sites do |site|
28
- f.imprimir :downcase # Podemos chamar o método do item nas seções.
29
- f.imprimir :numero_linha, f.numero_linha, :posicao => 100
30
- end
42
+ def test_secao
43
+ @portifolio.ler_arquivo 'test/example.txt' do |linha|
44
+ nome_secao = case linha.numero
45
+ when 1 then :cabecalho
46
+ when 2..3 then :telefones
47
+ when 4..6 then :sites
48
+ when 7 then :rodape
49
+ end
50
+ assert_equal nome_secao, linha.secao, "secao nao eh #{nome_secao.inspect}"
51
+ end
52
+ end
31
53
 
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.
35
- end
54
+ def test_secao?
55
+ @portifolio.ler_arquivo 'test/example.txt' do |linha|
56
+ assert linha.secao?(linha.secao)
36
57
  end
37
58
  end
38
- end
39
59
 
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
60
+ def test_ler_campo
61
+ @portifolio.ler_arquivo 'test/example.txt' do |linha|
62
+ case linha.secao
63
+ when :cabecalho
64
+ assert_equal '01', linha.ler(:tipo)
65
+ assert_equal 'Lucas da Silva', linha.ler(:nome)
66
+ assert_equal 25, linha.ler(:idade)
67
+ end
68
+ end
43
69
  end
44
- end
45
- Caligrafo::Formatador.registrar :fone, Telefone
46
70
 
47
- class CaligrafoTest < Test::Unit::TestCase
48
- def test_gerar_arquivo
49
- pessoa = Portifolio.new :nome => 'Lucas da Silva',
50
- :idade => 25,
51
- :salario => 90_000.5,
52
- :telefones => ['558622223333', '558699991234'],
53
- :sites => ['Google.com', 'Blip.tv', 'SlideShare.net']
54
- pessoa.gerar_arquivo 'test/arquivo_gerado.txt'
55
- assert_file_content 'test/arquivo_gerado.txt', <<-EOF
56
- Lucas da Silva 0259000050 1
57
- Fone#1: 55 86 2222-3333 2
58
- Fone#2: 55 86 9999-1234 3
59
- google.com 4
60
- blip.tv 5
61
- slideshare.net 6
62
- FIM 7
63
- EOF
71
+ def test_description
72
+ arquivo = Portifolio.estrutura
73
+ assert arquivo
74
+
75
+ cabecalho = arquivo.secoes.first
76
+ assert_equal :cabecalho, cabecalho.nome
77
+
78
+ registros = arquivo.secoes.last
79
+ assert_equal :rodape, registros.nome
80
+
81
+ campos = cabecalho.campos
82
+ assert_equal 6, campos.size
64
83
  end
84
+
65
85
  end
@@ -0,0 +1,62 @@
1
+ require 'caligrafo'
2
+ require 'ostruct'
3
+
4
+ class Telefone < Caligrafo::Converter::Base
5
+ def value_to_string(valor)
6
+ valor.to_s.gsub(/(\d\d)(\d\d)(\d\d\d\d)(\d\d\d\d)/,'\1 \2 \3-\4')
7
+ end
8
+ end
9
+
10
+ Caligrafo::Converter.registrar :fone, Telefone
11
+
12
+ class Portifolio < OpenStruct
13
+ include Caligrafo
14
+
15
+ arquivo do
16
+ secao :cabecalho do
17
+ campo :tipo, '01'
18
+ campo :nome, :tamanho => 50
19
+ campo :idade, :tamanho => 3, :formato => :numerico
20
+ campo :salario, :tamanho => 7
21
+ campo :vazio, 7.espacos
22
+ campo :linha
23
+ end
24
+ secao :telefones do
25
+ campo :tipo, '02'
26
+ campo :descricao, :tamanho => 8
27
+ campo :to_s, :tamanho => 59, :formato => :fone
28
+ campo :linha
29
+ end
30
+ secao :sites do
31
+ campo :tipo, '03'
32
+ campo :downcase, :tamanho => 67
33
+ campo :linha
34
+ end
35
+ secao :rodape do
36
+ campo :tipo, '04'
37
+ campo :fim, 'FIM'
38
+ campo :vazio, 64.espacos
39
+ campo :linha
40
+ end
41
+ end
42
+
43
+ def gerar_arquivo(nome_arquivo)
44
+ escrever_arquivo nome_arquivo do |f|
45
+ f.secao :cabecalho do
46
+ f.imprimir :linha, f.numero_linha
47
+ end
48
+ f.secao :telefones do
49
+ f.imprimir :descricao, "Fone##{f.indice + 1}: "
50
+ f.imprimir :linha, f.numero_linha
51
+ end
52
+ f.secao :sites do
53
+ f.imprimir :linha, f.numero_linha
54
+ end
55
+ f.secao :rodape do
56
+ f.imprimir :linha, f.numero_linha
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+
@@ -0,0 +1,7 @@
1
+ 01Lucas da Silva 0259000050 1
2
+ 02Fone#1: 55 86 2222-3333 2
3
+ 02Fone#2: 55 86 9999-1234 3
4
+ 03google.com 4
5
+ 03blip.tv 5
6
+ 03slideshare.net 6
7
+ 04FIM 7
@@ -1,12 +1,11 @@
1
1
  require 'test_helper'
2
2
  require 'caligrafo'
3
- require 'ostruct'
4
3
 
5
4
 
6
5
  class FormatadorTest < Test::Unit::TestCase
7
6
 
8
7
  def test_formatador_default
9
- test_formatador Caligrafo::Formatador::Base, 12, 10, '12 '
8
+ test_formatador Caligrafo::Converter::Base, 12, 10, '12 '
10
9
  end
11
10
 
12
11
  def test_formatador_personalizado
@@ -14,12 +13,12 @@ class FormatadorTest < Test::Unit::TestCase
14
13
  end
15
14
 
16
15
  def test_pesquisar_formatador_inexistente
17
- assert_raise Caligrafo::Formatador::FormatadorNaoEncontrado do
18
- Caligrafo::Formatador.pesquisar_por_nome! :bolinha
16
+ assert_raise Caligrafo::Converter::ConverterNotFound do
17
+ Caligrafo::Converter.pesquisar_por_nome! :bolinha
19
18
  end
20
19
 
21
20
  assert_nothing_raised do
22
- Caligrafo::Formatador.pesquisar_por_nome! :numerico
21
+ Caligrafo::Converter.pesquisar_por_nome! :numerico
23
22
  end
24
23
  end
25
24
 
@@ -27,7 +26,7 @@ class FormatadorTest < Test::Unit::TestCase
27
26
  def test_formatador(formatador, valor_original, tamanho, valor_esperado)
28
27
  f = formatador.new
29
28
 
30
- valor_formatado = f.formatar valor_original
29
+ valor_formatado = f.value_to_string valor_original
31
30
  valor_preenchido = f.preencher valor_formatado, tamanho
32
31
  assert_equal valor_esperado, valor_preenchido
33
32
  end
@@ -28,8 +28,13 @@ class Test::Unit::TestCase
28
28
  begin
29
29
  assert_equal line, lines_of_given_text[index]
30
30
  rescue Test::Unit::AssertionFailedError => e
31
- column = Diff::LCS.diff(line, lines_of_given_text[index]).first.first.position + 1
32
- raise Test::Unit::AssertionFailedError, "Line #{index + 1} dont match on column #{column}:\n#{e.message}"
31
+ given = lines_of_given_text[index]
32
+ if given
33
+ column = Diff::LCS.diff(line, lines_of_given_text[index]).first.first.position + 1
34
+ raise Test::Unit::AssertionFailedError, "Line #{index + 1} dont match on column #{column}:\n#{e.message}"
35
+ else
36
+ raise Test::Unit::AssertionFailedError, "Line #{index + 1} does not exist."
37
+ end
33
38
  end
34
39
  if content.to_a.size < given.read.to_a.size
35
40
  assert_equal content, given.read
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.3
4
+ version: 0.1.4
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-11-03 00:00:00 -03:00
12
+ date: 2009-11-18 00:00:00 -03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,9 +31,15 @@ files:
31
31
  - VERSION
32
32
  - caligrafo.gemspec
33
33
  - lib/caligrafo.rb
34
+ - lib/caligrafo/converter.rb
35
+ - lib/caligrafo/descriptor.rb
36
+ - lib/caligrafo/fixnum.rb
37
+ - lib/caligrafo/reader.rb
38
+ - lib/caligrafo/writer.rb
34
39
  - test/caligrafo_test.rb
40
+ - test/example.rb
41
+ - test/example.txt
35
42
  - test/formatador_test.rb
36
- - test/leitura_test.rb
37
43
  - test/test_helper.rb
38
44
  has_rdoc: true
39
45
  homepage: http://github.com/lucasdecastro/caligrafo
@@ -67,4 +73,4 @@ test_files:
67
73
  - test/test_helper.rb
68
74
  - test/caligrafo_test.rb
69
75
  - test/formatador_test.rb
70
- - test/leitura_test.rb
76
+ - test/example.rb
@@ -1,82 +0,0 @@
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