On Tue, Sep 18, 2007 at 12:01:07PM +0100, Brian Candler wrote:
> However I suspect this can be generalised. One way I'm thinking is to
> combine all the path regexps into one mega path matching regexp, and let the
> regexp engine take care of it.
...
> However I'm not sure if there's a cheap way to identify which branch of a
> regexp has matched.

Here's an outline of how I think it could be done.

--- 8< ---------------------------------------------------------------------
# Part one: show how it works manually
RE = %r{\A(?:
  (?> ()/admin/(\w+)) |
  (?> ()/(\w+)/(\w+))
)\z}x
puts RE.inspect

ACTIONS = {
        1 => proc { |m| puts "www: #{m.inspect}" },
        3 => proc { |m| puts "xxx/yyy/zzz: #{m.inspect}" },
}

def test(patt)
  m = RE.match(patt).to_a
  puts m.inspect
  i = m.index("")
  ACTIONS[i].call(m[i+1..-1])
end

test("/admin/wibble")
test("/bibble/boing")
puts "----"

# Part two: proof of concept of automatic regexp construction
class POC
  attr_reader :regexps, :re, :actions
  def initialize
    @regexps = []   #  [[regexp,[action,action]], [regexp,[action,action], ...]
  end

  def match(regexp, action)
    regexp = Regexp.new(regexp) unless regexp.is_a? Regexp
    if entry = @regexps.find { |e| e[0] == regexp }
      entry[1] << action
    else
      @regexps << [regexp, [action]]
    end
  end

  def compile
    @re = %r{\A(?:[EMAIL PROTECTED] { |r| "(?>()#{r[0]})" }.join("|")})\z}
    @actions = {}
    i = 1
    @regexps.each do |r,a|
      @actions[i] = a
      i += 1 + r.source.scan(%r{\((?!\?)}).size   # ergh, count number of 
captures
    end
  end

  def run(patt, request={})
    m = @re.match(patt).to_a
    i = m.index("")
    @actions[i].each { |a| res = a.call(request, m[i+1..-1]); return res if res 
}
    nil
  end
end

r = POC.new
r.match(%r{/admin/(\w+)}, proc { |req,path| {:action=>"www"} })
r.match(%r{/(\w+)/(\w+)}, proc { |req,path| {:action=>"xxx"} if 
req[:method]=="delete" })
r.match(%r{/(\w+)/(\w+)}, proc { |req,path| {:action=>"yyy"} if 
req[:method]=="post" })
r.match(%r{/(\w+)/(\w+)}, proc { |req,path| {:action=>"zzz"} })
p r.regexps
r.compile
p r.re
p r.actions
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")
--- 8< ---------------------------------------------------------------------

Seem feasible?

Other things to do:

- create procs automatically for the simple 'non-deferred' routing, which is
easy since procs are first-class objects. No eval required :-)

- map m[0], m[1], m[2]... into the named params fields like :controller,
:action etc if the original regexp had :foo placeholders

Regards,

Brian.
_______________________________________________
Merb-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/merb-devel

Reply via email to