Difficult Inheritance Problem

Hi all,

I have a problem I can't think of a solution to with regard to
inheritence.
I have an abstract class that others inherit from:

class DataObject
  class << self
    attr_accessor :difference_mapping

    ...
  end
  ...
end

The descendents of this class each define their own difference
mapping:

class Page < DataObject
  self.difference_mapping = {:blah => :blah}
end

I would now like to inherit from the Page class but use its
difference_mapping. Obviously this does not work because the
attr_accessor in the DataObject uses a class instance variable that is
unique to each class. I tried using a proper class variable
(@@difference_mapping) but that is the same for all descending
classes.

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods? I mean I could wrap the data structure in a
method but that does not seem like a very clean solution.

Thanks in advance,
Toby

Hi,

I am using something like below for my program. I trick is to use methods
instead of class variables as they persist through inheritance.

Martin

class A
   def self.define_attr_method(name, value=nil)
     sing = class << self; self; end
     sing.class_eval "def #{name}; #{value.inspect}; end"
   end

   # Sets and/or returns current difference_mapping
   def self.difference_mapping(value = nil)
     self.define_attr_method('difmap', value) unless value.nil?
     self.difmap
   end
end

class B < A
  difference_mapping 'class B'
end

class C < B
end

class D < B
difference_mapping 'class D'
end

puts B.difference_mapping
puts C.difference_mapping
puts D.difference_mapping

This will print:
class B
class B
class D

···

On Wednesday 27 August 2008 09:08:43 Toby Clemson wrote:

Hi all,

I have a problem I can't think of a solution to with regard to
inheritence.
I have an abstract class that others inherit from:

class DataObject
  class << self
    attr_accessor :difference_mapping

    ...
  end
  ...
end

The descendents of this class each define their own difference
mapping:

class Page < DataObject
  self.difference_mapping = {:blah => :blah}
end

I would now like to inherit from the Page class but use its
difference_mapping. Obviously this does not work because the
attr_accessor in the DataObject uses a class instance variable that is
unique to each class. I tried using a proper class variable
(@@difference_mapping) but that is the same for all descending
classes.

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods? I mean I could wrap the data structure in a
method but that does not seem like a very clean solution.

Thanks in advance,
Toby

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods? I mean I could wrap the data structure in a
method but that does not seem like a very clean solution.

Try this, perhaps:

class DataObject
  class << self
    attr_writer :mapping

    def mapping
      @mapping || (self == DataObject ? nil : superclass.mapping)
    end
  end
end

class Page < DataObject; end

DataObject.mapping = 'foo'

puts Page.mapping #=> 'foo'

You could write a class helper to generate these methods. I think Rails has
something called 'write_inheritable_attribute' somewhere that does the same
thing.

I would now like to inherit from the Page class but use its
difference_mapping. Obviously this does not work because the
attr_accessor in the DataObject uses a class instance variable that is
unique to each class. I tried using a proper class variable
(@@difference_mapping) but that is the same for all descending
classes.

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods? I mean I could wrap the data structure in a
method but that does not seem like a very clean solution.

two possible approaches are rails' class_inheritable_accessor and superclass_delegating_accessor.

Both lean on class instance variables. In the case of class_inheritable_accessor the attributes are store in a hash and self.inherited is hooked to copy that hash over when a subclass is created

superclass_delegating_accessor looks for an appropriate instance variable and if it does not exist calls the superclass (stopping when it gets to the the class that created the superclass_delegating_accessor). Writing however always writes to an instance variable of the class being changed. I put some more detail about them at Fun with class variables - Space Vatican

Fred

···

On 27 Aug 2008, at 10:08, Toby Clemson wrote:

Thanks in advance,
Toby

I am currently working on a new release of Labardor, but there still
is sooo much to do, however, you might be interested in the following
code of Labrador.
I am however aware that this implies a rethink of your strategy,
however one of my goals is to explore different behavior based OO
approaches than inheritance and mixins, in case you find that a useful
paradigm shift.
And I appologize for the long post but Labrador just is in no shape
for a new release to Rubyforge.

HTH
Robert
Skip the rest if you are not particularily interested in different OO
approaches.
----------------------------- 8< ------------------------

···

On Wed, Aug 27, 2008 at 11:08 AM, Toby Clemson <tobyclemson@gmail.com> wrote:
#--
# vim: sts=2 sw=2 tw=0 expandtab nu:
#*
#* Labrador, The Lazy Programmer's Best Friend.
#*
#* Distributed under the terms of the BSD License.
#* Copyright (c) 2007 Robert Dober
#* All rights reserved.
#*
#* Redistribution and use in source and binary forms, with or without
#* modification, are permitted provided that the following conditions are met:
#* * Redistributions of source code must retain the above copyright
#* notice, this list of conditions and the following disclaimer.
#* * Redistributions in binary form must reproduce the above copyright
#* notice, this list of conditions and the following disclaimer in the
#* documentation and/or other materials provided with the distribution.
#* * Neither the name of the Labrador packahe nor the
#* names of its contributors may be used to endorse or promote products
#* derived from this software without specific prior written permission.
#*
#* THIS SOFTWARE IS PROVIDED BY Robert Dober ``AS IS'' AND ANY
#* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#* DISCLAIMED. IN NO EVENT SHALL Robert Dober BE LIABLE FOR ANY
#* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#++

class Array

  def ivar_names_to_strings
    map { |ele| ele.to_s.sub("@","") }
  end

  def ivar_names_to_symbols
    map { |ele| ele.to_s.sub("@","").to_sym }
  end
  def names_to_ivar_strings
    map { |ele| "@#{ele.to_s.sub("@","")}" }
  end

  def names_to_ivar_symbols
    map { |ele| "@#{ele.to_s.sub("@","")}".to_sym }
  end

end # class Array

class Object
  def get_ivars *ivars
    ivars = instance_variables if ivars.empty?
    ivars.ivar_names_to_symbols.
      inject( {} ) { |h, ivar|
        h.update ivar => instance_variable_get( "@#{ivar}" )
      }
  end

  def pop_ivars
    set_ivars! @__ivar_stack__.pop
  end

  def push_ivars hash
    ( @__ivar_stack__ ||= [] ).
      push(
           get_ivars( *hash.keys )
          )
    set_ivars! hash
  end

  def set_ivars hash
    hash.keys.each do
      > ivar |
      ivar_name = "@#{ivar}".sub( /^@@/, "@" )
      instance_variable_set ivar_name, hash[ ivar ] unless
        instance_variables.include? ivar_name
    end
  end

  def set_ivars! hash
    hash.keys.each do
      > ivar |
      ivar_name = "@#{ivar}".sub( /^@@/, "@" )
      instance_variable_set ivar_name, hash[ ivar ]
    end
  end

  def set_only_ivars hash, *keys
    set_ivars hash.keys.inject( {} ){ |h, k|
      keys.include?( k ) ? h.update( k => hash[k] ) : h
    }
  end # def set_only_ivars hash, *keys

  def set_only_ivars! hash, *keys
    set_ivars! hash.keys.inject( {} ){ |h, k|
      keys.include?( k ) ? h.update( k => hash[k] ) : h
    }
  end # def set_only_ivars hash, *keys
end # class Object

class Behavior
  attr_reader :block
  def initialize &blk
    @block = blk
  end
end

module Kernel
  def Behavior &blk
    Behavior::new &blk
  end
end

class Module
  def empty?; instance_methods.empty? end
  def empty!; instance_methods.each do |im| remove_method im end end
end

module Pushable
  CannotPopException = Class::new RuntimeError
  ArgumentError = Class::new ::ArgumentError

  def pop_behavior
    @__bsp__ -= 1
    raise CannotPopException, "empty entity #{self}" if @__bsp__ < 0
    @__behavior_stack__[@__bsp__].empty!
  end

  def push_behavior *behaviors, &blk
    @__behavior_stack__ ||= []
    @__bsp__ ||= 0
    raise ArgumentError,
      "push_behavior takes at least one behavior or block" if
        behaviors.empty? and blk.nil?

    behaviors.each do |behavior|
      _push_behavior behavior
    end

    _push_behavior blk if blk
    self
  end

  private

  def _push_behavior behavior
    m = @__behavior_stack__[@__bsp__]
    @__behavior_stack__ << ( m = Module::new ) unless m
    include m rescue extend m # this is autoprotected against double
inclusion; so finally turns out it is a feature :wink:
    m.empty!
    m.module_eval &(behavior.block rescue behavior)
    @__bsp__ += 1
  end
end # module Pushable

class << Pushable
  def new *args, &blk
    c = Class::new( *args, &blk )
    c.extend self
    c
  end
end # class << Pushable

class << PushableModule = Module::new
  def new *args, &blk
    m = Module::new( *args, &blk )
    m.extend Pushable
    m
  end
end
------------------------------------ 8<
-----------------------------------------

Hi --

Hi all,

I have a problem I can't think of a solution to with regard to
inheritence.
I have an abstract class that others inherit from:

class DataObject
class << self
   attr_accessor :difference_mapping

   ...
end
...
end

The descendents of this class each define their own difference
mapping:

class Page < DataObject
self.difference_mapping = {:blah => :blah}
end

I would now like to inherit from the Page class but use its
difference_mapping. Obviously this does not work because the
attr_accessor in the DataObject uses a class instance variable that is
unique to each class. I tried using a proper class variable
(@@difference_mapping) but that is the same for all descending
classes.

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods? I mean I could wrap the data structure in a
method but that does not seem like a very clean solution.

The easiest way I can think of is:

   class Page
     class << self
       attr_accessor :difference_mapping
     end
     def self.inherited(c)
       c.difference_mapping = difference_mapping
     end
   end

   Page.difference_mapping = { :blah => :blah }

   class Next < Page
   end

   p Next.difference_mapping # { :blah => :blah }

David

···

On Wed, 27 Aug 2008, Toby Clemson wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL
See http://www.rubypal.com for details and updates!

Toby Clemson wrote:

Is there a way to define a class level variable that descends down the
inheritance tree unless it is overridden in the same way as this is
possible for methods?

Yes, using a hash that inherits values:

http://redshift.sourceforge.net/superhash/
http://redshift.sourceforge.net/superhash/lib/superhash.html

Sorry for the crappy docs. It's an old project, but I use it frequently.

Here's an example

require 'superhash'

class A
   class_superhash :options

   options[:foo] = "A foo"
   options[:bar] = "A bar"

   def options; self.class.options; end
end

class B < A
   options[:foo] = "B foo"
end

p A.options
p B.options.to_hash
p B.new.options.to_hash

__END__

output:

{:foo=>"A foo", :bar=>"A bar"}
{:foo=>"B foo", :bar=>"A bar"}

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

The easiest way I can think of is:

class Page
   class << self
     attr_accessor :difference_mapping
   end
   def self.inherited(c)
     c.difference_mapping = difference_mapping
   end
end

Page.difference_mapping = { :blah => :blah }

class Next < Page
end

p Next.difference_mapping # { :blah => :blah }

Depends whether you only need the definition done once. This technique will
not cause the subclass to reflect the parent class' value if it is changed,
e.g.:

Page.difference_mapping = {:foo => :something}
Next.difference_mapping #=> {:blah => :blah}

Wow that is great, I would call them viral class variables!
R.

···

On Wed, Aug 27, 2008 at 1:37 PM, David A. Black <dblack@rubypal.com> wrote:

Hi --

The easiest way I can think of is:

class Page
   class << self
     attr_accessor :difference_mapping
   end
   def self.inherited(c)
     c.difference_mapping = difference_mapping
   end
end

Page.difference_mapping = { :blah => :blah }

class Next < Page
end

p Next.difference_mapping # { :blah => :blah }

Depends whether you only need the definition done once. This technique will
not cause the subclass to reflect the parent class' value if it is changed,
e.g.:

Page.difference_mapping = {:foo => :something}
Next.difference_mapping #=> {:blah => :blah}

True, but the OP described it as: "Is there a way to define a class
level variable that descends down the inheritance tree unless it is
overridden in the same way as this is possible for methods?" In the
method case, you'd have:

   class A
     def x; 1; end
   end

   class B < A
   end

   class C < A
     def x; 2; end
   end

and even if A changed x, C would still have its override in effect. So
the same deal with the attribute might be OK in this case.

David

···

On Wed, 27 Aug 2008, James Coglan wrote:

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL
See http://www.rubypal.com for details and updates!

Hi --

···

On Wed, 27 Aug 2008, Robert Dober wrote:

On Wed, Aug 27, 2008 at 1:37 PM, David A. Black <dblack@rubypal.com> wrote:
Wow that is great, I would call them viral class variables!

I think class variables are already viral :slight_smile:

David

--
Rails training from David A. Black and Ruby Power and Light:
   Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
   Advancing with Rails January 19-22 Fort Lauderdale, FL
See http://www.rubypal.com for details and updates!

In that case, let me take a shot:

class Page

  class << self
    attr_accessor :difference_mapping
  end

  module DescendantClassMethods
    def difference_mapping
      Page.difference_mapping
    end
    def difference_mapping= value
      Page.difference_mapping = value
    end
  end

  def self.inherited klass
    klass.extend DescendantClassMethods
  end
end

Since I'm in a late-night hacking mood, here's a way that's even more meta:

# because << drives me crazy
require 'metaid'

# name it whatever you want...
module ClassVariables
  module BaseClassMethods
    def class_variable name
      self.meta_eval do
        attr_accessor name
      end
      superclass = self
      (self::InheritableClassVariables ||= Module.new).module_eval do
        define_method name do
          superclass.send name
        end
        assignment = :"#{name}="
        define_method assignment do |value|
          superclass.send assignment, value
        end
      end
    end

    def inherited klass
      mod = self::InheritableClassVariables
      # I think this, instead of extend, will let subclasses override
      # these methods properly.
      klass.meta_eval do
        include mod
      end
    end
  end

  def self.included mod
    mod.extend BaseClassMethods
  end
end

Hmm. That's... mostly complete. Usage would be:

class Page
  include ClassVariable
  class_variable :difference_mapping
end

But I haven't so much as executed it to check for syntax errors.

···

On Wednesday 27 August 2008 06:44:46 James Coglan wrote:

>
> The easiest way I can think of is:
>
> class Page
> class << self
> attr_accessor :difference_mapping
> end
> def self.inherited(c)
> c.difference_mapping = difference_mapping
> end
> end
>
> Page.difference_mapping = { :blah => :blah }
>
> class Next < Page
> end
>
> p Next.difference_mapping # { :blah => :blah }

Depends whether you only need the definition done once. This technique will
not cause the subclass to reflect the parent class' value if it is changed,

Well they are unique, applying your pattern however the following holds

class X < Page
   @difference_mapping = 42
end

Y = Class::new( X )

Y.difference_mapping => 42
*while*
Next.difference_mapping is still the hash.

···

On Wed, Aug 27, 2008 at 2:55 PM, David A. Black <dblack@rubypal.com> wrote:

Hi --

On Wed, 27 Aug 2008, Robert Dober wrote:

On Wed, Aug 27, 2008 at 1:37 PM, David A. Black <dblack@rubypal.com> >> wrote:
Wow that is great, I would call them viral class variables!

I think class variables are already viral :slight_smile:

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

I meant with viral that the *new* values are propagated *only* down
the inheritance tree that is what OP wanted and I have to admit I did
not see immediately that your solution was correct in that regard.
When I realized how it was I found it quite amazing.

I should have said that self.inherited was viral.

Cheers
Robert

Thank you all for your responses. I ended up using James Coglan's
solution as that is what I was trying to achieve even if I didn't make
that clear in my first post. But even so I've learnt something from
each of your responses.
Cheers,
Toby

I believe there is a better approach that solves the inheritance issue
generally. (Jame's solution will only reuse mappings as long as no
mapping is defined in the current class.) Usually you want to deal
with all the mappings aggregated so far in the inheritance hierarchy.
Thus my solution would be

class DataObject
  class<<self
    attr_writer :difference_mapping

    def difference_mapping
      ancestors.inject({}) do |m,cl|
        m.merge(cl.instance_variable_get("@difference_mapping")) rescue m
      end
    end
  end
end

class Page < DataObject
  self.difference_mapping = {:blah => :blah}
  p [self, difference_mapping]
end

class YourClass < Page
  p [self, difference_mapping]
end

Kind regards

robert

···

2008/9/2 tobyclemson@gmail.com <tobyclemson@gmail.com>:

Thank you all for your responses. I ended up using James Coglan's
solution as that is what I was trying to achieve even if I didn't make
that clear in my first post. But even so I've learnt something from
each of your responses.

--
use.inject do |as, often| as.you_can - without end