HashCode

An organizer of symbols

Archive for November, 2008

Para arquivos offline use a gem Parseline

Friday, November 14th, 2008

Parsear um arquivo texto em Ruby é algo fácil, persistir os dados no banco com Rails é trivial.
Vou demostrar como carregar arquivos csv e arquivos com layout largura fixa no braço, e mais a frente a nova gem Parseline.

Caso 1: Largura fixa

Suponhamos que seja necessário carregar dados de produtos de um arquivo denominado “data.txt” com o conteúdo

000001PRODUTO 1       S 21/11/2008000090.00
000002PRODUTO 2       N 22/11/2008000341.33
000003PRODUTO 3       N 01/11/2008000001.99
000004PRODUTO 4       S 15/11/2008000034.98
000005PRODUTO 5       N 14/11/2008000130.44
000006PRODUTO 6       S 05/11/2008000020.11

sendo o layout definido dessa forma:
- posição 0 até 5 = código do produto 00221
- posiçao 6 até 21 = nome do produto
- posição 22 = ‘S’ se tem em estoque e ‘N’ não tem
- posição 23 = reservado para uso futuro
- posição 24 até 33 = a data do cadastro
- posição 34 até 42 = preço do produto

Nossa tabela/model será Product definida por

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.integer  :code
      t.string  :name
      t.boolean :in_stock
      t.date    :date
      t.float   :price
    end
  end

  def self.down
    drop_table :products
  end
end

Vamos percorrer cada linha do arquivo fazendo parser dos campos, como descrito no layout acima

File.readlines('data.txt').each do |line|
  line.strip! #retirando caracteres não imprimiveis
  #definindo os campos
  code    = line[0..5]
  name    = line[6..21].strip
  in_stock   = line[22..22]
  nothing = line[23..23]
  date    = line[24..33]
  price   = line[34..42]
  #trantando os dados
  in_stock=(in_stock=='S') #=> true se 'S', false se 'N'
  date=date.split(/\//).reverse.join('-') #=> formata data para '2008-11-05'

  #attribuindo os valores no AR
  product=Product.new :code => code,
                      :name => name,
                      :in_stock => in_stock,
                      :date => date,
                      :price => price
  product.save # salvando :)                     

end

Pronto, tá lá no banco.

Caso 2: Arquivos CSV

Arquivos delimitados por um caracter geralmente , ou ;
Esse será o nosso arquivo “data.csv” com o conteúdo

1;PRODUTO 1;S;;21/11/2008;90.00
2;PRODUTO 2;N;;22/11/2008;341.33
3;PRODUTO 3;N;;01/11/2008;1.99
4;PRODUTO 4;S;;15/11/2008;34.98
5;PRODUTO 5;N;;14/11/2008;130.44
6;PRODUTO 6;S;;05/11/2008;20.11

temos o layout para cada linha

código do produto;nome;se tem em estoque;*reservado*;data;preço

Utilizaremos a mesma migration CreateProducts. Vamos ao código

File.readlines("data.csv").each do |line|
  #definindo os campos
  code,name,in_stock,nothing,date,price = line.strip.split(';')

  #trantando 
  in_stock=(in_stock=='S') #true se 'S', false se 'N'
  date=date.split(/\//).reverse.join('-') #formata data para '2008-11-05'

  #atribuindo os valores
  product=Product.new :code => code,
                      :name => name,
                      :in_stock => in_stock,
                      :date => date,
                      :price => price

  product.save                      

end

Nova gem ParseLine

O que ela faz? Nos ajuda a carregar dados externos de arquivos offline de uma forma mais elegante.
Suporta CSV ou largura fixa.

Funcionamento

Basta extender o módulo ParseLine::CSV ou ParseLine::FixedWidth e definir o layout.

Caso 1: Largura fixa

Utilizaremos o módulo ParseLine::FixedWidth com o arquivo “data.txt”, a sintáxe é

  parse.field :nome_do_campo, intervalo, lambda{|campo| fomatador}

Olha como é simples

require 'parseline'

class Product < ActiveRecord::Base
  extend ParseLine::FixedWidth
  fixed_width_layout do |parse|
    parse.field :code , 0..5
    parse.field :name,  6..21
    parse.field :in_stock, 22..22, lambda {|f| f == 'S' }
    parse.field :date , 24..33,    lambda {|d| d.split(/\//).reverse.join('-') }
    parse.field :price, 34..42
  end
end

então posso carregar apenas uma linha

dados=File.readlines("data.txt")
@product=Product.load_line dados[0]
@product.save
#<Product id: nil, code: "1", name: "PRODUTO 1", in_stock: false, date: "2008-11-21", price: 90.0>

ou carregar automáticamente todo arquivo em um array de Product

@products=Product.load_lines "data.txt"

conteúdo de @products

[
#<Product id: 29, code: "1", name: "PRODUTO 1", in_stock: true, date: "2008-11-21", price: 90.0>,
#<Product id: 30, code: "2", name: "PRODUTO 2", in_stock: false, date: "2008-11-22", price: 341.33>,
#<Product id: 31, code: "3", name: "PRODUTO 3", in_stock: false, date: "2008-11-01", price: 1.99>,
#<Product id: 32, code: "4", name: "PRODUTO 4", in_stock: true, date: "2008-11-15", price: 34.98>,
#<Product id: 33, code: "5", name: "PRODUTO 5", in_stock: false, date: "2008-11-14", price: 130.44>,
#<Product id: 34, code: "6", name: "PRODUTO 6", in_stock: true, date: "2008-11-05", price: 20.11>
]


Caso 2: Arquivos CSV

Utilizaremos o módulo ParseLine::CSV com o arquivo “data.csv”, a sintáxe é

  parse.field :nome_do_campo, lambda{|campo| fomatador}

para ignorar um campo como aquele “reservado para uso futuro” basta invocar o método ignore_field

Veja

class Product < ActiveRecord::Base
  extend ParseLine::CSV
  csv_layout :delimiter => ";" do |parse|
    parse.field :code
    parse.field :name
    parse.ignore_field
    parse.field :in_stock, lambda {|f| f == 'S' }
    parse.field :date ,    lambda {|d| d.split(/\//).reverse.join('-') }
    parse.field :price
  end
end

O ParseLine::CSV também tem os métodos Model.load_line e Model.load_lines.
Então temos para uma linha de dados

dados=File.readlines("data.csv")
@product=Product.load_line dados[0]
@product.save
#<Product id: nil, code: "1", name: "PRODUTO 1", in_stock: false, date: "2008-11-21", price: 90.0>

Ou carregar tudo de um array

@products=Product.load_lines "data.txt"


Instalação

#rubyforge
sudo gem install parseline

Se ainda não estiver disponível baixe aqui
Espero que gostem.

ps. em breve no GitHub em inglês

Ops! RailsTree, de novo?

Monday, November 10th, 2008

Puts! Foi mal Akita e Carlos esse projeto é de mil novecentos e Titanic mas obrigado por mencionar. Eu repostei ele sem querer.

O post real está aqui

RGhost 0.8.6 amigo do Prawn e do Ruport

Thursday, November 6th, 2008

Já está disponível RGhost 0.8.6, compatível com Prawn e Ruport, isto é, agora você pode fazer seu benchmark sem precisar desinstalar o RGhost para usar o Prawn ou vise-versa.

A outra novidade é o ganho de 50 milissegundos pra cada template(EPS) renderizado, tal façanha pode não parecer muita coisa mas se seu documento tiver 100 páginas, são 5 segundos a menos(tempo de um gole de cerveja). Isso devido a criação de uma nova forma de vetorizar fontes em Postscript.

Divirtam-se e ganhem dinheiro!