Robert Klemme
11/23/2004 3:29:00 PM
"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