[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

How to read and write 80 column punched cards with Ruby

Christer Nilsson

9/9/2006 8:42:00 AM

I'm trying to communicate with a bank. Banks are still using the punched
card layout in their files.

Example: 001234567800023420060909
(account=12345678, amount=234 and day=20060909)

I'm trying to make some nice Ruby classes to handle this.

I would like to define the class like this (compare attr)

class Trans < Record
field :account, 10
field :amount, 6
field :lastday, 8
end

This is what I've been able to do:
(There are several problems: As the fields have to be in a certain order
a hash will not work. On the other hand, looping through an array takes
time. Another is the problem of setting an attribute to nil,
trans.account=nil)

class Field
attr_accessor :name, :size, :pad, :value
def initialize(name,size,pad)
@name=name
@size=size
@pad=pad
end
end

class Record
def initialize
@fields = []
end
def field name, size, pad
@fields << Field.new(name.to_s, size, pad)
end
def save s
p=0
@fields.each do |f|
f.value = s[p,f.size]
p += f.size
end
end
def to_s
s=""
@fields.each do |f|
s += f.value.to_s
end
s
end
def method_missing(id, value=nil)
if value==nil then
@fields.each do |f|
return f.value if f.name == id.to_s
end
else
x = id.to_s.gsub("=", "")
@fields.each do |f|
f.value = value if f.name == x
end
end
end
end

class Trans < Record
def initialize
super
field :account, 10
field :amount, 6
field :lastday, 8
end
end

trans = Trans.new
trans.save "001234567800023420060909"

puts trans.account
trans.amount = 120
puts trans

/Christer

--
Posted via http://www.ruby-....

4 Answers

Martin DeMello

9/9/2006 11:02:00 AM

0

On 9/9/06, Christer Nilsson <janchrister.nilsson@gmail.com> wrote:
> I'm trying to communicate with a bank. Banks are still using the punched
> card layout in their files.
>
> Example: 001234567800023420060909
> (account=12345678, amount=234 and day=20060909)
>
> I'm trying to make some nice Ruby classes to handle this.
>
> I would like to define the class like this (compare attr)
>
> class Trans < Record
> field :account, 10
> field :amount, 6
> field :lastday, 8
> end
>
> This is what I've been able to do:
> There are several problems: As the fields have to be in a certain order
> a hash will not work. On the other hand, looping through an array takes
> time.

Could you expand on that a bit further? What are you trying to
optimise for? If you want an O(1) accessor for say, [:account] you
could use one of the several OrderedHash implementations posted here
recently (check google groups - there was a long thread), or simply
maintain a separate hash { :account => 1, :amount => 2, :lastday => 3
} and use that to index into the array of fields.

It seems to me that you could get away with a hash of the form :field
=> [start, end], coupled with an array giving the ordering of the
fields (for when you want to iterate over all the fields), and store
Then you could access an individual field thus:
class Record
def initialize(str)
@record = str
@field_limits = {}
@fields = []
end

def [](field)
start, end = @field_limits[field]
@record[start..end]
end

def []=(field, value)
start, end = @field_limits[field]
value = pad(value, end-start)
@record[start..end] = value
end
end

martin

Alex Fenton

9/9/2006 11:10:00 AM

0

Christer Nilsson wrote:
> I'm trying to communicate with a bank. Banks are still using the punched
> card layout in their files.
>
> Example: 001234567800023420060909
> (account=12345678, amount=234 and day=20060909)

> I would like to define the class like this (compare attr)
>
> class Trans < Record
> field :account, 10
> field :amount, 6
> ...

You're on the right lines, but in your example you're working with instance methods when you want to work with class methods. Define a 'field' *class* method in the Record class, which defines reader and writer *instance* methods. Something like below.

Tip - don't use method_missing unless you really have to; there is often another way, e.g. define_method. Some well-known libraries do it but it really screws up debugging and reflection, and makes method dispatch even slower.

hth
alex

___

class Record
class Field
attr_accessor :name, :length, :position
def initialize
yield self
end
end

def Record.field(field_name, field_length)
@fields ||= []
position = @fields.inject(0) { | sum, f | sum += f.length }
@fields << Field.new do | f |
f.name, f.length, f.position = field_name, field_length, position
end
attr_reader field_name
define_method("#{field_name}=") do | value |
value = value.to_s.rjust(field_length, '0')
instance_variable_set("@#{field_name}", value)
end
end

def Record.fields
@fields
end

def initialize(str)
self.class.fields.each do | field |
send("#{field.name}=", str[field.position, field.length])
end
end

def to_s
self.class.fields.inject('') { | str, f | str << send(f.name) }
end
end

class Transaction < Record
field :account, 10
field :amount, 6
field :lastday, 8
end

trans = Transaction.new("001234567800023420060909")
puts trans # 001234567800023420060909
puts trans.amount.to_i # 234

trans.amount = 120
puts trans # 001234567800012020060909

Christer Nilsson

9/9/2006 8:55:00 PM

0

Thank you Martin and Alex, your suggestions were really helpful!
/Christer

--
Posted via http://www.ruby-....

Christian Neukirchen

9/10/2006 9:27:00 AM

0

Alex Fenton <alex@deleteme.pressure.to> writes:

> Tip - don't use method_missing unless you really have to; there is
> often another way, e.g. define_method. Some well-known libraries do
> it but it really screws up debugging and reflection, and makes
> method dispatch even slower.

It's true that method_missing often is a source of problems and should
be avoided if possible, but with regards to speed, you should
benchmark first before making such as statement. I was surprised when
I benchmarked it some time ago.

> alex
--
Christian Neukirchen <chneukirchen@gmail.com> http://chneuk...