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