[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Can you rewrite this in a better way?

Bob Sidebotham

10/1/2004 1:06:00 AM

I would like to define a method that can be used to compactly define
some items of interest (here called "fields") for subclasses. It's used
like this:

class B < A
fields :a, :b, :c
end

class C < A
fields :x, :y
end

p B.new.fields => [:a, :b, :c]
p C.new.fields => [:x, :y]

Here are two definitions for class A that support these semantics:

---#1------------------------------------------------

class A
def self.fields(*fields)
@fields = fields
def fields
self.class.getFields
end
end

private
def self.getFields
@fields
end
end

---#2------------------------------------------------

class A
def self.fields(*fields)
eval "def fields
[#{fields.collect{|f| ':' + f.to_s}.join(',')}]
end"
end
end

-----------------------------------------------------

Is there a better way (more efficient, shorter and/or clearer) to do this?

Thanks!
Bob Sidebotham

9 Answers

Ara.T.Howard

10/1/2004 2:23:00 AM

0

Markus

10/1/2004 3:34:00 AM

0

No idea if it's "better" but here's one I use:

class Module
#
# To automate the reading and writing of objects
#
def columns
@columns ||= []
end
private
def field(*ids)
@columns ||= []
ids.each { |id|
id = id.to_s
attr_accessor id unless id =~ /\[/ or
instances_respond_to? id+'='
@columns << id
}
end
end



On Thu, 2004-09-30 at 18:10, Bob Sidebotham wrote:
> I would like to define a method that can be used to compactly define
> some items of interest (here called "fields") for subclasses. It's used
> like this:
>
> class B < A
> fields :a, :b, :c
> end
>
> class C < A
> fields :x, :y
> end
>
> p B.new.fields => [:a, :b, :c]
> p C.new.fields => [:x, :y]
>
> Here are two definitions for class A that support these semantics:
>
> ---#1------------------------------------------------
>
> class A
> def self.fields(*fields)
> @fields = fields
> def fields
> self.class.getFields
> end
> end
>
> private
> def self.getFields
> @fields
> end
> end
>
> ---#2------------------------------------------------
>
> class A
> def self.fields(*fields)
> eval "def fields
> [#{fields.collect{|f| ':' + f.to_s}.join(',')}]
> end"
> end
> end
>
> -----------------------------------------------------
>
> Is there a better way (more efficient, shorter and/or clearer) to do this?
>
> Thanks!
> Bob Sidebotham
>



Chris Morris

10/1/2004 4:43:00 AM

0

> Is there a better way (more efficient, shorter and/or clearer) to do this?

What about this:

---------------------------------------------- Object#instance_variables
obj.instance_variables -> anArray
------------------------------------------------------------------------
Returns an array of instance variable names for the receiver. Note
that simply defining an accessor does not create the corresponding
instance variable.
class Fred
attr_accessor :a1
def initialize
@iv = 3
end
end
Fred.new.instance_variables #=> ["@iv"]


--
Chris
http:/...


Robert Klemme

10/1/2004 8:01:00 AM

0


"Bob Sidebotham" <bob@windsong.bc.ca> schrieb im Newsbeitrag
news:c427d.152105$%S.128398@pd7tw2no...
> I would like to define a method that can be used to compactly define
> some items of interest (here called "fields") for subclasses. It's used
> like this:
>
> class B < A
> fields :a, :b, :c
> end
>
> class C < A
> fields :x, :y
> end
>
> p B.new.fields => [:a, :b, :c]
> p C.new.fields => [:x, :y]
>
> Here are two definitions for class A that support these semantics:
>
> ---#1------------------------------------------------
>
> class A
> def self.fields(*fields)
> @fields = fields
> def fields
> self.class.getFields
> end
> end
>
> private
> def self.getFields
> @fields
> end
> end
>
> ---#2------------------------------------------------
>
> class A
> def self.fields(*fields)
> eval "def fields
> [#{fields.collect{|f| ':' + f.to_s}.join(',')}]
> end"
> end
> end
>
> -----------------------------------------------------
>
> Is there a better way (more efficient, shorter and/or clearer) to do
this?

There are numerous alternatives:

class B
attr_accessor :a, :b, :c
end

class B < Struct.new(:a, :b, :c)
end

Also you can use Class#inherited(cl) to do some magic on a sub class. For
an example (not exactly what you want, but you get the picture):

module WithFields
def fields(*a)
@fields = a unless a.empty? ; @fields
end
end

class Base
extend WithFields

def self.inherited(cl)
cl.extend WithFields
def cl.inherited(cl2) superclass.inherited(cl2) end
end
end

class Sub < Base
fields :a, :b, :c
end

class Sub2 < Sub
fields :x, :y
end

p Sub.fields
p Sub2.fields

Kind regards

robert

Pit Capitain

10/1/2004 3:54:00 PM

0

Bob Sidebotham schrieb:
> I would like to define a method that can be used to compactly define
> some items of interest (here called "fields") for subclasses.
>
> (...)
>
> Here are two definitions for class A that support these semantics:
>
> ---#1------------------------------------------------
>
> (...)
>
> ---#2------------------------------------------------
>
> class A
> def self.fields(*fields)
> eval "def fields
> [#{fields.collect{|f| ':' + f.to_s}.join(',')}]
> end"
> end
> end
>
> -----------------------------------------------------
>
> Is there a better way (more efficient, shorter and/or clearer) to do this?

#2 could be implemented as:

class A
def self.fields(*fields)
define_method(:fields) { fields }
end
end

Here you don't need to build a textual version of the fields array.

Regards,
Pit


Bob Sidebotham

10/1/2004 7:03:00 PM

0

Bob Sidebotham wrote:
> I would like to define a method that can be used to compactly define
> some items of interest (here called "fields") for subclasses. It's used
> like this:

Thanks for all the responses. I especially liked:

class A
def self.fields(*f); @fields||=f; end
def fields; self.class.fields; end
end

and

class A
def self.fields(*fields)
define_method(:fields) { fields }
end
end

The first solution is very clever, but perhaps a little obfuscated. The
second is quite direct--I didn't know about define_method. I hope this
bleeding edge stuff made it into the new pickaxe book.

Thanks again,
Bob

Robert Klemme

10/1/2004 9:54:00 PM

0


"Bob Sidebotham" <bob@windsong.bc.ca> schrieb im Newsbeitrag
news:yRh7d.561483$M95.370027@pd7tw1no...
> Bob Sidebotham wrote:
>> I would like to define a method that can be used to compactly define some
>> items of interest (here called "fields") for subclasses. It's used like
>> this:
>
> Thanks for all the responses. I especially liked:
>
> class A
> def self.fields(*f); @fields||=f; end
> def fields; self.class.fields; end
> end
>
> and
>
> class A
> def self.fields(*fields)
> define_method(:fields) { fields }
> end
> end
>
> The first solution is very clever, but perhaps a little obfuscated. The
> second is quite direct--I didn't know about define_method. I hope this
> bleeding edge stuff made it into the new pickaxe book.
>
> Thanks again,
> Bob

One thing I find irritating about your approach: you define fields on class
level but query them on instance level. As long as "fields" is just a list
of symbols I'd prefer to deal with them solely on class level. Because
that's where it belongs.

If OTOH you want this mechanism to do something like attr_accessor (i.e.
define something that affects instances' state) then you will access them on
instance level but the query ("which fields are there?") still belongs to
the class IMHO.

Note also that you may want to freeze your fields array especially when
accessing it via instances because clients still can modify the array
arbitrarily.

Kind regards

robert

Bob Sidebotham

10/2/2004 2:17:00 AM

0

Robert Klemme wrote:

> One thing I find irritating about your approach: you define fields on
> class level but query them on instance level. As long as "fields" is
> just a list of symbols I'd prefer to deal with them solely on class
> level. Because that's where it belongs.
>
> If OTOH you want this mechanism to do something like attr_accessor (i.e.
> define something that affects instances' state) then you will access
> them on instance level but the query ("which fields are there?") still
> belongs to the class IMHO.
>
> Note also that you may want to freeze your fields array especially when
> accessing it via instances because clients still can modify the array
> arbitrarily.
>
> Kind regards
>
> robert
>

Thanks for the observation. You may be right that keeping levels
separate would be a good idea. Still, if I hand you an object, and tell
you that I want you to do something with a set of items that are somehow
associated with the object (which in this example, I've called
"fields"), and if I tell you that you can get a list of those fields by
calling some particular function, why should I ALSO have to tell you HOW
to call the function (i.e. foo.fields vs. foo.class.fields). Doesn't
this break encapsulation by forcing the caller to know too much about
the internals of the receiver?

In this case, I think it's a minor point, anyway: the two classes are
very closely related and not intended to work independently.

I think part of the issue is that the right answer to a question like
this perhaps depends upon the application. So without knowing more about
the application, and the context within which all this happens, I'm not
sure you can say that one pattern is necessarily better than the other.

Bob


Robert Klemme

10/2/2004 10:36:00 AM

0


"Bob Sidebotham" <bob@windsong.bc.ca> schrieb im Newsbeitrag
news:Dco7d.162456$%S.13340@pd7tw2no...

> Thanks for the observation. You may be right that keeping levels separate
> would be a good idea. Still, if I hand you an object, and tell you that I
> want you to do something with a set of items that are somehow associated
> with the object (which in this example, I've called "fields"), and if I
> tell you that you can get a list of those fields by calling some
> particular function, why should I ALSO have to tell you HOW to call the
> function (i.e. foo.fields vs. foo.class.fields). Doesn't this break
> encapsulation by forcing the caller to know too much about the internals
> of the receiver?

Yeah, I guess you are right. In that case, defining those fields at class
level is just an optimization of the implementation. In this case I'd
definitely freeze the array (or return a copy of it, if you need to be able
to change the set of fields of a class) to avoid unwanted effects. Because
the info is stored in the class instance unwanted modification will do more
harm than if it was stored on instance level: *all* instances of the class
will then have their "fields" property changed.

> In this case, I think it's a minor point, anyway: the two classes are very
> closely related and not intended to work independently.
>
> I think part of the issue is that the right answer to a question like this
> perhaps depends upon the application. So without knowing more about the
> application, and the context within which all this happens, I'm not sure
> you can say that one pattern is necessarily better than the other.

That's definitely true!

Kind regards

robert