Golf, anyone?

M

m4dc4p

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"],
# ["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

Ara.T.Howard

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"],
# ["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.


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.
a: #{a.class.prop.inspect},
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"],
["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"]


you could do it with a Foo module too, but it's excess typing and you said 'golf' ;-)

regards.

-a
--
===============================================================================
| 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
===============================================================================
 
R

Robert Klemme

m4dc4p said:
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
 
T

Trans

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

m4dc4p

Trans said:
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.
 
T

Trans

m4dc4p said:
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.
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top