[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Generalizing and Organizing Code

T. Onoma

11/23/2004 2:55:00 PM

I am finding it difficult to properly generalize and organize my code. Perhaps
others can offer some advice. Here is a snip two related parts. I have about
five others like these:

# section
def parse_section_set_tab? ; false ; end
def parse_section_set_tab ; tab ; end
def parse_section?( line, tab )
true
end
def parse_section_skip? ; false ; end
def parse_section( block )
text = block.collect{ |line| line.text }.join("\n")
md = /([=]+)[ ]*(.*?)[ ]*[=]+/.match( text )
raise "Unexpeted match error" unless md
level = md[1].length
title = md[2]
dobj = ArtML::DOM::Section.new( level, title )

until level > @document_stack.last.level do
@document_stack.pop
end
@document_stack.last << dobj # add this section to its parent
@document_stack << dobj # and pop it on
end


# paragraph
def parse_paragraph_set_tab? ; true ; end
def parse_paragraph_set_tab( line, tab ) ; line.indent ; end
def parse_paragraph?( line, tab )
line.kind != :paragraph or line.indent > tab
end
def parse_paragraph_skip? ; false ; end
def parse_paragraph( block )
text = block.collect{ |line| line.text }.join("\n")
content = text #parse_inline( text )
@document_stack.last << ArtML::DOM::Paragraph.new( content )
end

In the generalized code these are called using #send. Eg.

if send("parse_#{state}?", line, tab)
send("parse_#{state}", block)
end

As you can see, I am using a name prefix "trick" to encapsulate these. I
would like to encapsulate them better, but I have a couple of difficulties.
My first problem is the use of @document_stack which they all work on. The
second problem is that I'm not sure what the encapsulation would be --they
consist of both data and code (but not state), yet there is only ever one of
each, so they aren't classes. And yet they don't seem like objects either b/c
the code differs between each of them. Perhaps use a module? That only seems
to adjust the name trick (Paragraph::parse instead of paragraph_parse). Plus,
I then have to do 'class << self', which seems ugly to me. Nor does it seem
like that's how a "module" is meant to be used.

What is the proper way to handle this in a class-based OOPL?

Thanks,
T.


4 Answers

dblack

11/23/2004 3:20:00 PM

0

Robert Klemme

11/23/2004 3:29:00 PM

0


"trans. (T. Onoma)" <transami@runbox.com> schrieb im Newsbeitrag
news:200411230955.18950.transami@runbox.com...
> I am finding it difficult to properly generalize and organize my code.
Perhaps
> others can offer some advice. Here is a snip two related parts. I have
about
> five others like these:
>
> # section
> def parse_section_set_tab? ; false ; end
> def parse_section_set_tab ; tab ; end
> def parse_section?( line, tab )
> true
> end
> def parse_section_skip? ; false ; end
> def parse_section( block )
> text = block.collect{ |line| line.text }.join("\n")
> md = /([=]+)[ ]*(.*?)[ ]*[=]+/.match( text )
> raise "Unexpeted match error" unless md
> level = md[1].length
> title = md[2]
> dobj = ArtML::DOM::Section.new( level, title )
>
> until level > @document_stack.last.level do
> @document_stack.pop
> end
> @document_stack.last << dobj # add this section to its parent
> @document_stack << dobj # and pop it on
> end
>
>
> # paragraph
> def parse_paragraph_set_tab? ; true ; end
> def parse_paragraph_set_tab( line, tab ) ; line.indent ; end
> def parse_paragraph?( line, tab )
> line.kind != :paragraph or line.indent > tab
> end
> def parse_paragraph_skip? ; false ; end
> def parse_paragraph( block )
> text = block.collect{ |line| line.text }.join("\n")
> content = text #parse_inline( text )
> @document_stack.last << ArtML::DOM::Paragraph.new( content )
> end
>
> In the generalized code these are called using #send. Eg.
>
> if send("parse_#{state}?", line, tab)
> send("parse_#{state}", block)
> end
>
> As you can see, I am using a name prefix "trick" to encapsulate these.
I
> would like to encapsulate them better, but I have a couple of
difficulties.
> My first problem is the use of @document_stack which they all work on.
The
> second problem is that I'm not sure what the encapsulation would
be --they
> consist of both data and code (but not state),

I'd keep the option to put state in there, because you might need it some
time.

> yet there is only ever one of
> each, so they aren't classes.

But you can make them classes nevertheless.

> And yet they don't seem like objects either b/c
> the code differs between each of them. Perhaps use a module? That only
seems
> to adjust the name trick (Paragraph::parse instead of paragraph_parse).
Plus,
> I then have to do 'class << self', which seems ugly to me. Nor does it
seem
> like that's how a "module" is meant to be used.
>
> What is the proper way to handle this in a class-based OOPL?

If I'd strive for the most OO solution I'd do this:

class Parser
class BaseProc
def self.children() @children ||= [] end
def self.inherited(cl) self.children << cl end

attr_accessor :parent

def initialize(parent) self.parent = parent end
def parse(*a) false end
end

class Section < BaseProc
def parse(line, tab)
# ...
end
end

class Paragraph < BaseProc
def parse(line, tab)
# ...
end
end

BaseProc.children.each do |cl|
define_method(cl.name.downcase.gsub(/^.*::/, '')) { @handlers[cl] }
end


attr_accessor :document_stack
attr_reader :section, :paragraph

def initialize
@handlers = Hash.new {|h,k| h[k]=k.new(self)}
end

end


p = Parser.new
p.section.parse "s",""
p.paragraph.parse "p", ""



But this one does work, too

class Parser
attr_reader :section, :paragraph

def initialize
@section = lambda { |*a| puts "section: @foo=#{@foo}
args=#{a.inspect}" }
@paragraph = lambda { |*a| puts "paragraph: @foo=#{@foo}
args=#{a.inspect}" }
@foo = "something"
end

end

p = Parser.new
p.section.call "s"
p.paragraph.call "p"

My 0.02EUR...

Kind regards

robert

T. Onoma

11/25/2004 7:07:00 AM

0

On Tuesday 23 November 2004 10:19 am, David A. Black wrote:
| I don't think I'd characterize module-wise organization as a name
| trick of the same kind as hanging things off a method name. Also,
| don't be squeamish about class << self -- the "<< obj" notation is
| really just an alternative to the "SomeConstant" notation, designed to
| work with anonymous classes. Unfortunately it has a reputation as
| tricky or 3vi1 or whatever, but it really isn't :-)

Fair enough. I think it gets the reputation b/c of how it looks --it looks
"tricky" and is not readily intuitive.

| > What is the proper way to handle this in a class-based OOPL?
|
| I can't claim definitiveness by any means, but here's a mock-up of one
| possibility (untested):
|
| [snip excleent exmaple]

I very much like your example. Nice and clean. Easy to follow. And I've used a
part of it: the state is the object. That part's especially nice.
Unfortunately the document stack being a class instance var worries me as I
do not think it thread safe. But that's okay, I also used a part of Robert's
example to remedy that.

Thanks and Happy Thanksgiving, David.
T.


T. Onoma

11/25/2004 7:18:00 AM

0

On Tuesday 23 November 2004 10:33 am, Robert Klemme wrote:
| I'd keep the option to put state in there, because you might need it some
| time.

Good point.

| > yet there is only ever one of
| > each, so they aren't classes.
|
| But you can make them classes nevertheless.

True.

| If I'd strive for the most OO solution I'd do this:
|
| class Parser
| class BaseProc
| def self.children() @children ||= [] end
| def self.inherited(cl) self.children << cl end
|
| attr_accessor :parent
|
[snip]
|
| BaseProc.children.each do |cl|
| define_method(cl.name.downcase.gsub(/^.*::/, '')) { @handlers[cl] }
| end
|
|
| attr_accessor :document_stack
| attr_reader :section, :paragraph
|
| def initialize
| @handlers = Hash.new {|h,k| h[k]=k.new(self)}
| end
|
| end

Took me a few minutes to get this one. I've never used Hash block form of
instantiation before. The use of parent is also quite insightful. So I ended
up doing it in part as you suggest, with a bit of David's solution mixed in
too, and my own of course. I'm still not perfectly satisfied but it's
definitely improving.

Thanks and Happy Givings as well,
T.