"Gavin Kistner" <gavin / refinery.com> schrieb im Newsbeitrag news:4F71B514-0D6F-11D9-BA76-000A959CF5AC / refinery.com... > Summary > I'm looking for advice on how to design code so that I can dynamically > add/remove instantiable classes, where each of these classes have > values for a set of properties defined by a parent class. > > Details > I'm starting to design/write an uber-ambitious home automation hub, in > Ruby. In addition to having convenient things like a web-based GUI > front-end, scheduler, and so on, the heart of this application is that > it will (eventually) work with any automation hardware whose > communication protocol is open (or reverse-engineered). > > The plan is to have 'adaptors' for each discrete hardware type, which > encapsulate all the guts of the communication protocol and expose > methods for that bit of hardware. The user can then 'instantiate' one > or more of these adaptors, representing physical instances of that bit > of hardware in the home. > > (For example, there's a single "Lutron RadioRA Wall Dimmer" adaptor, > but the user may have 4 such switches in the house, named "Front > Kitchen Lights", "Rear Kitchen Lights", "Bedroom Lights", and "Entry > Lights".) > > In addition to custom methods, each adaptor needs to expose some common > information, such as its name, manufacturer, category, sub-category, > model number, and so on. > > I initially decided to have code like this: > > me = Foo::Bar::DeviceType.new( 'Lutron RadioRA Wall Dimmer', :Lutron, > :Switches, :Dimmers, 'RA-ND6' ) > me.add_action( :turn_on, Proc.new{ ... } ) > me.add_action( :turn_off, Proc.new{ ... } ) > me.add_action( :level=, Proc.new{ ... } ) > me.add_action( :level, Proc.new{ ... } ) > > #...and then later do something like... > Foo::Bar::add_device( 'Entry Lights', blah ) > # ...where blah is a pointer to the DeviceType instance > > > But then I realized that I'm mostly just putting a wrapper around a > class definition. Further, I realized that I want all adaptors to > support common methods (name, manufacturer, model number), all light > switches to support some common methods (turn_on, turn_off), and all > dimmers to support others still (level= and level). This reeks of a > class hierarchy, so I was thinking that I might do something like: > > # Core app > class Foo::Bar::DeviceType > def name; raise "Implement Me!"; end > def manufacturer; raise "Implement Me!"; end > def model; raise "Implement Me!"; end > > class Light > def turn_on; raise "Implement Me!"; end > def turn_off; raise "Implement Me!"; end > > class Dimmer > def level=(n); raise "Implement Me!"; end > def level; raise "Implement Me!"; end I'd define a method to easy this or use the approach shown below (stored mandatory method names). class Class def method_stub(m) class_eval { define_method(m) { raise "Implement Me!" } } end end class Base method_stub :foo end Base.new >> Base.new.foo RuntimeError: Implement Me! from (irb):19:in `foo' from (irb):19:in `foo' from (irb):25 > end > end > end > > #Adaptor file > class Foo::Bar::DeviceType::Light::Dimmer::RadioRA > def name; 'Lutron RadioRA Wall Dimmer'; end > def manufacturer; 'Lutron'; end > def name; 'RA-ND6'; end > def turn_on; ...; end; > def turn_off; ...; end; > def level=(n); ...; end; > def level; ...; end; > Foo::Bar::DeviceType::add_device( self ) > end > > .. and the add_device method would > a) Create a dummy parent class and run through all the methods to see > if they throw errors. > b) Add the class into a list of instantiable adaptors if it works. > > Questions > 1) Is there a way to inspect the runtime state and figure out which > subclasses exist for a given class? Here are two ways to ensure that you know your subclasses recursively: class Base_1 def self.inherited(cl) (@children ||= []) << cl me = self class <<cl;self;end.class_eval { define_method(:inherited) {|cl2| me.inherited cl2} } end def self.subclasses() @children end end class Base def self.inherited(cl) (@children ||= []) << cl def cl.inherited(cl2) superclass.inherited(cl2) end end def self.subclasses() @children end end class Sub1 < Base end class Sub2 < Sub1 end class Sub3 < Sub2 end p Base.subclasses > 2) Is there a better way to force/detect if a subclass implements > certain methods? You could store a set of method names in the base class and add a check method for sub classes. require 'set' class Base def self.inherited(cl) (@children ||= []) << cl def cl.inherited(cl2) superclass.inherited(cl2) end end def self.subclasses() @children end def self.set_mandatory_methods(*m) @mandatory = Set.new(m.flatten.map{|m| m.to_s}) end def self.check_mandatory @children.each do |cl| diff = @mandatory - Set.new(cl.instance_methods) raise "Methods missing: class=#{cl} methods=#{diff.to_a.inspect}" unless diff.empty? end end end > 3) Is there a better way overall to achieve my goal? I don't have a different approach right now. Sounds reasonable. Kind regards robert