[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Complex Library Object/Class and its Interface

Trans

1/24/2005 4:14:00 PM

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.

9 Answers

Ilmari Heikkinen

1/24/2005 4:41:00 PM

0

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



Ilmari Heikkinen

1/24/2005 4:49:00 PM

0

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



Trans

1/24/2005 5:00:00 PM

0

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

1/24/2005 5:32:00 PM

0

> 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

1/24/2005 7:10:00 PM

0

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.

Glenn Parker

1/24/2005 8:36:00 PM

0

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


Joel VanderWerf

1/24/2005 8:36:00 PM

0

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/
/marker start/


Csaba Henk

1/24/2005 8:55:00 PM

0

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.

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

Trans

1/25/2005 3:49:00 PM

0

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