Can you rewrite this in a better way?

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

···

On Fri, 1 Oct 2004, 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

--

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

===============================================================================

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

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://clabs.org

"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

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

"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

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

"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