On Tue, Sep 18, 2007 at 01:17:41PM +0100, Brian Candler wrote:
> Here's an outline of how I think it could be done.

This was broken as I was using (?> ...), which permanently eats characters
and then won't allow the data to match a different branch. Fixed version
attached, with some other cleanups.

Incidentally, instead of writing

    (?: () ...branch1... ) |
    (?: () ...branch2... )

it would be simpler to write

    ( ...branch1... ) |
    ( ...branch2... )

since the outer brackets also capture the entire match, which gives a
suitable non-nil sentinel to mark the start of the branch.

However this way means I need a fast way to detect the first non-nil element
in the match array (rather than the first empty string element) and I
couldn't think of a way to do that faster than

    i = (0...m.size).find { |x| m[x] }

Also, I decided to anchor the whole expression, which means the individual
branch regexps don't need to be anchored, but instead we could insist that
the branches need to have their own anchors (which I think is the current
behaviour)

Regards,

Brian.
class POCRouter
  class Path
    attr_reader :regexp, :num_captures, :blocks
    def initialize(regexp, blocks=[])
      @regexp = regexp
      @num_captures = @regexp.source.scan(%r{\((?!\?)}).size  # FIXME, e.g. \( 
or [(]
      @blocks = blocks
    end
  end

  attr_reader :paths, :re, :capindex
  def initialize
    @paths = []
  end

  def match(regexp, &blk)
    regexp = Regexp.new(regexp) unless regexp.is_a? Regexp
    if path = @paths.find { |p| p.regexp == regexp }
      path.blocks << blk
    else
      @paths << Path.new(regexp, [blk])
    end
  end

  def compile
    @re = %r{\A(?:
[EMAIL PROTECTED] { |p| "(?: () #{p.regexp})" }.join("|\n")}
)\z}x  # extended RE is easier to read for debugging
    @capindex = {}
    i = 0
    @paths.each do |p|
      @capindex[i] = p
      i += p.num_captures + 1
    end
  end

  def inspect
    (@re || @paths).inspect
  end

  def run(path, request={})
    m = @re.match(path).captures
    i = m.index("") || (return nil)
    path = capindex[i]
    path.blocks.each do |blk|
      res = blk.call(request, m[i+1, path.num_captures])
      return res if res
    end
    nil
  end
end

r = POCRouter.new
r.match(%r{/admin/(\w+)}) { |req,m| p m; {:action=>"www"} }
r.match(%r{/(\w+)/(\w+)}) { |req,m| p m; {:action=>"xxx"} if 
req[:method]=="delete" }
r.match(%r{/(\w+)/(\w+)}) { |req,m| {:action=>"yyy"} if req[:method]=="post" }
r.match(%r{/(\w+)/(\w+)}) { |req,m| {:action=>"zzz"} }
p r
r.compile
p r
p r.run("/admin/wibble", :method=>"get")
p r.run("/bibble/boing", :method=>"delete")
p r.run("/bibble/boing", :method=>"post")
p r.run("/bibble/boing", :method=>"get")
_______________________________________________
Merb-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/merb-devel

Reply via email to