Complex Library Object/Class and its Interface

Been beating my head against this one for far too long now, and I think
others might be interested in it. I have a lib where the user needs to
define a set of "things" that have both assignable data and assignable
functions. Also they should be extensible so the user can add
additional functionality. It's a bit difficult to explain so I will
just give my exmaple.

class Parser::Token

class << self
   def esc(str) ; Regexp.escape(str) ; end
   def unit? ; @unit ; end
   def unit(u) ; @unit = ( u ? true : false ) ; end
   def exclusive? ; @exclusive ; end
   def exclusive(excl) ; @exclusive = ( excl ? true : false ) ; end
   def priority ; @priority ||= 10 ; end
   def priority(pri) ; @priority = pri ; end
   def <=> ; @priority ; end
   def start_exp(match=nil) ; @start.call(match) ; end
   def start(&blk) ; @start = blk ; end
   def stop_exp(match=nil) ; @stop.call(match) ; end
   def stop(&blk) ; @stop = blk ; end
end

attr_reader :parent, :match, :body

def initialize( parent, match )
   @parent = parent
   @match = match
   @body = []
end

def <<( content ) ; @body << content ; end
def last ; @body.empty? ? @body : @body.last ; end
def empty? ; @body.empty? ; end
def pop ; @body.pop ; end
def each(&blk) ; @body.each(&blk) ; end

end #class Parser::Token

So then the lib usr can define each of their "things" based on this,
minimally:

class Marker < Parser::Token

   exclusive false
   start { %r{ \< (.*?) \> }mx }
   stop { |match| %r{ \< [ ]* (#{esc(match[1])}) (.*?) \. \> }mx }

   #... user's optional methods ...

It toke me some time to work this out in itself. And I thought I had
finally gotten a fairly nice interface here. But to my dismay, I just
discovered that I can't subclass Marker b/c I loose the definitions of
the above attributes (ie. exclusive, start, stop). So now I'm back to
rethinking the whole setup. (Also note that if the user's methods
redefine #initialize or the other Token methods, it might cause the
parser that uses it to break --another slight down side and a possible
use case for RCR #198)

So how does one properly build something like this in a nice neat way?
Thanks,
T.

Hi,

>class Marker < Parser::Token
>
> exclusive false
> start { %r{ \< (.*?) \> }mx }
> stop { |match| %r{ \< [ ]* (#{esc(match[1])}) (.*?) \. \> }mx }
>
> #... user's optional methods ...

It toke me some time to work this out in itself. And I thought I had
finally gotten a fairly nice interface here. But to my dismay, I just
discovered that I can't subclass Marker b/c I loose the definitions of
the above attributes (ie. exclusive, start, stop). So now I'm back to
rethinking the whole setup. (Also note that if the user's methods
redefine #initialize or the other Token methods, it might cause the
parser that uses it to break --another slight down side and a possible
use case for RCR #198)

So how does one properly build something like this in a nice neat way?

This is the first thought that entered my mind: turn the configuration methods into instance methods, move the method calls to #initialize, and call super there:

class Marker < Parser::Token
   def initialize
     super
     exclusive false
     start { %r{ \< (.*?) \> }mx }
     stop { |match| %r{ \< [ ]* (#{esc(match[1])}) (.*?) \. \> }mx }
   end
end

class ExclusiveMarker < Marker
   def initialize
     super
     exclusive true
   end
end

Maybe that would work?

···

On 24.1.2005, at 18:15, Trans wrote:

--
Ilmari Heikkinen

So how does one properly build something like this in a nice neat way?

Sounds like you probably want to make those methods be macros,
building instance methods that do what you want, rather than making
them class singleton methods.

Trans wrote:

It toke me some time to work this out in itself. And I thought I had
finally gotten a fairly nice interface here. But to my dismay, I just
discovered that I can't subclass Marker b/c I loose the definitions of
the above attributes (ie. exclusive, start, stop).

What exactly do you mean when you say you "lose the definitions"? The definitions for Marker will remain accessible via the Marker scope, but a derived class will start out with its own set of (empty) definitions. Do you want auto-inherit for class instance variables?

class Parent
   class << self
     def set_name(n)
       @name = n
     end
     def name
       (defined? @name) ? @name : superclass.name
     end
   end
   set_name "Smith"
end

class Child < Parent
end

class Grandchild < Child
   set_name "Jones"
end

puts "Parent.name = #{Parent.name}"
puts "Child.name = #{Child.name}"
puts "Grandchild.name = #{Grandchild.name}"

Prints:

Parent.name = Smith
Child.name = Smith
Grandchild.name = Jones

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/>

Aahhh and 10 seconds after I sent that, another way surfaced:

Parser::Token configurators could use define_method instead of @class_ivar, that way they'd be inherited.

Mmh. I'm not really happy with either of my suggestions. Oh well, may others have better ways.

···

On 24.1.2005, at 18:40, Ilmari Heikkinen wrote:

Hi,
On 24.1.2005, at 18:15, Trans wrote:

>class Marker < Parser::Token
>
> exclusive false
> start { %r{ \< (.*?) \> }mx }
> stop { |match| %r{ \< [ ]* (#{esc(match[1])}) (.*?) \. \> }mx }
>
> #... user's optional methods ...

It toke me some time to work this out in itself. And I thought I had
finally gotten a fairly nice interface here. But to my dismay, I just
discovered that I can't subclass Marker b/c I loose the definitions of
the above attributes (ie. exclusive, start, stop). So now I'm back to
rethinking the whole setup. (Also note that if the user's methods
redefine #initialize or the other Token methods, it might cause the
parser that uses it to break --another slight down side and a possible
use case for RCR #198)

So how does one properly build something like this in a nice neat way?

This is the first thought that entered my mind: turn the configuration methods into instance methods, move the method calls to #initialize, and call super there:

class Marker < Parser::Token
  def initialize
    super
    exclusive false
    start { %r{ \< (.*?) \> }mx }
    stop { |match| %r{ \< [ ]* (#{esc(match[1])}) (.*?) \. \> }mx }
  end
end

class ExclusiveMarker < Marker
  def initialize
    super
    exclusive true
  end
end

Maybe that would work?

--
Ilmari Heikkinen

Thanks Ilmari Heikkinen,

A fair suggestion. The problem though is I'd have to make an instance
of the class just to read the exclusive, start and stop information. I
thought about doing it that way and creating dummy tokens to use, but
using dummy tokens seemed rather unRubyish. It would be better if I
could get that info with out instantiating the class.

T.

Aredridel,

Would that solve the access problem (i.e. being getting that info from
the class). I just tried:

class Parser::Token

class << self

   def inherited(klass)
     klass.unit self.unit?
     klass.exclusive self.exclusive?
     klass.start &self.start
     klass.stop &self.stop
   end

    #...

That solve the inheritence problem, it look like. But I'm jave
troubles. I'm starting to think that the problem is simply that I'm
trying to combine two different objects into one.

T.

Trans wrote:

Aredridel,

Would that solve the access problem (i.e. being getting that info from
the class). I just tried:

> class Parser::Token
>
> class << self
>
> def inherited(klass)
> klass.unit self.unit?
> klass.exclusive self.exclusive?
> klass.start &self.start
> klass.stop &self.stop
> end
>
> #...

That solve the inheritence problem, it look like. But I'm jave
troubles. I'm starting to think that the problem is simply that I'm
trying to combine two different objects into one.

This inheritance will not be updated when you change the value in the parent class.

This seems to come up often enough that maybe ruby needs a built in way to inherit mutable and overridable variables in the class hierarchy. Class vars, class instance vars, and constants don't quite do this.

You can do it with superhash:

require 'superhash'

class Token
   class_superhash :stuff
   class << self
     def start(*args)
       if args.size > 0
         self.stuff[:start] = args[0]
       else
         self.stuff[:start]
       end
     end
   end
end

class Marker < Token
   start /marker start/
end

class FooMarker < Marker
end

p Marker.start
p FooMarker.start

__END__

Output:

/marker start/

Make those methods instance methods, but...

Don't make it a class, make it a module. Extend it by itself.

Instead of

class Marker < Parser::Token
....

do special customizations as

module Marker
  extend self
  extend Parser::Token
....

Csaba

···

On 2005-01-24, Trans <transfire@gmail.com> wrote:

A fair suggestion. The problem though is I'd have to make an instance
of the class just to read the exclusive, start and stop information. I
thought about doing it that way and creating dummy tokens to use, but
using dummy tokens seemed rather unRubyish. It would be better if I
could get that info with out instantiating the class.

This seems to come up often enough that maybe ruby needs
a built in way to inherit mutable and overridable variables in
the class hierarchy. Class vars, class instance vars, and
constants don't quite do this.

Does seem like that's something there should be a reasonbly straight
foward way to do.