From: Stefano Crocco Date: 2008-10-29T04:30:30+09:00 Subject: Re: Proper way to define a subclass within parent file? Alle Tuesday 28 October 2008, Scott Strattner ha scritto: > I am starting to build a script to manipulate various network device > information, including a list of Vlans from a switch. To that end, I > created a Vlan class: > > class Vlan > @@max = 399 > @@min = 1 > @@protected = [1,2,5] > @@namelen = 32 > def initialize (id,desc) > self.set_name(desc) if (self.set_id(id)) > end > def set_id (id) > return nil unless (id.kind_of? Integer) > return nil if (@@protected.index(id) || id > @@max || id < @@min) > @id = id > end > def set_name (desc) > desc.gsub!(/\s+/,"_") > desc = desc[0..@@namelen-1] if (desc.length > @@namelen) > @description = desc > end > def to_s > return "#{@id}\t#{@description}" > end > def valid? > return true if (@id.kind_of? Integer) > return false > end > # snip various other methods > end > > This works as expected; to test it out I added some debugging code to > loop through some values and verify it only created valid instances: > > v = Array.new > 20.times { |a| > temp = Vlan.new(a,"this is vlan id #{a}") > v.insert(-1,temp) if (temp.valid?) > } > v.length.times { |a| puts "#{a} #{v[a].to_s}" } > > I then added a simple subclass, within the same file. This special kind > of Vlan acts like the others, except it uses a different range of valid > id values: > > class HMC_Vlan < Vlan > @@min = 701 > @@max = 799 > @@protected = [700,705] > end > > I then added to the debug code: > > v2 = Array.new > 700.upto(720) { |a| > temp2 = HMC_Vlan.new(a,"hmcvlan.#{a}") > v2.insert(-1,temp2) if (temp2.valid?) > } > v2.length.times { |a| puts "#{a} #{v2[a].to_s}" } > > When I ran this file, it created HMC_Vlans as expected, but no longer > created any valid Vlans ("v" array was empty). It turns out the class > variables @@max, min, protected in HMC_Vlan are overwriting the class > variables in Vlan - even though no instance of HMC_Vlan is created until > after my debug code creates the "v" array. > It was my understanding that a Class declaration was treated as a > conditional block - *if* someone creates this Class, *then* run through > the contents. You're wrong here. The body of a class definition is executed when it's found. Method bodies (for both instance methods and class methods) aren't executed untill they're called, but all the rest of the class body is executed immediately. For example, the code class C puts "body of class C" def method1 puts "body of method method1" end end when read produces the output "body of class C" and, after that, calling C.instance_methods will return an array which contains the string "method1", showing that the method method1 has already been defined. On the other hand, the body of method1 is only executed after a call to it, for example with the code C.new.method1. > But here it appears the class declaration is processed > before any instance of it is made. Even so, shouldn't a class variable > be specific to that class, and not be shared with children? Is there > another kind of inheritance I can use to maintain this separation in > class variables? Is there a specific way to define a subclass within the > same file as the parent? Class variables are shared among a class and all its subclasses. If you don't want that, you can use class instance variables, that is instance variables of the class object. They work like common instance variables, and can be accessed outside the class body only if appropriate class methods are defined (note: this is also true inside instance methods of the class). You could do this: class Vlan @max = 399 @min = 1 @protected = [1,2,5] @namelen = 32 def self.max @max end def self.min @min end def self.protected @protected end def set_id (id) return nil unless (id.kind_of? Integer) return nil if (self.class.protected.index(id) || id > self.classl.max || id < self.class.min) @id = id end ... The only problem with this approach is that all instance variables should be initialized in all classes which need them (you don't need to redefine the methods, instead). Another possibility, if the values in the instance variables shouldn't change is to use constants: class Vlan MAX = 399 MIN = 1 PROTECTED = [1,2,5] NAMELEN = 32 I hope this helps Stefano