| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| 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 |
|
#2
| |||
| |||
| 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 |
|
#3
| |||
| |||
| [Note: parts of this message were removed to make it a legal post.] > 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. |
|
#4
| |||
| |||
| On 27 Aug 2008, at 10:08, Toby Clemson wrote: > > 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 http://www.spacevatican.org/2008/8/1...lass-variables Fred > Thanks in advance, > Toby > |
|
#5
| |||
| |||
| On Wed, Aug 27, 2008 at 11:08 AM, Toby Clemson <tobyclemson@gmail.com> wrote: 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< ------------------------ #-- # 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 ![]() 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< ----------------------------------------- |
|
#6
| |||
| |||
| Hi -- On Wed, 27 Aug 2008, 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. 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 -- 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! |
|
#7
| |||
| |||
| [Note: parts of this message were removed to make it a legal post.] > > 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} |
|
#8
| |||
| |||
| Hi -- On Wed, 27 Aug 2008, 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, > 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 -- 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! |
|
#9
| |||
| |||
| 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! R. |
|
#10
| |||
| |||
| 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 :-) 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! |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.