Generalizing and Organizing Code

T

trans. (T. Onoma)

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

David A. Black

Hi --

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

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

class Document

def Document.stack
@stack ||= []
end

module SectionLike

def parse_set_tab?
false
end

def parse?(line,tab)
true
end

def parse_skip?
false
end

def parse(block)
# ...
Document.stack.pop # or whatever
end

end

# ...

module Section
extend SectionLike
end

end

state = Document::Section

if state.parse?(line, tab)
state.parse(block)
end

(The separation of SectionLike and Section is in case you can use it
to consolidate and cascade from one module to another and save some
method definitions.)


David
 
R

Robert Klemme

trans. (T. Onoma) said:
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

trans. (T. Onoma)

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

trans. (T. Onoma)

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.
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top