From: Trans Date: 2005-01-25T01:15:52+09:00 Subject: 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.