Created
January 26, 2009 19:41
-
-
Save rue/52933 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Node in the URL resolution tree. | |
# | |
class Node | |
attr_accessor :component, :definition | |
attr_accessor :statics, :placeholders, :consumables, :maybes, :rest | |
def initialize(component) | |
@component = component | |
@statics = [] | |
@placeholders = [] | |
@consumables = [] | |
@maybes = [] | |
@rest = nil | |
@definition = nil | |
end | |
end | |
# Define a mapping from a request to a resource. | |
# | |
# TODO: Once done building, flatten into an Array. Needs | |
# polymorphic support. | |
# | |
# TODO: Define backend methods rather than using block? | |
# | |
def on(methods, *pathspec, &block) | |
case methods | |
when Array | |
# Nothing | |
when true | |
methods = [:get, :put, :post, :head] | |
else | |
methods = [methods] | |
end | |
methods.each {|m| | |
tier = maps[m] | |
pathspec.each_with_index {|component, i| | |
case component | |
when String, nil # Static and faked empty path | |
current = tier.statics.find {|n| n.component == component} | |
unless current | |
current = Node.new component | |
tier.statics << current | |
end | |
tier = current | |
when Symbol # Placeholder | |
# TODO: Could combine same label, but..point? | |
current = Node.new component | |
tier.placeholders << current | |
tier = current | |
when Range # Some number of required and/or optional arguments | |
# TODO: Forgetting to cap the range? | |
required = component.begin | |
required.times { | |
current = Node.new "<consumable>" | |
tier.consumables << current | |
tier = current | |
} | |
# Figure out if we can consume the rest. | |
# TODO: Support other types of terminators? | |
tail = pathspec[(i + 1)..-1].find {|c| c.kind_of? String } | |
# Last required argument must have a definition | |
tier.definition = block if !tail and required > 0 | |
if component.end == -1 | |
raise "Rest of arguments must be last" unless i == (pathspec.size - 1) | |
current = Node.new "<rest>" | |
tier.rest = current | |
tier = current | |
break | |
end | |
# Then the optionals | |
component.to_a[1..-1].each { | |
current = Node.new tail # I.e. storing the one we are expecting | |
current.definition = block # Need this if not getting all. | |
tier.maybes << current | |
tier = current | |
} | |
tier.definition = nil # UGH. | |
when true | |
component = 1..-1 | |
redo | |
else | |
raise "wtf?" | |
end | |
} | |
raise "Mapping already defined!" if tier.definition | |
tier.definition = block | |
} | |
end | |
end | |
# Later | |
# ... | |
# | |
# Occurs in a specific order. | |
# | |
# TODO: Needs a bunch of optimisation. | |
# | |
def descend(node, path) | |
if path.empty? | |
return node if node.definition | |
return | |
end | |
component, rest = path.at(0), path[1..-1] | |
# Static strings | |
node.statics.each {|n| | |
# This _must_ match (or not) since it is not variable | |
return descend n, rest if n.component == component | |
} | |
# Maybes | |
node.maybes.each {|n| | |
# There may be multiple statics here | |
if n.component == component | |
found = descend n, rest | |
found = descend n, path unless found | |
return found if found | |
end | |
} | |
# Placeholders | |
node.placeholders.each {|n| | |
found = descend n, rest | |
if found | |
captured[n.component] = component | |
return found | |
end | |
} | |
# Consumables | |
node.consumables.each {|n| | |
found = descend n, rest | |
return found if found | |
} | |
# Deeper into the maybes that did not match here | |
node.maybes.each {|n| | |
found = descend n, rest | |
return found if found | |
} | |
# All of the remaining path | |
node.rest | |
end | |
# | |
# | |
def get(request) | |
node = descend @maps[:get], extract_path(request) | |
raise "No node found" unless node | |
block = node.definition or raise "No block in #{node.inspect} of #{@maps[:get].inspect} for #{request.path}" | |
instance_eval &block | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment