require 'rack' unless Rack::Utils.respond_to?(:uri_escape) module Rack module Utils def uri_escape(s) s.to_s.gsub(/([^:\/?\[\]\-_~\.!\$&'\(\)\*\+,;=@a-zA-Z0-9]+)/n) { '%'<<$1.unpack('H2'*$1.size).join('%').upcase }.tr(' ', '+') end module_function :uri_escape def uri_unescape(s) gsub(/((?:%[0-9a-fA-F]{2})+)/n){ [$1.delete('%')].pack('H*') } end module_function :uri_unescape end end end class Usher module Util class Generators class URL attr_accessor :usher def generate_full(routing_lookup, request, params = nil) path = path_for_routing_lookup(routing_lookup, params) result = generate_start(path, request) result << generate_path(path, params) end def generate(routing_lookup, params = nil) generate_path(path_for_routing_lookup(routing_lookup, params), params) end def generate_start(path, request) result = (path.route.generate_with && path.route.generate_with.scheme || request.scheme).dup result << '://' result << (path.route.generate_with && path.route.generate_with.host) ? path.route.generate_with.host : request.host port = path.route.generate_with && path.route.generate_with.port || request.port if result[4] == ?s result << ':' << port.to_s unless port == 443 else result << ':' << port.to_s unless port == 80 end result end def path_for_routing_lookup(routing_lookup, params = {}) path = case routing_lookup when Symbol route = @usher.named_routes[routing_lookup] route.find_matching_path(params || {}) when Route routing_lookup.find_matching_path(params || {}) when nil params.is_a?(Hash) ? @usher.path_for_options(params) : raise when Route::Path routing_lookup end end # Generates a completed URL based on a +route+ or set of optional +params+ # # set = Usher.new # route = set.add_named_route(:test_route, '/:controller/:action') # set.generate_url(nil, {:controller => 'c', :action => 'a'}) == '/c/a' => true # set.generate_url(:test_route, {:controller => 'c', :action => 'a'}) == '/c/a' => true # set.generate_url(route.primary_path, {:controller => 'c', :action => 'a'}) == '/c/a' => true def generate_path(path, params = nil) raise UnrecognizedException.new unless path params = Array(params) if params.is_a?(String) if params.is_a?(Array) given_size = params.size extra_params = params.last.is_a?(Hash) ? params.pop : nil params = Hash[*path.dynamic_parts.inject([]){|a, dynamic_part| a.concat([dynamic_part.name, params.shift || raise(MissingParameterException.new("got #{given_size}, expected #{path.dynamic_parts.size} parameters"))]); a}] params.merge!(extra_params) if extra_params end result = '' path.parts.each do |part| case part when Route::Variable::Glob value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new) value.each_with_index do |current_value, index| part.valid!(current_value) result << current_value.to_s result << '/' if index != value.size - 1 end when Route::Variable value = (params && params.delete(part.name)) || part.default_value || raise(MissingParameterException.new) part.valid!(value) result << value.to_s else result << part end end result = Rack::Utils.uri_escape(result) unless params.nil? || params.empty? has_query = result[??] params.each do |k,v| case v when Array v.each do |v_part| result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape("#{k.to_s}[]") << '=' << Rack::Utils.escape(v_part.to_s) end else result << (has_query ? '&' : has_query = true && '?') << Rack::Utils.escape(k.to_s) << '=' << Rack::Utils.escape(v.to_s) end end end result end end end end end