Complex Library Object/Class and its Interface

Lähettäjä: "Trans" <transfire@gmail.com>
Aihe: 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.

I think you may be making this more difficult than it is. What
are you trying to achieve? Just reduce the syntax or are you
intending for Parser to automatically be able to use the user-
defined class? Conventionally:

class Token
  def init
    @var = 'foo'
  end

  def fun
    # ...
  end
  
  attr_accessor :some_attr
end

class Marker < Token
  def initialize
    super
  end

  # fun is available here
  # some_attr is available here
  # @var is available here

  def my_fun
    # ...
  end
end

E

" I think you may be making this more difficult than it is. What
" are you trying to achieve?

Yes, I think that's part of it. I haven't been able to get my head
around exactly what is at the heart of the problem. So I've thought
about it some more and played around with everyones suggestions. THANK
YOU ALL for those suggestions btw!

I realized I made a slight mistake about the inhertience issue. It
doesn't actually buy me anything unless there is an instance var
embedded in the regular expression so that they'll vary from token to
token. But that turns out to be tricky too, in just such a way, in
fact, as to lead me to realize what's really going on.

Essentially I do indeed have two different things: a "match token" that
determines how the parser finds tokens, and a "build token" that the
parser will create from what it finds. The trick is that the two have a
ONE-TO-ONE coorespondance --for a given match token object there is one
and only one build token class. So that's what is messing me up. How do
I code that cleanly?

Thanks,
T.

Ugh! No, it is a ONE-TO-MANY between class and object.

.. [ Token Class ]<-------*[ Listener Instance ]
Now maybe I can figure this b* out!

T.