Can you rewrite this in a better way?

B

Bob Sidebotham

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
 
A

Ara.T.Howard

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

i don't know if it's any better, but:

harp:~ > cat a.rb

class A
def self.fields(*f); @fields||=f; end
def fields; self.class.fields; end
end
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]

harp:~ > ruby a.rb

[:a, :b, :c]
[:x, :y]

this would be a great place for a mixin:

harp:~ > cat a.rb

module Fieldable
def self.append_features klass
class << klass
def fields(*f); @fields||=f; end
end
super
end
def fields; self.class.fields; end
end
class B
include Fieldable
fields :a, :b, :c
end
class C
include Fieldable
fields :x, :y
end
p B.new.fields # => [:a, :b, :c]
p C.new.fields # => [:x, :y]

harp:~ > ruby a.rb

[:a, :b, :c]
[:x, :y]


it's not shorter - but it frees you from inheritence, which breaks
encapsulation.

kind regards.

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| A flower falls, even though we love it;
| and a weed grows, even though we do not love it.
| --Dogen
===============================================================================
 
M

Markus

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
 
C

Chris Morris

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"]
 
R

Robert Klemme

Bob Sidebotham said:
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
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
 
P

Pit Capitain

Bob said:
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

#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
 
B

Bob Sidebotham

Bob said:
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
 
R

Robert Klemme

Bob Sidebotham said:
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
 
B

Bob Sidebotham

Robert said:
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
 
R

Robert Klemme

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
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top