lib/usher/node.rb in joshbuddy-usher-0.5.3 vs lib/usher/node.rb in joshbuddy-usher-0.5.4
- old
+ new
@@ -4,35 +4,52 @@
class Node
Response = Struct.new(:path, :params, :remaining_path, :matched_path)
- attr_reader :lookup, :greedy_lookup
- attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods
+ attr_reader :normal, :greedy, :request
+ attr_accessor :terminates, :request_method_type, :parent, :value, :request_methods
def initialize(parent, value)
@parent = parent
@value = value
- @lookup = Hash.new
- @greedy_lookup = Hash.new
- @exclusive_type = nil
+ @request = nil
+ @normal = nil
+ @greedy = nil
+ @request_method_type = nil
end
+
+ def activate_normal!
+ @normal ||= Hash.new
+ end
- def upgrade_lookup
- @lookup = FuzzyHash.new(@lookup)
+ def activate_greedy!
+ @greedy ||= Hash.new
end
- def upgrade_greedy_lookup
- @greedy_lookup = FuzzyHash.new(@greedy_lookup)
+ def activate_request!
+ @request ||= Hash.new
end
+ def upgrade_normal!
+ @normal = FuzzyHash.new(@normal)
+ end
+
+ def upgrade_greedy!
+ @greedy = FuzzyHash.new(@greedy)
+ end
+
+ def upgrade_request!
+ @request = FuzzyHash.new(@request)
+ end
+
def depth
@depth ||= @parent.is_a?(Node) ? @parent.depth + 1 : 0
end
def greedy?
- !@greedy_lookup.empty?
+ @greedy && !@greedy.empty?
end
def self.root(route_set, request_methods)
root = self.new(route_set, nil)
root.request_methods = request_methods
@@ -43,16 +60,26 @@
@terminates
end
def pp
$stdout << " " * depth
- $stdout << "#{depth}: #{value.inspect} #{!!terminates?}\n"
- @lookup.each do |k,v|
+ $stdout << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n"
+ normal.each do |k,v|
$stdout << " " * (depth + 1)
- $stdout << "#{k} ==> \n"
+ $stdout << ". #{k.inspect} ==> \n"
v.pp
- end
+ end if normal
+ greedy.each do |k,v|
+ $stdout << " " * (depth + 1)
+ $stdout << "g #{k.inspect} ==> \n"
+ v.pp
+ end if greedy
+ request.each do |k,v|
+ $stdout << " " * (depth + 1)
+ $stdout << "r #{k.inspect} ==> \n"
+ v.pp
+ end if request
end
def add(route)
route.paths.each do |path|
set_path_with_destination(path)
@@ -65,41 +92,42 @@
end
end
def unique_routes(node = self, routes = [])
routes << node.terminates.route if node.terminates
- node.lookup.values.each do |v|
+ node.normal.values.each do |v|
unique_routes(v, routes)
- end
- node.greedy_lookup.values.each do |v|
+ end if node.normal
+ node.greedy.values.each do |v|
unique_routes(v, routes)
- end
+ end if node.greedy
+ node.request.values.each do |v|
+ unique_routes(v, routes)
+ end if node.request
routes.uniq!
routes
end
- def find(usher, request, original_path, path, params = [], position = 0)
- if exclusive_type
- [lookup[request.send(exclusive_type)], lookup[nil]].each do |n|
- if n && (ret = n.find(usher, request, original_path, path.dup, params.dup, position))
- return ret
- end
- end
- nil
- elsif terminates? && (path.size.zero? || terminates.route.partial_match?)
- if terminates.route.partial_match?
- Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position])
+ def find(usher, request_object, original_path, path, params = [], position = 0)
+ if request_method_type
+ if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position))
+ ret
+ elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position))
+ ret
else
- Response.new(terminates, params, nil, original_path)
+ nil
end
-
- elsif !path.size.zero? && (greedy? && (match_with_result_output = greedy_lookup.match_with_result(whole_path = original_path[position, original_path.size])))
+ elsif terminates? && (path.empty? || terminates.route.partial_match?)
+ terminates.route.partial_match? ?
+ Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) :
+ Response.new(terminates, params, nil, original_path)
+ elsif !path.empty? && (greedy? && (match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size])))
next_path, matched_part = match_with_result_output
position += matched_part.size
params << [next_path.value.name, whole_path.slice!(0, matched_part.size)]
- next_path.find(usher, request, original_path, whole_path.size.zero? ? whole_path : usher.splitter.url_split(whole_path), params, position)
- elsif !path.size.zero? && (next_part = lookup[part = path.shift] || lookup[nil])
+ next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.url_split(whole_path), params, position)
+ elsif !path.empty? && normal && (next_part = normal[part = path.shift] || normal[nil])
position += part.size
case next_part.value
when Route::Variable::Glob
params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name
loop do
@@ -113,11 +141,11 @@
break
elsif !usher.delimiter_chars.include?(part[0])
next_part.value.valid!(part)
params.last.last << part
end
- if path.size.zero?
+ if path.empty?
break
else
part = path.shift
end
end
@@ -129,61 +157,109 @@
next_path_part = path.shift
position += next_path_part.size
params.last.last << next_path_part
end
end
- next_part.find(usher, request, original_path, path, params, position)
+ next_part.find(usher, request_object, original_path, path, params, position)
else
nil
end
end
private
+
def set_path_with_destination(path, destination = path)
- parts = path.parts.dup
- request_methods.each do |type|
- parts.push(Route::RequestMethod.new(type, path.route.conditions[type])) if path.route.conditions && path.route.conditions.key?(type)
+ node = path.parts.inject(self){ |node, key| process_path_part(node, key) }
+ node = process_request_parts(node, request_methods_for_path(path))
+
+ while node.request_method_type
+ node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)))
end
-
- current_node = self
- while !parts.size.zero? || current_node.exclusive_type
+
+ node.terminates = destination
+ end
+
+ def request_method_index(type)
+ request_methods.index(type)
+ end
+
+ def process_request_parts(node, parts)
+ while parts.any?{ |p| !p.trivial? }
key = parts.shift
- target_node = case key
- when Route::RequestMethod
- current_node.upgrade_lookup if key.value.is_a?(Regexp)
- if current_node.exclusive_type == key.type
- current_node.lookup[key.value] ||= Node.new(current_node, key)
- elsif current_node.lookup.empty?
- current_node.exclusive_type = key.type
- current_node.lookup[key.value] ||= Node.new(current_node, key)
- else
+
+ next if key.trivial?
+
+ node.activate_request!
+
+ node = if node.request_method_type.nil?
+ node.request_method_type = key.type
+ node.upgrade_request! if key.value.is_a?(Regexp)
+ node.request[key.value] ||= Node.new(node, key)
+ else
+ case request_method_index(node.request_method_type) <=> request_method_index(key.type)
+ when -1
parts.unshift(key)
- current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil))
+ node.request[key.value] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil))
+ when 0
+ node.upgrade_request! if key.value.is_a?(Regexp)
+ node.request[key.value] ||= Node.new(node, key)
+ when 1
+ previous_node = node.parent
+ current_node_entry_key = nil
+ current_node_entry_lookup = nil
+ [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l|
+ current_node_entry_key = l.each{|k,v| break k if node == v}
+ current_node_entry_lookup = l and break if current_node_entry_key
+ end
+
+ current_node_entry_lookup.respond_to?(:delete_value) ?
+ current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node}
+
+ new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil))
+ new_node.activate_request!
+ new_node.request_method_type = key.type
+ current_node_entry_lookup[current_node_entry_key] = new_node
+ node.parent = new_node
+ new_node.request[nil] = node
+ parts.unshift(key)
+
+ new_node
end
- when Route::Variable
- upgrade_method, lookup_method = case key
- when Route::Variable::Greedy
- [:upgrade_greedy_lookup, :greedy_lookup]
- else
- [:upgrade_lookup, :lookup]
- end
-
- if key.regex_matcher
- current_node.send(upgrade_method)
- current_node.send(lookup_method)[key.regex_matcher] ||= Node.new(current_node, key)
- else
- current_node.send(lookup_method)[nil] ||= Node.new(current_node, key)
- end
- else
-
- current_node.upgrade_lookup if key.is_a?(Regexp)
- current_node.lookup[key] ||= Node.new(current_node, key)
end
- current_node = target_node
end
+ node
+ end
- current_node.terminates = destination
+
+ def process_path_part(node, key)
+ case key
+ when Route::Variable::Greedy
+ node.activate_greedy!
+ if key.regex_matcher
+ node.upgrade_greedy!
+ node.greedy[key.regex_matcher] ||= Node.new(node, key)
+ else
+ node.greedy[nil] ||= Node.new(node, key)
+ end
+ when Route::Variable
+ node.activate_normal!
+ if key.regex_matcher
+ node.upgrade_normal!
+ node.normal[key.regex_matcher] ||= Node.new(node, key)
+ else
+ node.normal[nil] ||= Node.new(node, key)
+ end
+ else
+ node.activate_normal!
+ node.upgrade_normal! if key.is_a?(Regexp)
+ node.normal[key] ||= Node.new(node, key)
+ end
end
+ def request_methods_for_path(path)
+ request_methods.collect do |type|
+ Route::RequestMethod.new(type, path.route.conditions && path.route.conditions[type])
+ end
+ end
end
end