Design Advice: Sub-Class 'Instances'

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
    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?
2) Is there a better way to force/detect if a subclass implements certain methods?
3) Is there a better way overall to achieve my goal?

Thanks if you even read this far. Thanks even more if you take the time to think about the problem and offer suggestions.

···

--
(-, /\ \/ / /\/

Gavin Kistner wrote:

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

This pleases me more than I can say.

I have a similar project which I outlined and began to code, but I ran
out of steam when I had problems debugging my X10 code.

See http://rubyhacker.com/domo.html

Let's see if we can develop some synergy or just plain merge these
projects.

I'm not picky about all the design details of "my" project; if I don't
have help with it, it will never materialize at all; and if I do have
help, the other person(s) will contribute to the design.

Questions
1) Is there a way to inspect the runtime state and figure out which subclasses exist for a given class?

I think there's a hook by which you can have a callback keep a list. I
forget the name: inherited I think. ri seems incorrect on that one.

2) Is there a better way to force/detect if a subclass implements certain methods?

Well... you might just use the callback technique again and maintain a
list of methods that are redefined.

3) Is there a better way overall to achieve my goal?

Dunno... for some thoughts on creating classes dynamically, you might
want to look at this snippet: http://rubyforge.org/snippet/detail.php?type=snippet&id=25

For a fancy plugin architecture (which you can even reuse), look at
FreeRIDE's thing called FreeBase.

Definitely let's talk further.

BTW: I like HomeSeer so much that if I could get Ruby scripting to work for
it, I would almost abandon the idea of a Linux solution. It is "supposed"
to work, but the last time I tried, I couldn't make it happen. If you're
interested in looking at that problem, let me know. I would *love* to get
that to work, because then I could start scripting my entire house in
Ruby *today*.

Thanks,
Hal

"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

Oops. I confused inheritance hierarchy with namespace hierarchy.
Sprinkle some "... < self" in there, along with a "... < Foo::Bar::DeviceType::Light::Dimmer"

···

On Sep 23, 2004, at 8:46 AM, Gavin Kistner wrote:

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

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.

I think it will turn out to be much simpler than the examples you have
written; there is no need to make a class hierarchy as you would in C++. In
Ruby, the concept of "class" is nowhere near as important as in C++; it's
just a convenient factory for creating objects which happen to implement the
same set of methods, at least at the time when they were created. Each
object individually can have methods added and removed dynamically, so
what's important is whether a particular object X has method Y right now,
rather than the class it came from.

So what I suggest is that you just make objects, which respond to methods
which make sense to them, in any way which is simple and convenient.

class DumbLight
  def initialize(location) ... end
  def turn_on ... end
  def turn_off ... end
end

class DimmerLight
  def initialize(location) ... end
  def turn_on ... end
  def turn_off ... end
  def level=(x) ... end
  def level ... end
end

switches =
switches << DumbLight.new('hallway')
switches << DumbLight.new('staircase')
switches << DimmerLight.new('lounge')

If you try to send a "level=" method to a DumbLight, then you will get a
NoMethodError exception, which you can catch easily. All it's saying is,
this object doesn't know how to do that.

If it saves you work, you can make class DimmerLight a subclass of
DumbLight; but you really don't have to if it doesn't make sense to do so,
e.g. if you don't re-use any significant amount of code.

Most importantly, you really don't have to have a common ancestor class
which contains methods that most or all your objects are expected to respond
to.

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.

All classes are instantiable, and the methods they respond to may vary at
run-time (even after the object has been instantiated), so it usually
doesn't make sense to do this.

Just take a pool of objects representing the things you want to control and
present it to the user. If you want to have the GUI dynamically adjust
itself for each object, then you can check if the method exists for each
object before you present it:

    gui.add_button("Turn On", foo, :turn_on) if foo.respond_to?(:turn_on)
    gui.add_button("Turn Off", foo, :turn_off) if foo.respond_to?(:turn_off)

(or the objects themselves could contain code which knows how to create the
appropriate GUI controls, but personally I prefer to keep knowledge about
presentation in the GUI. After all, you could have many different types of
interface controlling the same set of objects: web, command-line, Tcl/Tk,
etc)

You can also use #arity to work out how many parameters a method takes, if
you want your GUI to present the right number of fields automatically:

  class OnOffSwitch
    def initialize(ipaddress, port)
      @addr = ipaddress
      @port = port
    end
    ...
  end

  p OnOffSwitch.instance_method(:initialize).arity
  => 2

However, in this case, it may be better to return a data structure designed
for the GUI to prompt the information. This can include a human-readable
description, a validation regexp, and perhaps a method to call to convert it
from a string into the desired format.

  class OnOffSwitch
    def self.initialize_parameters
      [
        ["IP Address", 15, /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/, nil],
        ["Port", 5, /\A\d+\z/, :to_i],
      ]
    end
  end

  p OnOffSwitch.initialize_parameters

Equally, you can ask what methods an object has, but it probably makes sense
to return an array of methods which the user can *sensibly* call on this
object.

Questions
1) Is there a way to inspect the runtime state and figure out which
subclasses exist for a given class?

Yes, there is:
   ObjectSpace.each_object(Class) { |klass| p klass, klass.ancestors }
But usually that's not what you want to do.

2) Is there a better way to force/detect if a subclass implements
certain methods?

Yes, use #respond_to?(:foo) to check if an object implements method foo,
rather than worrying about what class it is in. Or just call it anyway, and
catch the exception if it fails.

If you have a common set of methods which are shared by many objects,
consider putting them into a Module which can then be included in a class:

module OnOff
  def turn_on ... end
  def turn_off ... end
end

class DumbLight
  include OnOff
  ...
end

class DimmerLight
  include OnOff
  ...
end

as that's more flexible than straightforward class inheritance. Also you can
add the methods to individual objects:

  fishes = FishTank.new('hallway')
  fishes.extend OnOff
  # this is the only fishtank with an on/off switch

Finally, as a general point, do consider delegation rather than inheritance;
that is, object X "has_a" Y, rather than object X "is_a" Y. It often turns
out to be a far more flexible way to make objects with composite or more
'intelligent' behaviour than the base object.

  class Fishtank
    def initialize
      @light = OnOffSwitch.new
      @pump = OnOffSwitch.new
    end
    def turn_on
      @light.turn_on
      @pump.turn_on
    end
    def turn_off
      @light.turn_off
      @pump.turn_off
    end
  end

3) Is there a better way overall to achieve my goal?

I hope the above makes some sense :slight_smile:

Regards,

Brian.

Sweet. I'll probably run out of steam myself at some point, but so far I'm optimistic. My project differs from yours significantly in the answer to this question (for your site):

"So are you completely reinventing the wheel?"

Yes. Yes I am. :slight_smile:

I realize this going into it, but (while I'll surely be hitting this list again and again for bits of advice and help) I want to write this on my own. (At least until it comes to the part of writing adaptors for every bit of hardware out there.)

I hope you won't take any offense, Hal; it's nothing personal about you or your project, but I want to be pig-headed and write all this from scratch. For the fun, the ownership, and the experience.

Which is also why I'm writing my own 'plugin' system. I could probably save some time and idiot-mistakes by investigating another well-thought-out solution like FreeRIDE, but a) like so many fools, I loathe doing prior-work research, and b) I hope that my solution will be incredibly clean, particularly because it is perfectly-tailored to my needs.

Thanks very much for the offer, and I may try and pick your brain later and/or dump the whole pigheaded approach altogether, but for now I'm walking the path on my own.

Now, if you could all just lift me up off the ground and point me in the write direction, perhaps with a shove or two... :wink:

···

On Sep 23, 2004, at 9:19 AM, Hal Fulton wrote:

I have a similar project which I outlined and began to code, but I ran
out of steam when I had problems debugging my X10 code.

See http://rubyhacker.com/domo.html

Let's see if we can develop some synergy or just plain merge these projects.

--
(-, /\ \/ / /\/

You should put this onto the web somewhere. Maybe in ruby-garden.org

Regards,

Brian

···

On Fri, 24 Sep 2004 01:07:43 +0900, Brian Candler wrote:

[Really nice essay on ruby inheritance/mixin/extension snipped]

--
Brian Schröder
http://www.brian-schroeder.de/

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

Whoa.

a) I had no idea about the #inherited method. Awesome. (In my defense, there appears to be a bug in the docs; in my ri, my own rdoc compilation, and the current docs on ruby-doc.org, Class#inherited appears in the method list, but links to #singleton_method_undefined. e.g. http://www.ruby-doc.org/core/classes/Class.html#M000472

b) I didn't really realize that you could use instance variables on the class itself; this had been what was holding up my method-tracking implementation. thanks!

c) While that second line is tricky, I actually prefer the hierarchical storage which occurs without it, since it allows for creating a hierarchical, visual representation of the class hierarchy. (Or is there a much cleaner/more built-in way than the following?)

class Foo
  def self.inherited( k )
    (@subclasses ||= ) << k
    #def k.inherited(k2)
      superclass.inherited(k2)
    end
  end
  
  def self.show_subclasses( indent=0 )
    puts "\t"*indent + self.inspect
    @ subclasses.each{ |k|
      k.show_subclasses( indent+1 )
    } if @ subclasses.respond_to? :each
  end

  class Bar < self
    class Jee < self; end
  end
  class Whee < self; end
end
class Foo2 < Foo; end

Foo.show_subclasses( )
#=> Foo
#=> Foo::Bar
#=> Foo::Bar::Jee
#=> Foo::Whee
#=> Foo2

diff = @mandatory - Set.new(cl.instance_methods)

Ooh, nice, I was going to just yell on a first-not-found basis but the Set is a very elegant way of doing it.

Thank you very much for your help, Robert. It's exactly what I needed! :slight_smile:

···

On Sep 23, 2004, at 9:29 AM, Robert Klemme wrote:
--
(-, /\ \/ / /\/

Each object individually can have methods added and removed dynamically, so
what's important is whether a particular object X has method Y right now,
rather than the class it came from.

I agree with a lot of what you say in general, Brian. Although with the above, I don't suspect that there will be any code extending specific instances on the fly; it simply wouldn't occur in the Plan that's in my head.

While I'm normally a huge duck-typing-plus fan, I'm waffling in this case. I think the reason is that I hope to have adaptor modules (classes) being contributed from many different authors, providing me with two challenges:

1) Given the large number of devices which fit under 'automation', I want a hierarchical organization of devices which may be available. If I leave it up to each author, I fear the user will install an adaptor for their hardware and find that it's under LightSwitch/Dimmer while every other similar adaptor is in LightSwitches/Adaptors. Or worse, it's in DimmerSwitches, or Electrical/Wall/Analog/Slider.

So, I feel the need to impose my own template hierarchy (at least as a starting framework) for displaying the adaptors. While I could use a hierarchical storage of symbols, I like the idea of using a class namespace hierarchy. It gives me hierarchical classification of an adaptor, and at the same time gives me a modicum of protection from adaptor authors running over each others' code (through namespace collisions).

2) Similar to the above, the more leeway I give the authors in the method naming convention, the more I fear that some switches will have "Turn On", some will have "On", and some smartass authors will use "Illuminate".Having thought about your points, I will certainly need to look at the exposed methods for any adaptor and be able to use them. (Who knows what bizarre functionality I will forget to include or account for?) But, for the sake of consistency, I think I want to say "Damnit, if you have a 'Light' class, you better have a :turn_on method."

And again, these methods should be hierarchically organized, conveniently corresponding to the device hierarchy already being defined to solve the classification and namespace problem.

Just take a pool of objects representing the things you want to control and
present it to the user. If you want to have the GUI dynamically adjust
itself for each object, then you can check if the method exists for each
object before you present it:

    gui.add_button("Turn On", foo, :turn_on) if foo.respond_to?(:turn_on)
    gui.add_button("Turn Off", foo, :turn_off) if foo.respond_to?(:turn_off)

(or the objects themselves could contain code which knows how to create the
appropriate GUI controls, but personally I prefer to keep knowledge about
presentation in the GUI. After all, you could have many different types of
interface controlling the same set of objects: web, command-line, Tcl/Tk,
etc)

Just to clarify my earlier point - you're right, the adaptors shouldn't know how to make the GUI, but the GUI does need to know how to describe the interface. What you list above is likely equivalent to what I'll end up doing (ensuring that the method exists). However, since I can't know all the methods/labels which might be needed, I'll likely be ensuring that it exists by iterating the exposed methods, and at that point I'm at the naming mercy of the author. And I don't want to be at their mercy for certain core functionality.

However, in this case, it may be better to return a data structure designed
for the GUI to prompt the information. This can include a human-readable
description, a validation regexp, and perhaps a method to call to convert it
from a string into the desired format.

Aye, I'm thinking even further (for rich GUI), along the lines of:

class Foo::Bar::Whee::La < Foo::Bar::Whee
  def turn_on; ...; end
  def get_funky( speed, dance_style, song ); ...; end
  ...
  describe_method( :turn_on, "Turn On", "Turns on the switch" )
  describe_method( :get_funky,
    "Get Funky",
    "Breaks down to the beat.",
    {
      :speed => {
        :type => :integer,
        :min => 0,
        :max =>100
      },
      :dance_style => {
        :type => :list,
        :values => ['Hip Hop','Disco' ]
      },
      :song => {
        :type => :file,
        :mask => /\.(mp3|ogg|aiff)$/
      }
    }
  )
end

If you have a common set of methods which are shared by many objects,
consider putting them into a Module which can then be included in a class:

Well, as Robert correctly surmised, I don't actually want to implement common code to them. (Some adaptor authors may want to, and they're welcome to define their own modules if they wish). What I'm probably going to end up with is something like:

class Device
  def self.mandatory_methods= ( *method_names )
    @mandatory = method_names.flatten.to_set
    @mandatory.merge( superclass.mandatory_methods ) if superclass.respond_to? :mandatory_methods
  end

  def self.mandatory_methods
    @mandatory
  end

  self.mandatory_methods = [:name, :manufacturer, :models]

  class Light < self
    self.mandatory_methods = [:turn_on, :turn_off]
    class Dimmer < self
      self.mandatory_methods = [:level=, :level]
    end
  end
  
  class Outlet < self
    self.mandatory_methods = [:turn_on, :turn_off]
  end
end

along with a test for these methods as part of my #instance handling.

Finally, as a general point, do consider delegation rather than inheritance;
that is, object X "has_a" Y, rather than object X "is_a" Y. It often turns
out to be a far more flexible way to make objects with composite or more
'intelligent' behaviour than the base object.

Good advice; I'll certainly think about some nice way to group primitives in the user UI.

Thanks so much for your considered advice.

···

On Sep 23, 2004, at 10:07 AM, Brian Candler wrote:

--
(-, /\ \/ / /\/

Gavin Kistner wrote:

I hope you won't take any offense, Hal; it's nothing personal about you or your project, but I want to be pig-headed and write all this from scratch. For the fun, the ownership, and the experience.

I concur totally.

But the most important thing we can steal... is ideas. :wink: And when you
steal ideas and implement them, you still get the thrill of invention.

If your project became usable and did the stuff I wanted, I would never
look at mine again. :slight_smile:

So even if you don't like my architecture, please try to allow for the
possibility of all the functionality I've imagined.

In other words, I urge you not to construct a framework in which certain
things become impossible.

Good luck...

Hal

http://www.ruby-doc.org/core/classes/Class.html#M000472

What? You mean you haven't read ruby source code? Its
been around since 1.8.0 release. Every good programmer
should read sources.

--dross

···

--- Gavin Kistner <gavin@refinery.com> wrote:

a) I had no idea about the #inherited method.
Awesome. (In my defense,
there appears to be a bug in the docs; in my ri, my
own rdoc
compilation, and the current docs on ruby-doc.org,
Class#inherited
appears in the method list, but links to
#singleton_method_undefined.
e.g.

__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail

Oops, I forgot that Hashes in Ruby aren't ordered. But you get the idea, albeit with an array and an extra hash spot naming the parameter.

Or maybe by the time I'm done, Ruby will have added keyword-arguments natively supported :wink:

···

On Sep 23, 2004, at 9:20 PM, Gavin Kistner wrote:

class Foo::Bar::Whee::La < Foo::Bar::Whee
  def turn_on; ...; end
  def get_funky( speed, dance_style, song ); ...; end
  ...
  describe_method( :turn_on, "Turn On", "Turns on the switch" )
  describe_method( :get_funky,
    "Get Funky",
    "Breaks down to the beat.",
    {
      :speed => {
        :type => :integer,
        :min => 0,
        :max =>100
      },
      :dance_style => {
        :type => :list,
        :values => ['Hip Hop','Disco' ]
      },
      :song => {
        :type => :file,
        :mask => /\.(mp3|ogg|aiff)$/
      }
    }
  )
end

--
"When I am working on a problem I never think about beauty. I only think about how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong."
- R. Buckminster Fuller

While I'm normally a huge duck-typing-plus fan, I'm waffling in this
case. I think the reason is that I hope to have adaptor modules
(classes) being contributed from many different authors, providing me
with two challenges:

1) Given the large number of devices which fit under 'automation', I
want a hierarchical organization of devices which may be available. If
I leave it up to each author, I fear the user will install an adaptor
for their hardware and find that it's under LightSwitch/Dimmer while
every other similar adaptor is in LightSwitches/Adaptors. Or worse,
it's in DimmerSwitches, or Electrical/Wall/Analog/Slider.

If I understand rightly, I think the main constraint here is that you want
to organise the classes logically for the user to see when selecting one (to
create a new instance of a DimmerSwitch, for example, selecting it from a
tree rather than a long linear scrolling list).

You can always have a separate hierarchy for display purposes - or even
multiple different hierarchies, or the same class popping up in multiple
places in the same displayed hierarchy. It doesn't have to follow a class or
module hierarchy, and although it could, I think it will limit your
flexibility if you do.

You might, for example, want to give one view organised by manufacturer, and
another organised by device type.

and at the same time gives me a modicum of protection from
adaptor authors running over each others' code (through namespace
collisions).

If that's a concern, then modules should be organised by vendor/author, not
by function, so that each author is responsible for her own namespace.

module ACMEwidgets
  class OnOffSwitch
    ...
  end
end

That's another reason for making the organisation of objects for display
purposes separate from the module/class hierarchy (unless you're happy to
organise the display by vendor).

Of course, when you do "require 'acmewidgets'", you might want the classes
to appear in the correct places in the GUI hierarchy automagically. To do
that, I think they should export an "index card", rather like when a new
book arrives at the library - the librarian looks at the index card to work
out where to file it.

To support that, you need to define your own classification system, and then
modules can pick the one which suits them best.

require 'homecontrol'
module ACMEwidgets
  class OnOffSwitch
    def self.indexcard
      [Homecontrol::DevType::Switch, "ACME Widgets model 345X mains controller"]
    end
  end
end

Or you could use a constant with a well-known name within the class (the
details of the exporting mechanism are unimportant, IMO)

2) Similar to the above, the more leeway I give the authors in the
method naming convention, the more I fear that some switches will have
"Turn On", some will have "On", and some smartass authors will use
"Illuminate".Having thought about your points, I will certainly need to
look at the exposed methods for any adaptor and be able to use them.
(Who knows what bizarre functionality I will forget to include or
account for?) But, for the sake of consistency, I think I want to say
"Damnit, if you have a 'Light' class, you better have a :turn_on
method."

Then you're imposing a duck-typing system anyway. You need to provide
hard-written guidelines for module authors, for each sort of device, what
methods it should have.

Although really, I think that end users are not likely to be invoking
methods on their devices directly (unless they are Hal), in which case the
GUI can take care of it.

Well, as Robert correctly surmised, I don't actually want to implement
common code to them. (Some adaptor authors may want to, and they're
welcome to define their own modules if they wish). What I'm probably
going to end up with is something like:

class Device
  def self.mandatory_methods= ( *method_names )
    @mandatory = method_names.flatten.to_set
    @mandatory.merge( superclass.mandatory_methods ) if
superclass.respond_to? :mandatory_methods
  end

  def self.mandatory_methods
    @mandatory
  end

  self.mandatory_methods = [:name, :manufacturer, :models]

  class Light < self
    self.mandatory_methods = [:turn_on, :turn_off]
    class Dimmer < self
      self.mandatory_methods = [:level=, :level]
    end
  end
  
  class Outlet < self
    self.mandatory_methods = [:turn_on, :turn_off]
  end
end

along with a test for these methods as part of my #instance handling.

Why, then, have these arrays of mandatory_methods? What exactly are you
going to do if the class turns out not to have a method that you claim is
mandatory? I guess you could write code which automatically mails the
offending module back to the author and rm's it from the filesystem! :slight_smile:

Seriously... I am a believer in "do the simplest thing which can possibly
work". If the method doesn't exist - then when you call it, Ruby will raise
an exception for you. There's no need to pre-list the methods expected, nor
to do
     def turn_on
       raise "Not implemented!"
     end
in a superclass, because that's essentially the default behaviour which Ruby
has.

As another example: you were talking about using introspection to work out
which classes were subclasses of a top class, to identify which controllable
objects exist in the system. Sure you can do that - but it might be simpler
just to set a global variable which contains an array or hash of them (a
hash indexed by name for the GUI to display). After all, how often do you
add a new *class* of object to the system, like a new type of light switch
(as opposed to a new instance of a light switch)? You'll probably edit a .rb
file to add the appropriate "require" line anyway, so adding a new entry
into a global list of usable object classes isn't hard.

I suppose the introspection way is a little prettier:

module Homecontrol
  module Controllable # dummy, just flags that this object is controllable
  end
end

...

module ACMEwidgets
  class OnOffSwitch
    include Homecontrol::Controllable
    DESCRIPTION = "ACME Widgets model 345X mains controller"
  end
end

...

klasses =
ObjectSpace.each_object(Class) { |k| klasses << k if k.include? Homecontrol::Controllable }
klasses.each { |k| puts k.const_get('DESCRIPTION') }

But I'd think of that as cake-icing really.

Cheers,

Brian.

···

On Fri, Sep 24, 2004 at 12:20:54PM +0900, Gavin Kistner wrote:

(Referring to design of a home automation system, using hierarchies to manage )

I don't have time to go through point by point (as what you've written
is fairly verbose), so I'll try to to address what I see as the main
points. Sorry for the somewhat abrupt tone; I don't have time to
write "nicely", but the advice is well-meant :slight_smile: Feel free to ask for
more details.

The design as it is seems to conceptually mix
* Namespacing the modules of external authors (good idea)
* Defining interfaces they can plug into (good idea at a point)
* Using a hierarchy for these interfaces (most likely a good idea)
* Forcing every module to plug into a single point in a single
hierarchy (bad idea).

I'd separate these, and redesign a la this:

== Provide a namespace that authors can put their plug-in modules in. ==

I'd grab some area of the Ruby namespace (e.g,
HomeAutomation::Device), and then tell each author to use their e-mail
address "turned on its head" as a subnamespace they can manage
themselves. This would give any devices I wrote the module
HomeAutomation::Device::Org::FreeBSD::Eivind::<something>. It's long,
but it is unique.

== Define interfaces the can plug into ==

I'd grab a namespace for this (e.g. HomeAutomation::Interface, too,
and provide a bunch of plug in points/interfaces. Every device under
HomeAutomation::Device would export a list of what interfaces it
supported. Examples of interfaces would be ToggleSwitch or
VoltageRegulator; a device could be one of them (a pure toggle, or a
voltage regulator that had a minimum higher than 0), or both.

== Device finding ==

I'd just iterate over the namespace in HomeAutomation::Device to find
anything that's there.

I'd also start off with bringing anything written by external authors
"Into the fold" - import it into my own version control repository,
and give them commit privileges. That way, it is possible to refactor
the code together with the rest of the system. I'd also build the
system with heavy testing from the start, to enable that refactoring.

Remember: The standard advice is that you can't build a framework
until you've done (and deployed) three full systems. If you're going
to be more ambitious than that, at least cover all the bases you can.

Eivind.

"Gavin Kistner" <gavin@refinery.com> schrieb im Newsbeitrag news:F761507A-0DD2-11D9-9F40-000A959CF5AC@refinery.com...

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

Whoa.

:slight_smile:

<snip/>

c) While that second line is tricky, I actually prefer the hierarchical storage which occurs without it, since it allows for creating a hierarchical, visual representation of the class hierarchy. (Or is there a much cleaner/more built-in way than the following?)

class Foo
def self.inherited( k )
(@subclasses ||= ) << k
#def k.inherited(k2)
superclass.inherited(k2)
end

def self.show_subclasses( indent=0 )
puts "\t"*indent + self.inspect
@ subclasses.each{ |k|
k.show_subclasses( indent+1 )
} if @ subclasses.respond_to? :each
end

You can do "if @subclasses" here because nil ~ false.

class Bar < self
class Jee < self; end
end
class Whee < self; end
end
class Foo2 < Foo; end

Foo.show_subclasses( )
#=> Foo
#=> Foo::Bar
#=> Foo::Bar::Jee
#=> Foo::Whee
#=> Foo2

diff = @mandatory - Set.new(cl.instance_methods)

Ooh, nice, I was going to just yell on a first-not-found basis but the Set is a very elegant way of doing it.

Thank you very much for your help, Robert. It's exactly what I needed! :slight_smile:

You're welcome! I'm glad I could help.

    robert

···

On Sep 23, 2004, at 9:29 AM, Robert Klemme wrote:

INTRODUCTION

     As some of you no doubt know by this point, I am in the process of
validating a medium large (~14,000 line) application on 1.8.1 & 1.8.2
(this is the same one I was working on last spring, when I was brining
it from 1.6.8 & other three other languages into pure ruby 1.8.0).

     I have found what appears to be a bug. Prior to digging in to the
ruby source, I'm wondering if anyone else is working this, and/or if
anyone has any thoughts on it.

SAMPLE CODE

     The following example program exhibits the bug. A routine which
takes one or more arguments, any of which may be of any type, is
implemented as both a procedure and a Proc object. The semantics should
be consistent and equivalent, regardless of the version used or the
class or number of the actual parameters.

        def test(head,*rest)
            print "head = #{head.inspect} "
            print "rest = #{rest.inspect}\n"
            end
        
        test2 = Proc.new { |head,*rest|
            print "head = #{head.inspect} "
            print "rest = #{rest.inspect}\n"
            }
        
        test(:a,:b,:c)
        test([:a],:b,:c)
        test([:a,:b],:c)
        test([:a,:b,:c])
        print "-----------------\n"
        test2.call(:a,:b,:c)
        test2.call([:a],:b,:c)
        test2.call([:a,:b],:c)
        test2.call([:a,:b,:c])
        
EXPECTED RESULTS (actual, on 1.8.0)

     Under 1.8.0, we get exactly what we would expect under all tested
conditions--note that it always correctly distinguishes the first
argument from the rest, and produces the same result regardless of which
form is used:

        head = :a rest = [:b, :c]
        head = [:a] rest = [:b, :c]
        head = [:a, :b] rest = [:c]
        head = [:a, :b, :c] rest = []

···

-----------------
        head = :a rest = [:b, :c]
        head = [:a] rest = [:b, :c]
        head = [:a, :b] rest = [:c]
        head = [:a, :b, :c] rest = []
        
ACTUAL RESULTS (on 1.8.1 & 1.8.2)

     But under 1.8.1 & 1.8.2 the Proc object produces aberrant results
in the special case of a single array parameter, acting as if the actual
parameter had been prefixed with an "*" when in fact it was not:

        head = :a rest = [:b, :c]
        head = [:a] rest = [:b, :c]
        head = [:a, :b] rest = [:c]
        head = [:a, :b, :c] rest = []
        -----------------
        head = :a rest = [:b, :c]
        head = [:a] rest = [:b, :c]
        head = [:a, :b] rest = [:c]
        head = :a rest = [:b, :c]
                ^^^^^^^^^^^^^^^^^ incorrect
   
     Thoughts? In the absence of helpful pointers I will try to trace
it in the code myself, but I don't want to waste a lot of time if this
is a known issue being worked by someone else (but I'm, as always,
willing to pitch in and help if needed).

-- Markus

If I understand rightly, I think the main constraint here is that you want
to organise the classes logically for the user to see when selecting one (to
create a new instance of a DimmerSwitch, for example, selecting it from a
tree rather than a long linear scrolling list).

I'd say it's one of two main constraints, and I'd expand upon it a little:

1) I want to organize the classes logically into a thought-out hierarchy, ensuring that similar classes are correctly presented to the user.

2) I want to ensure that the methods exposed by similar classes are presented to the user in a very consistent manner.

In both cases, my philosophy says that I shouldn't trust authors to to the Right thing, despite any clear guidelines I might provide. They either won't read them, or where it's a matter of taste ("What's the best place for this in a hierarchy?") they'll differ from my Grand Plan.

My philosophy says that I should make it hard for them to screw up, not hard for them to succeed. It's hard for many people to come up with something elegant, given a blank canvas. My goal is to define the equivalent of a layout template for a magazine page, making it easier for them to create the end product. "Just plug stuff in here, here, and here, and choose one of the existing hierarchy categories, and you're done!"

Now, the implementation of these goals is not necessarily best served through the class hierarchy. For example, you're dead right-on that the namespace issue should be organized by author/manufacturer. I don't know what I was thinking, trying to place it under the hierarchy-of-devices namespace. (Well, I do know: I was yet-again confusing thoughts of namespace hierarchy with inheritance hierarchy.)

module GavinKistner
  def author; "Gavin Kistner; end
  def author_url; "http://phrogz.net/&quot;; end
  module Lutron
    def manufacturer; "Lutron"; end
    def manufacturer_url; "http://www.lutron.com/&quot;; end
    class OnOffDimmer < Foo::Bar::Electrical::Switches::Dimmer
      include GavinKistner
      include Lutron
      def name; "Lutron RadioRA Dimmer"; end
      #...
      register_adaptor
    end
  end
end

That's what I'm leaning towards now, and it's so much cleaner than
Foo::Bar::Electrical::Switches::Dimmer::LutronRadioRADimmer < Foo::Bar::Electrical::Switches::Dimmer

Authors *will* be able to screw up my application, due to the gloriously dynamic nature of Ruby. It would be trivial for a stubborn author to add their own bizarre path to the classification hierarchy. (And should be allowed, as well; I am not so egotistical to imagine that my initial hierarchy will be perfect.) But I'd prefer to provide them with an existing hierarchy to drop their class into, and provide guidelines for the basic functionality they should provide.

[Hrm, aside: given the need to allow authors to run arbitrary code to communicate to/from the device, is there any hope of preventing the malicious author from supplying a trojan-horse module which does nefarious things alongside a bit of benefit?]

You can always have a separate hierarchy for display purposes - or even
multiple different hierarchies, or the same class popping up in multiple
places in the same displayed hierarchy. It doesn't have to follow a class or
module hierarchy, and although it could, I think it will limit your
flexibility if you do.

You might, for example, want to give one view organised by manufacturer, and
another organised by device type.

Using a single class hierarchy to classify devices will make it hard to place the objects in more than one classification hierarchy, and hard to place the same object in more than one spot in the hierarchy. I think I need to ponder fully whether or not either of these are problems, however. At the moment, I don't think they are, but I haven't really tried to categorize every sort of automation device that I can think of.

However, it doesn't prevent me from creating alternate views which sort by manufacturer, author, etc. ... provided that each adaptor class exposes this information. (Yet another reason that I need to ensure that certain methods are present on every class.)

require 'homecontrol'
module ACMEwidgets
  class OnOffSwitch
    def self.indexcard
      [Homecontrol::DevType::Switch, "ACME Widgets model 345X mains controller"]
    end
  end
end

Although I think there's far too much information (and too dynamic) to handle in a single 'card', this makes me think of an interesting idea, allowing multiple classification hierarchies, and placement within more than one spot in the same hierarchy:

module ACMEwidgets
  class OnOffSwitch < Foo::Bar::Device
    stick_in( ByType::Electrical::Switch,
      ByType::Switches::Special,
      ByUse::Lighting::Switch )
  end
end

class Foo::Bar::Device
  def self.stick_in( *classes )
    classes.flatten.each{ |k|
      k.add_device( self )
    }
  end
end

Basically, creating namespace hierarchies which aren't used for inheritance, but simply for their hierarchical classification potential.

Although really, I think that end users are not likely to be invoking
methods on their devices directly (unless they are Hal), in which case the
GUI can take care of it.

You are correct; the GUI will invoke all methods directly. However, I can't have:

module Author1
  class OnOffSwitch
    def turn_on; ...; end
    def turn_off; ...; end
  end
end

module Author2
  class DimmerSwitch
    def get_jiggy; ...; end
    def sleepy_time; ...; end
  end
end

I "can't" have that because I'm planning on displaying exposed methods to the user via

pretty_names = DimmerSwitch.instance_methods.map{ |meth_name|
  meth_name.gsub(/_/,' ').gsub(/\b\w/){ |c| c.upcase }
}

Hence my desire to specify certain methods which they must supply (methods which are reasonable for that class of device). In doing so, I both ensure that the adaptor has a bare-minimum of functionality, and that its functionality is consistent with similar adaptors.

What exactly are you
going to do if the class turns out not to have a method that you claim is
mandatory? I guess you could write code which automatically mails the
offending module back to the author and rm's it from the filesystem! :slight_smile:

No, but in addition to throwing a warning to stdout, I very well may choose not to expose the adaptor in the GUI. "Finish the bare minimum of functionality if you want your code to be useful."

Thanks for the good advice, and continuing to make me think through details.
(Planning? What's that?)

···

On Sep 24, 2004, at 6:07 AM, Brian Candler wrote:

--
(-, /\ \/ / /\/

        test2 = Proc.new { |head,*rest|

try to replace `Proc.new' with `proc'

Guy Decoux

That still works, but it doesn't really solve the problem with Proc
(or, more to the point in my code, sub-classes of it). In fact, it
raises my eyebrows further. When I type:

     x.call(...)

how the complier interprets "..." shouldn't depend on the history of how
object x was created.

-- Markus

···

On Fri, 2004-09-24 at 10:18, ts wrote:

> test2 = Proc.new { |head,*rest|

try to replace `Proc.new' with `proc'

User defined analogs to proc() also fail in the same way:

     def my_proc(&b); b; end

     x = my_proc { |head,*rest| ... }

interpolates an erroneous "*" when (and only when) called with a single
array.

If (as appears to be the case) no one else is working on thus I'll start
trying to tack it down.

-- MarkusQ

···

On Fri, 2004-09-24 at 10:28, Markus wrote:

On Fri, 2004-09-24 at 10:18, ts wrote:

>
> > test2 = Proc.new { |head,*rest|
>
> try to replace `Proc.new' with `proc'
>

     That still works, but it doesn't really solve the problem with Proc
(or, more to the point in my code, sub-classes of it). In fact, it
raises my eyebrows further. When I type:

     x.call(...)

how the complier interprets "..." shouldn't depend on the history of how
object x was created.