Dynamically created attributes

P

Peter Marks

I'd like to specify a quantity of object attributes when creating an
instance of an object. For example, I would like to create a Grid object
with 4 different 'row' attributes like this: Grid.new(4). Here's some
dirty code that does that:

class Grid
#Creates row1, row2, etc. depending on row arg
def initialize(rows)
row_count = 1
rows.times do
instance_eval "@row#{row_count} = 'xxx'"
row_count += 1
end
end
end

It's dirty, but it creates the attributes. I don't see how to
dynamically create accessor methods for all these attributes though. Any
suggestions? I sense there's a better way to do this.
 
S

Stefano Crocco

Alle luned=C3=AC 12 gennaio 2009, Peter Marks ha scritto:
I'd like to specify a quantity of object attributes when creating an
instance of an object. For example, I would like to create a Grid object
with 4 different 'row' attributes like this: Grid.new(4). Here's some
dirty code that does that:

class Grid
#Creates row1, row2, etc. depending on row arg
def initialize(rows)
row_count =3D 1
rows.times do
instance_eval "@row#{row_count} =3D 'xxx'"
row_count +=3D 1
end
end
end

It's dirty, but it creates the attributes. I don't see how to
dynamically create accessor methods for all these attributes though. Any
suggestions? I sense there's a better way to do this.

Well, the first thing I can think of is to replace your n instance variable=
s=20
with a single instance variable @rows which will be an array and contain th=
e=20
rows. Something like this:

class Grid

attr_reader :rows
def initialize(rows)
@rows =3D Array.new(rows){ 'xxx' }
end

end

If you don't want to make the array availlable from outside the instance, y=
ou
can skip the attr_readr line and provide methods to directly access the row=
s.=20
=46or example, you can define [] and []=3D methods as array does:

class Grid

def [](idx)
@rows[idx]
end

def []=3D(idx, value)
@rows[idx] =3D value
end

end

If you truly want to use an instance variable for each row, see the=20
documentation about instance_variable_set and look at the section "Object=20
specific classes" in the "Classes and Objects" chapter of the online editio=
n=20
of the Pickaxe book
(http://www.ruby-doc.org/docs/ProgrammingRuby/html/classes.html)

I hope this helps

Stefano
 
J

Jesús Gabriel y Galán

I'd like to specify a quantity of object attributes when creating an
instance of an object. For example, I would like to create a Grid object
with 4 different 'row' attributes like this: Grid.new(4). Here's some
dirty code that does that:

class Grid
#Creates row1, row2, etc. depending on row arg
def initialize(rows)
row_count = 1
rows.times do
instance_eval "@row#{row_count} = 'xxx'"
row_count += 1
end
end
end

It's dirty, but it creates the attributes. I don't see how to
dynamically create accessor methods for all these attributes though. Any
suggestions? I sense there's a better way to do this.

Maybe this is cleaner:

class Object
def singleton_class
class << self; self; end
end
end

class Grid
def initialize rows=0
rows.times do |i|
instance_variable_set "@row#{i}", "initial value"
singleton_class.instance_eval {attr_accessor "row#{i}"}
end
end
end

irb(main):020:0> a = Grid.new 3
=> #<Grid:0xb7c02008 @row0="initial value", @row2="initial value",
@row1="initial value">
irb(main):021:0> a.methods.grep /row/
=> ["row0", "row0=", "row1", "row1=", "row2", "row2="]

Hope this helps,

Jesus.
 
J

Jesús Gabriel y Galán

I'd like to specify a quantity of object attributes when creating an
instance of an object. For example, I would like to create a Grid object
with 4 different 'row' attributes like this: Grid.new(4). Here's some
dirty code that does that:

class Grid
#Creates row1, row2, etc. depending on row arg
def initialize(rows)
row_count =3D 1
rows.times do
instance_eval "@row#{row_count} =3D 'xxx'"
row_count +=3D 1
end
end
end

It's dirty, but it creates the attributes. I don't see how to
dynamically create accessor methods for all these attributes though. Any
suggestions? I sense there's a better way to do this.

Maybe this is cleaner:

class Object
def singleton_class
class << self; self; end
end
end

class Grid
def initialize rows=3D0
rows.times do |i|
instance_variable_set "@row#{i}", "initial value"
singleton_class.instance_eval {attr_accessor "row#= {i}"}
end
end
end

irb(main):020:0> a =3D Grid.new 3
=3D> #<Grid:0xb7c02008 @row0=3D"initial value", @row2=3D"initial value",
@row1=3D"initial value">
irb(main):021:0> a.methods.grep /row/
=3D> ["row0", "row0=3D", "row1", "row1=3D", "row2", "row2=3D"]

Ooops, my solution starts with 0, while you wanted to start with 1.
Anyway, an easy change. Nevertheless, I agree with Stefano that this
would be better off implemented as an array, instead of having
individual variables for each "field".

Jesus.
 
P

Peter Marks

Thanks for the help guys! The arrays sound like a much more suitable way
around this problem. Overthought this one.

Cheers!

Peter
 

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

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top