Golf, anyone?

I spent a day figuring out how to solve this problem, and it seems like
there should be a much better solution. I wanted to write a module
that, when included in an object, would create a class variable and
accessor for that object. If another class includes the module, it
should have its own class variable and accessor. The class variable
does *not* live in the module - it is not shared by all objects which
include the module.

A simplified version of what I came up with is below. Module Foo
defines the class variable and accessor. Class Bar and Baz are related
by inheritance. Instance variables a and b are of class Bar and Baz.
Instance variable w is of class Bil, which is not related to either Bar
or Baz.

When a value is added to the "prop" class variable of either a or
b, they both show it. However, when a value is added to the "prop"
class variable in w, it does not show up in either a or b.

I'm convinced there is a better way to do this and what I came up
with is just a hack on a kludge. Any and all help is appreciated!

module Foo
  def self.included(mod)
    super(mod)
    class << mod; self; end.class_eval <<-EOS
      @prop = []
      def prop
        @prop ||= []
      end
    EOS

    def mod.inherited(p)
      def p.prop
        superclass.prop
      end

    end
  end
end

class Bar
  include Foo
end

class Baz < Bar
end

class Bil
  include Foo
end

a = Bar.new
b = Bar.new
x = Baz.new
w = Bil.new

a.class.prop << "p1"
puts "a, b, and x share property added to a:
  #{a.class.prop.inspect},
  #{b.class.prop.inspect},
  #{x.class.prop.inspect}"

b.class.prop << "p2"
puts "and property added to b:
  #{a.class.prop.inspect},
  #{b.class.prop.inspect},
  #{x.class.prop.inspect}"

w.class.prop << "p3"
puts "but not a property added to w.
  b: #{b.class.prop.inspect},
  x: #{x.class.prop.inspect},
  w: #{w.class.prop.inspect}"

#>ruby mod_test.rb
#a, b, and x share property added to a:
# ["p1"],
# ["p1"],
# ["p1"]
#and property added to b:
# ["p1", "p2"],
# ["p1", "p2"],
# ["p1", "p2"]
#but not a property added to w.
# a: ["p1", "p2"],
# b: ["p1", "p2"],
# x: ["p1", "p2"],
# w: ["p3"]

p.s. The implementation here relies on the append_features method of
module, and the inherited method of objects. When an object first
includes the module, the class variable and property are added to the
object via append_features. I found that this this did NOT extend down
the inheritance chain for those objects, though. I was forced to
override the inherrited method on the initial object so I could place
the 'properties' definition in each subclass. This is my least favorite
part of my solution.

···

a: #{a.class.prop.inspect},

m4dc4p wrote:

I spent a day figuring out how to solve this problem, and it seems
like there should be a much better solution. I wanted to write a
module that, when included in an object, would create a class
variable and accessor for that object. If another class includes the
module, it should have its own class variable and accessor. The class
variable does *not* live in the module - it is not shared by all
objects which include the module.

Like this?

module Foo
  def self.included(cl)
    class <<cl
      attr_reader :prop
    end
    cl.class_eval { @prop = }
    def cl.inherited(cl2)
      cl2.class_eval { include Foo }
    end
  end
end
class Bar
  include Foo
end
Bar.prop << "a"
p Bar.prop
class Sub < Bar
end
class Sub2 < Sub
end
Sub.prop << "b"
p Sub.prop
Sub2.prop << "c"
p Sub2.prop

Kind regards

    robert

I spent a day figuring out how to solve this problem, and it seems like
there should be a much better solution. I wanted to write a module
that, when included in an object, would create a class variable and
accessor for that object. If another class includes the module, it
should have its own class variable and accessor. The class variable
does *not* live in the module - it is not shared by all objects which
include the module.

A simplified version of what I came up with is below. Module Foo
defines the class variable and accessor. Class Bar and Baz are related
by inheritance. Instance variables a and b are of class Bar and Baz.
Instance variable w is of class Bil, which is not related to either Bar
or Baz.

When a value is added to the "prop" class variable of either a or
b, they both show it. However, when a value is added to the "prop"
class variable in w, it does not show up in either a or b.

I'm convinced there is a better way to do this and what I came up
with is just a hack on a kludge. Any and all help is appreciated!

module Foo
  def self.included(mod)
    super(mod)
    class << mod; self; end.class_eval <<-EOS
      @prop =
      def prop
        @prop ||=
      end
    EOS

    def mod.inherited(p)
      def p.prop
        superclass.prop
      end

    end
  end
end

class Bar
  include Foo
end

class Baz < Bar
end

class Bil
  include Foo
end

a = Bar.new
b = Bar.new
x = Baz.new
w = Bil.new

a.class.prop << "p1"
puts "a, b, and x share property added to a:
  #{a.class.prop.inspect},
  #{b.class.prop.inspect},
  #{x.class.prop.inspect}"

b.class.prop << "p2"
puts "and property added to b:
  #{a.class.prop.inspect},
  #{b.class.prop.inspect},
  #{x.class.prop.inspect}"

w.class.prop << "p3"
puts "but not a property added to w.
  a: #{a.class.prop.inspect},
  b: #{b.class.prop.inspect},
  x: #{x.class.prop.inspect},
  w: #{w.class.prop.inspect}"

#>ruby mod_test.rb
#a, b, and x share property added to a:
# ["p1"],
# ["p1"]
#and property added to b:
# ["p1", "p2"],
# ["p1", "p2"]
#but not a property added to w.
# a: ["p1", "p2"],
# b: ["p1", "p2"],
# x: ["p1", "p2"],
# w: ["p3"]

p.s. The implementation here relies on the append_features method of
module, and the inherited method of objects. When an object first
includes the module, the class variable and property are added to the
object via append_features. I found that this this did NOT extend down
the inheritance chain for those objects, though. I was forced to
override the inherrited method on the initial object so I could place
the 'properties' definition in each subclass. This is my least favorite
part of my solution.

   harp:~ > cat a.rb
   require "traits"

   class Bar; class_trait "prop" => ;end
   class Baz < Bar; end
   class Bil; class_trait "prop" => ;end

   a, b, x, w = [Bar, Bar, Baz, Bil].map{|c| c::new}

   a.class.prop << "p1"
   puts "a, b, and x share property added to a:
           #{a.class.prop.inspect},
           #{b.class.prop.inspect},
           #{x.class.prop.inspect}"

   b.class.prop << "p2"
   puts "and property added to b:
           #{a.class.prop.inspect},
           #{b.class.prop.inspect},
           #{x.class.prop.inspect}"

   w.class.prop << "p3"
   puts "but not a property added to w.
           b: #{b.class.prop.inspect},
           x: #{x.class.prop.inspect},
           w: #{w.class.prop.inspect}"

   harp:~ > ruby a.rb
   a, b, and x share property added to a:
           ["p1"],
           ["p1"]
   and property added to b:
           ["p1", "p2"],
           ["p1", "p2"]
   but not a property added to w.
           b: ["p1", "p2"],
           x: ["p1", "p2"],
           w: ["p3"]

you could do it with a Foo module too, but it's excess typing and you said 'golf' :wink:

regards.

-a

···

On Fri, 11 Nov 2005, m4dc4p wrote:
           a: #{a.class.prop.inspect},
           a: ["p1", "p2"],
--

ara [dot] t [dot] howard [at] gmail [dot] com
all happiness comes from the desire for others to be happy. all misery
comes from the desire for oneself to be happy.
-- bodhicaryavatara

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

<quote>
I found that this this did NOT extend down
the inheritance chain for those objects, though. I was forced to
override the inherrited method on the initial object so I could place
the 'properties' definition in each subclass. This is my least favorite
part of my solution.
</quote>

Your view is far from uncommon. Have a look at:

  http://www.rcrchive.net/rcr/show/325

Also, there seems to be a bit some difficulty between what you state
you want and the example you present. You say:

  If another class includes the module, it should have its own class
variable and accessor.

But your exmaple has it that a subclass will share the same @prop (Ie.
Baz -> Bar). Which appreantly is why you added the superclass call.
(Notice also that Robert's example doesn't handle this). So I'm
wondering which you really want. Basically you asking two different
things: Standard module method inheritance (which has independent class
instance vars) or Or module method inheritance with addtioanl class
variable inheritance.

In either case here are possiblities using Facets. Method inheritance:

  include 'facet/class_inherit'

  module Foo
    class_inherit do
      def prop
        @prop ||= []
      end
    end
  end

or with variable inheritance:

  include 'facet/inheritor'

  module Foo
    inheritor :prop, [], :+
  end

Note this last example requires the use of bang method add to the
array, eg.

  a.prop! << 'p1"

T.

Trans wrote:

But your exmaple has it that a subclass will share the same @prop (Ie.
Baz -> Bar). Which appreantly is why you added the superclass call.
(Notice also that Robert's example doesn't handle this). So I'm
wondering which you really want. Basically you asking two different
things: Standard module method inheritance (which has independent class
instance vars) or Or module method inheritance with addtioanl class
variable inheritance.

Thanks for your reply - definitely the second case - "variable
inheritance."

  include 'facet/inheritor'

  module Foo
    inheritor :prop, , :+
  end

I looked at the implementation of inheritor and I wonder if you could
explain the reason for passing the ":+" symbol to the method. From the
implementation, you have this line:

    deflambda = lambda do
      define_method( key ) do
        defined?(super) ? super.__send__(op,obj) : obj.dup
      end
    ...

Why does the "+" method need to be called on the parent? I am hoping to
be able to understand what you are doing here so I can be a better
Rubyist!

Ara and Robert - sorry for replying directly to you. I meant to post to
the group but accidently clicked "reply to author". Thanks for your
responses also.

m4dc4p wrote:

Thanks for your reply - definitely the second case - "variable
inheritance."

> include 'facet/inheritor'
>
> module Foo
> inheritor :prop, , :+
> end
>

I looked at the implementation of inheritor and I wonder if you could
explain the reason for passing the ":+" symbol to the method. From the
implementation, you have this line:

    deflambda = lambda do
      define_method( key ) do
        defined?(super) ? super.__send__(op,obj) : obj.dup
      end
    ...

Why does the "+" method need to be called on the parent? I am hoping to
be able to understand what you are doing here so I can be a better
Rubyist!

'op' is set to :+, so :+ is being called on the parents *method* of the
same name, hence the array is being built via this operation as it
traverses through the inheritance chain. If we used a different
operator we could get a different effect:

  class X
    inheritor :x, , :-
  end

  class Y < X
    inheritor :x, , :-
  end

  X.x!.concat [1,2,3]
  Y.x! << 3
  Y.x #=> [1,2]

Inhertior is flexible so you can have any kind of inhertiable variable
with any one of it's methods acting as the inheritance operator, eg.

  inheritor :hsh, {}, :merge
  inheritor :sum, 0, :+
  inheritor :duck, Duck.new, :quack

If you were to write it out by hand, what it boils down to (when using
and :+) is basically this:

  class X
    @x =
    def self.x ; @x ; end
  end

  class Y < X
    @x =
    def self.x ; super + @x ; end
  end

Of course getting Modules to play nice in this system is the tricky
part which requires the use of class_inherit.rb.

HTH,
T.