Creating objects from a template

D

David McCabe

Hi folks.

I'm pretty comfortable with Python, and I just decided to give Ruby a
shot. I'm trying to duplicate a nifty method I use in Python to
instantiate objects with properties that are loaded from a file.

My program is a war strategy game, which involves several different
types of artillery units. Each type has properties such as range,
speed, power, etc. I need to load these values from a file.

In python, my parser returns a hash of unit_type objects, where the
names of units are the keys. Each time a unit is instantiated, it
receives a unit_type object as an argument. Now here's the interesting
part: it copies the instance variables from the unit_type into itself.

class Unit(object):
def __init__(self, type):
self.type = type
self.__dict__.update(self.type.__dict__)

There's more to it than that, but that's the interesting part. So now
I can say:

my_new_unit = Unit(types["Panzer"])

And I'll get a Unit object with all the values for Panzers
initialized.

drbrain on #ruby-lang came up with this:

http://rafb.net/paste/results/lgb4xv42.html

It looks like it'll do what I want. There's only one deficiency: I
would have to list out every possible proporty of a unit in two
seperate places in the source. In the python version, I don't have to
list it at all. Whatever properties are in the config file are loaded
into the object.

So, now I'm looking for any pointers on ways to do this.

Thanks!
 
A

Assaph Mehr

David said:
In python, my parser returns a hash of unit_type objects, where the
names of units are the keys. Each time a unit is instantiated, it
receives a unit_type object as an argument. Now here's the interesting
part: it copies the instance variables from the unit_type into itself.

class Unit(object):
def __init__(self, type):
self.type = type
self.__dict__.update(self.type.__dict__)

There's more to it than that, but that's the interesting part. So now
I can say:

my_new_unit = Unit(types["Panzer"])

And I'll get a Unit object with all the values for Panzers
initialized.

drbrain on #ruby-lang came up with this:

http://rafb.net/paste/results/lgb4xv42.html

It looks like it'll do what I want. There's only one deficiency: I
would have to list out every possible proporty of a unit in two
seperate places in the source. In the python version, I don't have to
list it at all. Whatever properties are in the config file are loaded
into the object.

So, now I'm looking for any pointers on ways to do this.

Suppose you have a hash of properties like this:
.. h = { :range => 100, :strength => 50 }

Then you can define at run time accessors for these properties and set
their values like this:
.. class Unit
.. def initialize properties
.. properties.each { |k, v|
.. # define accessor
.. singleton_class.send :attr_accessor, k
.. # initialize with value
.. self.instance_variable_set "@#{k}", v
.. }
.. end
.. end

Test with:
.. a = Unit.new h
.. puts a.range

You don't have to crate the accessors, but ruby treats all instance
variables as private. So unless you define them, you will not be able
to see them from outside the instance.


The #singleton_class method is a convenient shortcut, defined as:
.. class Object
.. def singleton_class
.. class << self; self; end
.. end
.. end


HTH,
Assaph
 
R

Robert Klemme

David McCabe said:
Hi folks.

I'm pretty comfortable with Python, and I just decided to give Ruby a
shot. I'm trying to duplicate a nifty method I use in Python to
instantiate objects with properties that are loaded from a file.

My program is a war strategy game, which involves several different
types of artillery units. Each type has properties such as range,
speed, power, etc. I need to load these values from a file.

In python, my parser returns a hash of unit_type objects, where the
names of units are the keys. Each time a unit is instantiated, it
receives a unit_type object as an argument. Now here's the interesting
part: it copies the instance variables from the unit_type into itself.

class Unit(object):
def __init__(self, type):
self.type = type
self.__dict__.update(self.type.__dict__)

There's more to it than that, but that's the interesting part. So now
I can say:

my_new_unit = Unit(types["Panzer"])

And I'll get a Unit object with all the values for Panzers
initialized.

drbrain on #ruby-lang came up with this:

http://rafb.net/paste/results/lgb4xv42.html

It looks like it'll do what I want. There's only one deficiency: I
would have to list out every possible proporty of a unit in two
seperate places in the source. In the python version, I don't have to
list it at all. Whatever properties are in the config file are loaded
into the object.

So, now I'm looking for any pointers on ways to do this.

You need two simple modifications: make the initialization more general
and add accessors automatically. (see file attached).

If those properties never need to change for instances, then of course
another approach applies (see access-2.rb).

Kind regards

robert
 
N

nobu.nokada

Hi,

At Wed, 9 Feb 2005 15:20:07 +0900,
David McCabe wrote in [ruby-talk:130214]:
In python, my parser returns a hash of unit_type objects, where the
names of units are the keys. Each time a unit is instantiated, it
receives a unit_type object as an argument. Now here's the interesting
part: it copies the instance variables from the unit_type into itself.

class Unit(object):
def __init__(self, type):
self.type = type
self.__dict__.update(self.type.__dict__)

There's more to it than that, but that's the interesting part. So now
I can say:

my_new_unit = Unit(types["Panzer"])

And I'll get a Unit object with all the values for Panzers
initialized.

What about:

module Unit
end

my_new_unit = types["Panzer"].extend(Unit)
 
D

David McCabe

Robert's solution seems to do exactly what I want. Hurrah!

The only thing I'm changing is making a hash with the new classes so
that I can instantiate them easily given their names:

Object.const_set klass_name, klass
kinds[klass_name.intern] = klass

called_for = :LongRange
p kinds[called_for].new

If this isn't the Right Thing in ruby, any pointers on better ways to
do it would be appreciated.

Thank you all!
 
R

Robert Klemme

David McCabe said:
Robert's solution seems to do exactly what I want. Hurrah!

The only thing I'm changing is making a hash with the new classes so
that I can instantiate them easily given their names:

Object.const_set klass_name, klass
kinds[klass_name.intern] = klass

called_for = :LongRange
p kinds[called_for].new

If this isn't the Right Thing in ruby, any pointers on better ways to
do it would be appreciated.

IMHO you don't need the hash. That's duplicating efforts the interpreter
does for you already. You can just use the class name:

# called_for can be a Symbol or String
Object.const_get(called_for).new

Kind regards

robert
 

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
474,167
Messages
2,570,911
Members
47,453
Latest member
MadelinePh

Latest Threads

Top