Complex Library Object/Class and its Interface

T

Trans

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

Ilmari Heikkinen

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

Ilmari Heikkinen

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.


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

Trans

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

Aredridel

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

Trans

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

Glenn Parker

Trans said:
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
 
J

Joel VanderWerf

Trans said:
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/
/marker start/
 
C

Csaba Henk

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.

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
 
T

Trans

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top