Assigning to dynamic class attributes

A

Allen

Hi!

Ruby n00b here, but experienced in Perl and Python and many others,
looking for guidance in my first program.

I want to build a class mirroring a flat file record that uses column
names as accessor methods, I want to dynamically assign an attribute
from a parsed string and assign it from the postitional list of symbols.

rec = QlbRecord.new(file_line)
rec.dpend += 5
puts rec.address

First, I Set up the class attributes in the class.
@@cols = [ :address, :classes, :status, :dpend, :cremote ]
@@cols.each { |a| attr a,true }

Next, I would populate the fields as such....
arec = record_string.split(/\t/)
@@cols.each { |c| self.c = arec.shift }
where c is the sumbol of the attribute to set. This is wrong.

Am I going in the right direction? It is like using a hash
data[c] = arec.shift
though I want to access the columns through methods (rec.address, etc.)

Is there a class in the library I should be using instead? Or do I
really just need to replicate code (ick!) for each data field?
address = arec.shift
classes = arec.shift
... etc. ...

Also, I don't completely understand how symbols work or why they are
needed.

Following is my class for your inspection. I can kludge something that
works, but I want to learn the *best* method. Thanks!

########################
class QlbRecord
@@cols = [ :address, :classes, :status, :dpend, :cremote ]
@@cols.each { |a| attr a,true }

def initialize(record_string=nil)
from_s(record_string) if record_string
end

def from_s(record_string)
arec = record_string.split(/\t/)
@@cols.each { |c| self.c = arec.shift }
##---------------------> I want it to go to @address, @classes, etc
end

def to_s
str=''
@@cols.each { |c| str += (str>'' ? "\t" : '') + self.c }
##--------------------------------------------> from @address, etc.
str
end
end
 
J

jeem

I'm sure you'll get better sugestions, but this works:

@@cols.each { |c| self.__send__ "#{c.to_s}=".to_sym, arec.shift }
 
D

daz

Allen said:
Hi!

[...]
Following is my class for your inspection. I can kludge something that
works, but I want to learn the *best* method. Thanks!

########################
class QlbRecord
@@cols = [ :address, :classes, :status, :dpend, :cremote ]
@@cols.each { |a| attr a,true }

def initialize(record_string=nil)
from_s(record_string) if record_string
end

def from_s(record_string)
arec = record_string.split(/\t/)
@@cols.each { |c| self.c = arec.shift }
##---------------------> I want it to go to @address, @classes, etc
end

def to_s
str=''
@@cols.each { |c| str += (str>'' ? "\t" : '') + self.c }
##--------------------------------------------> from @address, etc.
str
end
end

If you're going dynamic, the need for attribute accessors is reduced.
Here, instance_variable_get and ..._set are used:


class QlbRecord
@@cols = [ :mad:address, :mad:classes, :mad:status, :mad:dpend, :mad:cremote ]

def initialize(record_string=nil)
from_s(record_string) if record_string
end

def from_s(record_string)
record_string.split(/\t/).each_with_index do |c, ix|
instance_variable_set(@@cols[ix], c)
end
end

def to_s
@@cols.map do |col|
instance_variable_get(col)
end.join("\t")
end
end

qr1 = QlbRecord.new("1 addr aaa\tclasses aa\tst1\tdp1\tcr1")
p qr1
#-> #<QlbRecord:0x2c8b9e8 @address="1 addr aaa", @dpend="dp1",
#-> @status="st1", @classes="classes aa", @cremote="cr1">
p qr1.to_s
#-> "1 addr aaa\tclasses aa\tst1\tdp1\tcr1"


Also, I don't completely understand how symbols work or
why they are needed.


If you use strings to identify fields, each time you write the
field name, a new string is created. Symbols are unique and
memory-efficient.

p 'cremote'.object_id #-> 23356052
p 'cremote'.object_id #-> 23356016
p 'cremote'.object_id #-> 23355992
puts
p :mad:cremote.object_id #-> 3504654
p :mad:cremote.object_id #-> 3504654
p :mad:cremote.object_id #-> 3504654


Hope that's of use.

daz
 
G

Gavin Kistner

If you use strings to identify fields, each time you write the
field name, a new string is created. Symbols are unique and
memory-efficient.

p 'cremote'.object_id #-> 23356052
p 'cremote'.object_id #-> 23356016
p 'cremote'.object_id #-> 23355992
puts
p :mad:cremote.object_id #-> 3504654
p :mad:cremote.object_id #-> 3504654
p :mad:cremote.object_id #-> 3504654

While :mad:cremote is a valid symbol, it sort of implies that the @
symbol is somewhow special for symbols. The corresponding symbol to
"cremote" is :cremote.

irb(main):001:0> a = 'cremote'
=> "cremote"

irb(main):002:0> a.to_sym
=> :cremote

irb(main):003:0> a.to_sym.to_s
=> "cremote"


Note also that symbols are immutable (like a large integer), while
strings are mutable (like an Array). Think of symbols as a unique
identifier that you can pass around in a lightweight and unambiguous
way. For example, symbols are often used for the value of constants:

class Person
MALE = :male
FEMALE = :female
attr_reader :sex
def initialize( sex )
@sex = sex
end
end

pat = Person.new( Person::MALE )
jim = Person.new( Person::MALE )
if jim.sex == Person::FEMALE
# This won't occur
raise "Something went bad!"
end


Compare to this pathological case:

class Person
MALE = 'male'
FEMALE = 'female'
attr_reader :sex
def initialize( sex )
@sex = sex
end
end

pat = Person.new( Person::MALE )
jim = Person.new( Person::MALE )

# Trying to give JUST pat a sex change, the wrong way
pat.sex.sub!( /^/, 'fe' )

if jim.sex == Person::FEMALE
# This will occur!
raise "Something went bad!"
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top