diff options
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r-- | actionpack/lib/action_dispatch/http/url.rb | 57 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/formatter.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/parser.rb | 88 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/parser.y | 5 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/path/pattern.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/route.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/journey/visitors.rb | 144 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 41 |
8 files changed, 184 insertions, 160 deletions
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index c9860af909..a6c17f50a5 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -30,19 +30,25 @@ module ActionDispatch end def url_for(options) + unless options[:host] || options[:only_path] + raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + end + path = options[:script_name].to_s.chomp("/") path << options[:path].to_s - result = build_host_url(options) - if options[:trailing_slash] if path.include?('?') - result << path.sub(/\?/, '/\&') + path.sub!(/\?/, '/\&') else - result << path.sub(/[^\/]\z|\A\z/, '\&/') + path.sub!(/[^\/]\z|\A\z/, '\&/') end - else - result << path + end + + result = path + + unless options[:only_path] + result.prepend build_host_url(options) end if options.key? :params @@ -61,28 +67,25 @@ module ActionDispatch private def build_host_url(options) - unless options[:host] || options[:only_path] - raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + if match = options[:host].match(HOST_REGEXP) + options[:protocol] ||= match[1] unless options[:protocol] == false + options[:host] = match[2] + options[:port] = match[3] unless options.key?(:port) end - result = "" + options[:protocol] = normalize_protocol(options) + options[:host] = normalize_host(options) + options[:port] = normalize_port(options) - unless options[:only_path] - if match = options[:host].match(HOST_REGEXP) - options[:protocol] ||= match[1] unless options[:protocol] == false - options[:host] = match[2] - options[:port] = match[3] unless options.key?(:port) - end - - options[:protocol] = normalize_protocol(options) - options[:host] = normalize_host(options) - options[:port] = normalize_port(options) + result = options[:protocol] - result << options[:protocol] - result << rewrite_authentication(options) - result << options[:host] - result << ":#{options[:port]}" if options[:port] + if options[:user] && options[:password] + result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" end + + result << options[:host] + result << ":#{options[:port]}" if options[:port] + result end @@ -94,14 +97,6 @@ module ActionDispatch (options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil? end - def rewrite_authentication(options) - if options[:user] && options[:password] - "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" - else - "" - end - end - def normalize_protocol(options) case options[:protocol] when nil diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb index 57f0963731..aa8d5a4d79 100644 --- a/actionpack/lib/action_dispatch/journey/formatter.rb +++ b/actionpack/lib/action_dispatch/journey/formatter.rb @@ -12,7 +12,7 @@ module ActionDispatch @cache = nil end - def generate(type, name, options, recall = {}, parameterize = nil) + def generate(name, options, recall = {}, parameterize = nil) constraints = recall.merge(options) missing_keys = [] diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index 430812fafe..d129ba7e16 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -1,7 +1,7 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.4.9 -# from Racc grammar file "". +# This file is automatically generated by Racc 1.4.11 +# from Racc grammer file "". # require 'racc/parser.rb' @@ -9,42 +9,38 @@ require 'racc/parser.rb' require 'action_dispatch/journey/parser_extras' module ActionDispatch - module Journey # :nodoc: - class Parser < Racc::Parser # :nodoc: + module Journey + class Parser < Racc::Parser ##### State transition tables begin ### racc_action_table = [ - 17, 21, 13, 15, 14, 7, nil, 16, 8, 19, - 13, 15, 14, 7, 23, 16, 8, 19, 13, 15, - 14, 7, nil, 16, 8, 13, 15, 14, 7, nil, - 16, 8, 13, 15, 14, 7, nil, 16, 8 ] + 13, 15, 14, 7, 21, 16, 8, 19, 13, 15, + 14, 7, 17, 16, 8, 13, 15, 14, 7, 24, + 16, 8, 13, 15, 14, 7, 19, 16, 8 ] racc_action_check = [ - 1, 17, 1, 1, 1, 1, nil, 1, 1, 1, - 20, 20, 20, 20, 20, 20, 20, 20, 7, 7, - 7, 7, nil, 7, 7, 19, 19, 19, 19, nil, - 19, 19, 0, 0, 0, 0, nil, 0, 0 ] + 2, 2, 2, 2, 17, 2, 2, 2, 0, 0, + 0, 0, 1, 0, 0, 19, 19, 19, 19, 20, + 19, 19, 7, 7, 7, 7, 22, 7, 7 ] racc_action_pointer = [ - 30, 0, nil, nil, nil, nil, nil, 16, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 1, nil, 23, - 8, nil, nil, nil ] + 6, 12, -2, nil, nil, nil, nil, 20, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 4, nil, 13, + 13, nil, 17, nil, nil ] racc_action_default = [ - -18, -18, -2, -3, -4, -5, -6, -18, -9, -10, - -11, -12, -13, -14, -15, -16, -17, -18, -1, -18, - -18, 24, -8, -7 ] + -19, -19, -2, -3, -4, -5, -6, -19, -10, -11, + -12, -13, -14, -15, -16, -17, -18, -19, -1, -19, + -19, 25, -8, -9, -7 ] racc_goto_table = [ - 18, 1, nil, nil, nil, nil, nil, nil, 20, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ] + 1, 22, 18, 23, nil, nil, nil, 20 ] racc_goto_check = [ - 2, 1, nil, nil, nil, nil, nil, nil, 1, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ] + 1, 2, 1, 3, nil, nil, nil, 1 ] racc_goto_pointer = [ - nil, 1, -1, nil, nil, nil, nil, nil, nil, nil, + nil, 0, -18, -16, nil, nil, nil, nil, nil, nil, nil ] racc_goto_default = [ @@ -61,19 +57,20 @@ racc_reduce_table = [ 1, 12, :_reduce_none, 3, 15, :_reduce_7, 3, 13, :_reduce_8, - 1, 16, :_reduce_9, + 3, 13, :_reduce_9, + 1, 16, :_reduce_10, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, - 1, 19, :_reduce_14, - 1, 17, :_reduce_15, - 1, 18, :_reduce_16, - 1, 20, :_reduce_17 ] + 1, 19, :_reduce_15, + 1, 17, :_reduce_16, + 1, 18, :_reduce_17, + 1, 20, :_reduce_18 ] -racc_reduce_n = 18 +racc_reduce_n = 19 -racc_shift_n = 24 +racc_shift_n = 25 racc_token_table = { false => 0, @@ -137,12 +134,12 @@ Racc_debug_parser = false # reduce 0 omitted def _reduce_1(val, _values, result) - result = Cat.new(val.first, val.last) + result = Cat.new(val.first, val.last) result end def _reduce_2(val, _values, result) - result = val.first + result = val.first result end @@ -155,21 +152,24 @@ end # reduce 6 omitted def _reduce_7(val, _values, result) - result = Group.new(val[1]) + result = Group.new(val[1]) result end def _reduce_8(val, _values, result) - result = Or.new([val.first, val.last]) + result = Or.new([val.first, val.last]) result end def _reduce_9(val, _values, result) - result = Star.new(Symbol.new(val.last)) + result = Or.new([val.first, val.last]) result end -# reduce 10 omitted +def _reduce_10(val, _values, result) + result = Star.new(Symbol.new(val.last)) + result +end # reduce 11 omitted @@ -177,23 +177,25 @@ end # reduce 13 omitted -def _reduce_14(val, _values, result) - result = Slash.new('/') - result -end +# reduce 14 omitted def _reduce_15(val, _values, result) - result = Symbol.new(val.first) + result = Slash.new('/') result end def _reduce_16(val, _values, result) - result = Literal.new(val.first) + result = Symbol.new(val.first) result end def _reduce_17(val, _values, result) - result = Dot.new(val.first) + result = Literal.new(val.first) + result +end + +def _reduce_18(val, _values, result) + result = Dot.new(val.first) result end diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y index 040f8d5922..0ead222551 100644 --- a/actionpack/lib/action_dispatch/journey/parser.y +++ b/actionpack/lib/action_dispatch/journey/parser.y @@ -4,7 +4,7 @@ token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR rule expressions - : expressions expression { result = Cat.new(val.first, val.last) } + : expression expressions { result = Cat.new(val.first, val.last) } | expression { result = val.first } | or ; @@ -17,7 +17,8 @@ rule : LPAREN expressions RPAREN { result = Group.new(val[1]) } ; or - : expressions OR expression { result = Or.new([val.first, val.last]) } + : expression OR expression { result = Or.new([val.first, val.last]) } + | expression OR or { result = Or.new([val.first, val.last]) } ; star : STAR { result = Star.new(Symbol.new(val.last)) } diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb index fb155e516f..28c75618de 100644 --- a/actionpack/lib/action_dispatch/journey/path/pattern.rb +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -28,6 +28,11 @@ module ActionDispatch @required_names = nil @re = nil @offsets = nil + @format = Visitors::FormatBuilder.new.accept(spec) + end + + def format_path(path_options) + @format.evaluate path_options end def ast diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb index 2b399d3ee3..d4df96314f 100644 --- a/actionpack/lib/action_dispatch/journey/route.rb +++ b/actionpack/lib/action_dispatch/journey/route.rb @@ -76,7 +76,7 @@ module ActionDispatch value.to_s == defaults[key].to_s && !required_parts.include?(key) end - Visitors::Formatter.new(path_options).accept(path.spec) + path.format_path path_options end def optimized_path diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index d9f634623d..b635576b6a 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -1,14 +1,56 @@ # encoding: utf-8 -require 'thread_safe' - module ActionDispatch module Journey # :nodoc: + class Format + ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) } + ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) } + + class Parameter < Struct.new(:name, :escaper) + def escape(value); escaper.call value; end + end + + def self.required_path(symbol) + Parameter.new symbol, ESCAPE_PATH + end + + def self.required_segment(symbol) + Parameter.new symbol, ESCAPE_SEGMENT + end + + def initialize(parts) + @parts = parts + @children = [] + @parameters = [] + + parts.each_with_index do |object,i| + case object + when Journey::Format + @children << i + when Parameter + @parameters << i + end + end + end + + def evaluate(hash) + parts = @parts.dup + + @parameters.each do |index| + param = parts[index] + value = hash.fetch(param.name) { return ''.freeze } + parts[index] = param.escape value + end + + @children.each { |index| parts[index] = parts[index].evaluate(hash) } + + parts.join + end + end + module Visitors # :nodoc: class Visitor # :nodoc: - DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k| - h[k] = :"visit_#{k}" - } + DISPATCH_CACHE = {} def accept(node) visit(node) @@ -38,11 +80,41 @@ module ActionDispatch def visit_STAR(n); unary(n); end def terminal(node); end - %w{ LITERAL SYMBOL SLASH DOT }.each do |t| - class_eval %{ def visit_#{t}(n); terminal(n); end }, __FILE__, __LINE__ + def visit_LITERAL(n); terminal(n); end + def visit_SYMBOL(n); terminal(n); end + def visit_SLASH(n); terminal(n); end + def visit_DOT(n); terminal(n); end + + private_instance_methods(false).each do |pim| + next unless pim =~ /^visit_(.*)$/ + DISPATCH_CACHE[$1.to_sym] = pim end end + class FormatBuilder < Visitor # :nodoc: + def accept(node); Journey::Format.new(super); end + def terminal(node); [node.left]; end + + def binary(node) + visit(node.left) + visit(node.right) + end + + def visit_GROUP(n); [Journey::Format.new(unary(n))]; end + + def visit_STAR(n) + [Journey::Format.required_path(n.left.to_sym)] + end + + def visit_SYMBOL(n) + symbol = n.to_sym + if symbol == :controller + [Journey::Format.required_path(symbol)] + else + [Journey::Format.required_segment(symbol)] + end + end + end + # Loop through the requirements AST class Each < Visitor # :nodoc: attr_reader :block @@ -52,8 +124,8 @@ module ActionDispatch end def visit(node) - super block.call(node) + super end end @@ -105,62 +177,6 @@ module ActionDispatch end end - # Used for formatting urls (url_for) - class Formatter < Visitor # :nodoc: - attr_reader :options - - def initialize(options) - @options = options - end - - private - def escape_path(value) - Router::Utils.escape_path(value) - end - - def escape_segment(value) - Router::Utils.escape_segment(value) - end - - def visit(node, optional = false) - case node.type - when :LITERAL, :SLASH, :DOT - node.left - when :STAR - visit_STAR(node.left) - when :GROUP - visit(node.left, true) - when :CAT - visit_CAT(node, optional) - when :SYMBOL - visit_SYMBOL(node, node.to_sym) - end - end - - def visit_CAT(node, optional) - left = visit(node.left, optional) - right = visit(node.right, optional) - - if optional && !(right && left) - "" - else - [left, right].join - end - end - - def visit_STAR(node) - if value = options[node.to_sym] - escape_path(value) - end - end - - def visit_SYMBOL(node, name) - if value = options[name] - name == :controller ? escape_path(value) : escape_segment(value) - end - end - end - class Dot < Visitor # :nodoc: def initialize @nodes = [] diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index e699419f23..01378ae0ec 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -226,21 +226,23 @@ module ActionDispatch end def call(t, args) - options = t.url_options.merge @options - hash = handle_positional_args(t, args, options, @segment_keys) + controller_options = t.url_options + options = controller_options.merge @options + hash = handle_positional_args(controller_options, args, options, @segment_keys) t._routes.url_for(hash) end - def handle_positional_args(t, args, result, keys) + def handle_positional_args(controller_options, args, result, path_params) inner_options = args.extract_options! if args.size > 0 - if args.size < keys.size - 1 # take format into account - keys -= t.url_options.keys - keys -= result.keys + if args.size < path_params.size - 1 # take format into account + path_params -= controller_options.keys + path_params -= result.keys end - keys -= inner_options.keys - result.merge!(Hash[keys.zip(args)]) + path_params.each { |param| + result[param] = inner_options[param] || args.shift + } end result.merge!(inner_options) @@ -598,7 +600,7 @@ module ActionDispatch # Generates a path from routes, returns [path, params]. # If no route is generated the formatter will raise ActionController::UrlGenerationError def generate - @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE) + @set.formatter.generate(named_route, options, recall, PARAMETERIZE) end def different_controller? @@ -671,15 +673,18 @@ module ActionDispatch RESERVED_OPTIONS.each { |ro| path_options.delete ro } path, params = generate(path_options, recall) - params.merge!(options[:params] || {}) - - ActionDispatch::Http::URL.url_for(options.merge!({ - :path => path, - :script_name => script_name, - :params => params, - :user => user, - :password => password - })) + + if options.key? :params + params.merge! options[:params] + end + + options[:path] = path + options[:script_name] = script_name + options[:params] = params + options[:user] = user + options[:password] = password + + ActionDispatch::Http::URL.url_for(options) end def call(env) |