Skip to content

Instantly share code, notes, and snippets.

@jm
Created November 15, 2008 15:37
Show Gist options
  • Save jm/25263 to your computer and use it in GitHub Desktop.
Save jm/25263 to your computer and use it in GitHub Desktop.
module ActionController
module Routing
class RouteSet
attr_accessor :routes, :recognizer
def initialize
@routes = []
@route_structure = {}
end
def add_named_route(name, path, options = {})
add_route(path, options)
end
def add_route(route_string, *args)
@route_structure ||= {}
@routes ||= []
params = []
args = (args.pop || {})
request_method = args[:conditions] && args[:conditions][:method] ? args[:conditions].delete(:method) : :get
requirements = args.delete(:requirements) || {}
segments = route_string.split("/").map! do |segment|
segment = Regexp.escape(segment)
if segment =~ /:\w+/
segment_symbols = segment.scan(/:(\w+)/).flatten
segment_symbols.each do |segment_symbol|
params << segment_symbol.to_sym
requirements[segment_symbol] || ".*"
segment.gsub!(/:#{segment_symbol}/, '.*')
end
end
segment
end
raise "Invalid route: Controller not specified" unless (params.include?(:controller) || args.keys.include?(:controller))
new_route = RouterFu::Route.new(segments, params, request_method, args)
@routes << new_route
new_route.arguments[:controller] ||= :controller
new_route.arguments[:action] ||= :action
@route_structure[request_method] ||= {}
@route_structure[request_method][new_route.arguments[:controller]] ||= {}
@route_structure[request_method][new_route.arguments[:controller]][new_route.arguments[:action]] ||= []
@route_structure[request_method][new_route.arguments[:controller]][new_route.arguments[:action]] << new_route
end
def recognize(request)
# path, request_method = :GET
path = request.path
request_method = (request.request_method || :get)
@recognizer ||= build_recognizer
RAILS_DEFAULT_LOGGER.error(@recognizer)
matched = {}
# RAILS_DEFAULT_LOGGER.error("#{request_method} #{path}")
captures = @recognizer.match("#{request_method} #{path}").captures[1..-1]
# RAILS_DEFAULT_LOGGER.error(@recognizer.pretty_inspect)
# RAILS_DEFAULT_LOGGER.error(@routes.pretty_inspect)
#
# RAILS_DEFAULT_LOGGER.error(@routes[captures.index(captures.compact.last)].pretty_inspect)
if r = @routes[captures.index(captures.compact.last)]
path_segments = path.gsub(/^\//, '').split("/")
index = -1
r.segments.each do |segment|
next unless segment == "(.*)" # FIXFXIFXIFXFIX
index += 1
matched[r.params[index]] = path_segments[index]
end
matched = r.arguments.merge(matched)
RAILS_DEFAULT_LOGGER.error(matched.pretty_inspect)
matched[:action] = 'index' if matched[:action] == :action
end
request.path_parameters = matched
"#{matched[:controller].camelize}Controller".constantize
end
def generate(*args)
params = args.pop
request_method = params[:method].to_sym || :get
if params.keys.include?(:controller)
controller_routes = @route_structure[:request_method][params[:controller]]
unless controller_routes
controller_routes = @route_structure[:request_method][:controller]
end
action_routes = controller_routes[(params[:action] || 'index')] || controller_routes[:action]
action_routes.each do |route|
if (route.params - params.keys).empty?
puts "generating from #{route.inspect}"
return generate_url(route, params)
else
raise "No route to match that"
end
end
else
raise "No controller provided"
end
end
def generate_url(route, params)
route_string = route.segments.join("/")
return route_string unless route_string.include?("(.*)")
index = -1
route_string.gsub!(/\(\.\*\)/) do |match|
index += 1
params[route.params[index]]
end
end
def route_resources(resources)
resources = resources.to_s
add_route resources, :controller => resources.to_sym, :action => 'index', :method => :get
add_route resources, :controller => resources.to_sym, :action => 'create', :method => :post
add_route "#{resources}/:id", :controller => resources.to_sym, :action => 'show', :method => :get
add_route "#{resources}/:id", :controller => resources.to_sym, :action => 'update', :method => :put
add_route "#{resources}/:id", :controller => resources.to_sym, :action => 'destroy', :method => :delete
add_route "#{resources}/new", :controller => resources.to_sym, :action => 'new', :method => :get
add_route "#{resources}/:id/edit", :controller => resources.to_sym, :action => 'edit', :method => :get
end
def build_recognizer
recognizers = []
@routes.each do |route|
recognizers << "(#{route.request_method} #{route.recognizer})"
end
@recognizer = /^(#{recognizers.join("|")})$/
end
end
end
end
module RouterFu
class Route
attr_accessor :params, :segments, :arguments
attr_reader :recognizer, :request_method
def initialize(segment_list, param_list, request_method, argument_list = {})
@segments = segment_list
@params = param_list
@arguments = argument_list || {}
@recognizer = "#{@segments.join("\/")}$"
@request_method = request_method
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment