Relationship between classes

L

Luke A. Kanies

Hi all,

I think this is a ruby question, as my problem is written in ruby, but it
is mostly just a programming question.

I have two types of classes, objects and operations. Each object has one
or more operation class associated with it (meaning that these are
operations which can be executed on the objectgs). When an object
instance is created, it is created with one or more attributes, and as
many operations as can be successfully run will also be created.

For instance, given a File operation, and chmod, chown, and chgrp
operations, if I create a File instance with only ownership information,
then I can only create an instance of the chmod operation.

Now, to the problem: I am have serious difficulty coming up with a good
way to store these relationships. It's a one-to-many, in that each object
class is related to many operations. It would be easy if I were willing
to just let the operations call the objects directly:

chmod(file.path, file.owner) # or whatever

But because some operations might be associated with more than one object
(e.g., chmod would be associated with both Files and Directories), I can't
afford to assume that much about the objects. What I want to do is
somehow store the association information with the object, like this:

...
:chown => [
[:eek:wner, :file],
proc { |object|
Blink::Operation::Chown.new(
:eek:wner => proc { |object| object[:uid] },
:file => proc { |object| object[:file] }
)
}
],
...

This stores my three key pieces of information: What the object calls the
operation (so it knows how to execute it on itself later), what attributes
are necessary in order to create an instance of the operation, and how to
actuallly instantiate the operation.

I am currently storing all of these in a big hash in each object class
definition.

This seems really inelegant, though, and it causes some weird side effects
in my thinking.

Is there some better way to store these relationships, without causing
leaking of the object interface into the operation definition?

Much thanks,
Luke
 
D

David A. Black

Hi --

...
:chown => [
[:eek:wner, :file],
proc { |object|
Blink::Operation::Chown.new(
:eek:wner => proc { |object| object[:uid] },
:file => proc { |object| object[:file] }
)
}
],
...

This stores my three key pieces of information: What the object calls the
operation (so it knows how to execute it on itself later), what attributes
are necessary in order to create an instance of the operation, and how to
actuallly instantiate the operation.

I am currently storing all of these in a big hash in each object class
definition.

This seems really inelegant, though, and it causes some weird side effects
in my thinking.

Is there some better way to store these relationships, without causing
leaking of the object interface into the operation definition?

Maybe create some classes to wrap all of this in, so that you would
then do something like:

obj.add_operation:)chown) do |op|
op.attributes = [:eek:wner,:file]
op.action = lambda {|object| .... }
...
end

and so on. That might scale more nicely, and allow you to easily add
ways to query and view things as you go.


David
 
R

Robert Klemme

Luke A. Kanies said:
Hi all,

I think this is a ruby question, as my problem is written in ruby, but it
is mostly just a programming question.

I have two types of classes, objects and operations. Each object has one
or more operation class associated with it (meaning that these are
operations which can be executed on the objectgs). When an object
instance is created, it is created with one or more attributes, and as
many operations as can be successfully run will also be created.

For instance, given a File operation, and chmod, chown, and chgrp
operations, if I create a File instance with only ownership information,
then I can only create an instance of the chmod operation.

I've tried to translate your requirements and this is what I came up with
(note the two ways to execute an operation):

class ItemClass
attr :eek:perations

def initialize
@operations = {}
end

def add_operation(name, *attrs, &code)
@operations[name.to_sym] = Operation.new(*attrs, &code)
self
end

def create(attributes)
inst = ItemInstance.new(self, attributes)

@operations.each do |name, op|
if op.mandatory_attributes.all? {|attr| attributes.has_key? attr}
inst.operations[name] = op
end
end

inst
end
end

class ItemInstance
attr_accessor :attributes, :eek:perations

def initialize(item_class, attributes)
@item_class = item_class
@attributes = attributes
@operations = {}
end

def execute(op)
operation = @operations[op.to_sym]
raise NameError, "No such operation: #{op}" unless operation
instance_eval &operation.code
end

def execute2(op)
operation = @operations[op.to_sym]
raise NameError, "No such operation: #{op}" unless operation
operation.code.call(*operation.mandatory_attributes.map {|at|
attributes[at]})
end
end


class Operation
attr_reader :mandatory_attributes, :code

def initialize(*attributes, &code)
@mandatory_attributes = attributes
@code = code
end
end

# ----------------------------------------------

cl = ItemClass.new
cl.add_operation:)print_name, :name) do
puts self.attributes[:name]
end

cl.add_operation:)print_name2, :name) do |name|
puts name
end

cl.add_operation:)print_size, :size) do
puts self.attributes[:size]
end

inst = cl.create:)name => "foo")
inst.execute :print_name # "foo"
inst.execute2 :print_name2 # "foo"
inst.execute :print_size # NameError


Alternatively you might be able to use Ruby classes as classes and Ruby
instances as instances. That would have taken me some more thinking, but
it might be more elegant.

Kind regards

robert
 
R

Robert Klemme

Now here's the class version - couldn't resist... :)


class ItemClass
def self.new
cl = Class.new(self)
cl.instance_variable_set "@operations", {}

class <<cl
attr_reader :eek:perations

def add_operation(name, *attrs, &code)
@operations[name.to_sym] = Operation.new(*attrs, &code)
self
end

def new(attributes)
inst = allocate
s_class = class<<inst ; self ; end

attributes.each do |name, val|
s_class.instance_eval do
define_method name.to_sym do val end
end
end

@operations.each do |name, op|
if op.mandatory_attributes.all? {|attr| attributes.has_key?
attr}
s_class.instance_eval do
define_method name.to_sym, &op.code
end
end
end

inst
end
end

cl
end

end

class Operation
attr_reader :mandatory_attributes, :code

def initialize(*attributes, &code)
@mandatory_attributes = attributes
@code = code
end
end

# ----------------------------------------------

cl = ItemClass.new

cl.add_operation:)print_name, :name) do
puts self.name
end

cl.add_operation:)print_size, :size) do
puts self.attributes[:size]
end

inst = cl.new:)name => "foo")
inst.print_name # "foo"
inst.print_size # NameError


Kind regards

robert
 
L

Luke A. Kanies

Maybe create some classes to wrap all of this in, so that you would
then do something like:

obj.add_operation:)chown) do |op|
op.attributes = [:eek:wner,:file]
op.action = lambda {|object| .... }
...
end

and so on. That might scale more nicely, and allow you to easily add
ways to query and view things as you go.

Hmm. I'll look into that; after sending my email out last night, I think
I may have been able to see the problem a bit more simply (as usually
happens). I think it would be a bit easier to wrap the data in these
simple methods, but the methods would have to be called on the class
itself, not on instances or any such thing.

I'll think about it over the weekend, I guess.

Thanks,
Luke

--
Secondly, Latin is a so-called "dead language." It takes a lot to
kill a language. There are countries the size of my kitchen that have
their own healthy languages. Clearly, if Latin was useful in its
normal form, it would be alive today. Therefore the language must be
defective. I don't see much risk in changing it. What's the worst
thing that could happen -- Latin will become unpopular?
-- from the DNRC Newsletter, by Scott Adams
 

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,776
Messages
2,569,602
Members
45,185
Latest member
GluceaReviews

Latest Threads

Top