aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb2
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb8
-rw-r--r--actionpack/lib/action_controller/metal.rb4
-rw-r--r--actionpack/lib/action_controller/metal/head.rb2
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb16
-rw-r--r--actionpack/lib/action_controller/metal/live.rb3
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb12
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb16
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb57
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb8
-rw-r--r--actionpack/lib/action_dispatch/journey/parser.rb88
-rw-r--r--actionpack/lib/action_dispatch/journey/parser.y5
-rw-r--r--actionpack/lib/action_dispatch/journey/path/pattern.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb11
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb12
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb173
-rw-r--r--actionpack/lib/action_dispatch/middleware/public_exceptions.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb61
24 files changed, 274 insertions, 233 deletions
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb
index 4a95e1f276..72d07b0927 100644
--- a/actionpack/lib/abstract_controller/url_for.rb
+++ b/actionpack/lib/abstract_controller/url_for.rb
@@ -11,7 +11,7 @@ module AbstractController
def _routes
raise "In order to use #url_for, you must include routing helpers explicitly. " \
- "For instance, `include Rails.application.routes.url_helpers"
+ "For instance, `include Rails.application.routes.url_helpers`."
end
module ClassMethods
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 879d5fdd94..2694d4c12f 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -90,7 +90,13 @@ module ActionController
end
def instrument_fragment_cache(name, key) # :nodoc:
- ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
+ payload = {
+ controller: controller_name,
+ action: action_name,
+ key: key
+ }
+
+ ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield }
end
end
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 0f4cc7a8f5..696fbf6e09 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -232,5 +232,9 @@ module ActionController
new.dispatch(name, klass.new(env))
end
end
+
+ def _status_code
+ @_status
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 43407f5b78..84a9112144 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -27,7 +27,7 @@ module ActionController
self.status = status
self.location = url_for(location) if location
- if include_content?(self.status)
+ if include_content?(self._status_code)
self.content_type = content_type || (Mime[formats.first] if formats)
self.response.charset = false if self.response
self.response_body = " "
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 2eb7853aa6..3111992f82 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -90,17 +90,29 @@ module ActionController
end
def authenticate(request, &login_procedure)
- unless request.authorization.blank?
+ if has_basic_credentials?(request)
login_procedure.call(*user_name_and_password(request))
end
end
+ def has_basic_credentials?(request)
+ request.authorization.present? && (auth_scheme(request) == 'Basic')
+ end
+
def user_name_and_password(request)
decode_credentials(request).split(':', 2)
end
def decode_credentials(request)
- ::Base64.decode64(request.authorization.split(' ', 2).last || '')
+ ::Base64.decode64(auth_param(request) || '')
+ end
+
+ def auth_scheme(request)
+ request.authorization.split(' ', 2).first
+ end
+
+ def auth_param(request)
+ request.authorization.split(' ', 2).second
end
def encode_credentials(user_name, password)
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index acf40b2e16..4c0554d27b 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -102,7 +102,8 @@ module ActionController
end
end
- @stream.write "data: #{json}\n\n"
+ message = json.gsub("\n", "\ndata: ")
+ @stream.write "data: #{message}\n\n"
end
end
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index bdf6e88699..6921834044 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -6,7 +6,7 @@ module ActionController
extend ActiveSupport::Concern
delegate :headers, :status=, :location=, :content_type=,
- :status, :location, :content_type, :to => "@_response"
+ :status, :location, :content_type, :_status_code, :to => "@_response"
def dispatch(action, request)
set_response!(request)
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 2812038938..136e086d0d 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -14,7 +14,7 @@ module ActionController
include ActionController::RackDelegation
include ActionController::UrlFor
- # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
+ # Redirects the browser to the target specified in +options+. This parameter can be any one of:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
@@ -24,6 +24,8 @@ module ActionController
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
+ # === Examples:
+ #
# redirect_to action: "show", id: 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
@@ -32,7 +34,7 @@ module ActionController
# redirect_to :back
# redirect_to proc { edit_post_url(@post) }
#
- # The redirection happens as a "302 Found" header unless otherwise specified.
+ # The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option:
#
# redirect_to post_url(@post), status: :found
# redirect_to action: 'atom', status: :moved_permanently
@@ -60,8 +62,10 @@ module ActionController
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
#
- # When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback
- # behavior for this case by rescuing ActionController::RedirectBackError.
+ # When using <tt>redirect_to :back</tt>, if there is no referrer,
+ # <tt>ActionController::RedirectBackError</tt> will be raised. You
+ # may specify some fallback behavior for this case by rescuing
+ # <tt>ActionController::RedirectBackError</tt>.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") unless options
raise AbstractController::DoubleRenderError if response_body
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 0443b73953..29ce5abd55 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -6,6 +6,11 @@ module ActionController
Renderers.add(key, &block)
end
+ # See <tt>Renderers.remove</tt>
+ def self.remove_renderer(key)
+ Renderers.remove(key)
+ end
+
class MissingRenderer < LoadError
def initialize(format)
super "No renderer defined for format: #{format}"
@@ -83,6 +88,17 @@ module ActionController
RENDERERS << key.to_sym
end
+ # This method is the opposite of add method.
+ #
+ # Usage:
+ #
+ # ActionController::Renderers.remove(:csv)
+ def self.remove(key)
+ RENDERERS.delete(key.to_sym)
+ method = "_render_option_#{key}"
+ remove_method(method) if method_defined?(method)
+ end
+
module All
extend ActiveSupport::Concern
include Renderers
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 37d4a96ee1..89c3545323 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -23,12 +23,12 @@ module ActionController
include AbstractController::UrlFor
def url_options
- @_url_options ||= super.reverse_merge(
+ @_url_options ||= {
:host => request.host,
:port => request.optional_port,
:protocol => request.protocol,
:_recall => request.symbolized_path_parameters
- ).freeze
+ }.merge(super).freeze
if (same_origin = _routes.equal?(env["action_dispatch.routes".freeze])) ||
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index dcb299ed03..378d7393ff 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -25,8 +25,8 @@ module ActionDispatch
def path_parameters=(parameters) #:nodoc:
@symbolized_path_params = nil
- @env.delete("action_dispatch.request.parameters")
- @env["action_dispatch.request.path_parameters"] = parameters
+ @env.delete(Routing::RouteSet::PARAMETERS_KEY)
+ @env[Routing::RouteSet::PARAMETERS_KEY] = parameters
end
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
@@ -41,7 +41,7 @@ module ActionDispatch
#
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
- @env["action_dispatch.request.path_parameters"] ||= {}
+ @env[Routing::RouteSet::PARAMETERS_KEY] ||= {}
end
def reset_parameters #:nodoc:
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index daa06e96e6..cdb3e44b3a 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -64,6 +64,7 @@ module ActionDispatch
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
+ # Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt)
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
@@ -71,9 +72,10 @@ module ActionDispatch
RFC3648 = %w(ORDERPATCH)
RFC3744 = %w(ACL)
RFC5323 = %w(SEARCH)
+ RFC4791 = %w(MKCALENDAR)
RFC5789 = %w(PATCH)
- HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
+ HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
HTTP_METHOD_LOOKUP = {}
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 3d27ff2b24..eaea93b730 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -296,6 +296,9 @@ module ActionDispatch # :nodoc:
cookies
end
+ def _status_code
+ @status
+ end
private
def before_committed
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..7eaf8e49ce 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 = []
@@ -30,6 +30,12 @@ module ActionDispatch
parameterized_parts.key?(key) || route.defaults.key?(key)
end
+ defaults = route.defaults
+ required_parts = route.required_parts
+ parameterized_parts.delete_if do |key, value|
+ value.to_s == defaults[key].to_s && !required_parts.include?(key)
+ end
+
return [route.format(parameterized_parts), params]
end
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..cb0a02c298 100644
--- a/actionpack/lib/action_dispatch/journey/path/pattern.rb
+++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb
@@ -30,6 +30,10 @@ module ActionDispatch
@offsets = nil
end
+ def build_formatter
+ Visitors::FormatBuilder.new.accept(spec)
+ end
+
def ast
@spec.grep(Nodes::Symbol).each do |node|
re = @requirements[node.to_sym]
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 2b399d3ee3..cc3c7f20cb 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -31,6 +31,7 @@ module ActionDispatch
@parts = nil
@decorated_ast = nil
@precedence = 0
+ @path_formatter = @path.build_formatter
end
def ast
@@ -72,15 +73,7 @@ module ActionDispatch
alias :segment_keys :parts
def format(path_options)
- path_options.delete_if do |key, value|
- value.to_s == defaults[key].to_s && !required_parts.include?(key)
- end
-
- Visitors::Formatter.new(path_options).accept(path.spec)
- end
-
- def optimized_path
- Visitors::OptimizedPath.new.accept(path.spec)
+ @path_formatter.evaluate path_options
end
def optional_parts
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index 36561c71a1..3bfa03713d 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -90,7 +90,7 @@ module ActionDispatch
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
end
- yield(route, nil, parameters)
+ yield(route, parameters)
end
end
@@ -136,11 +136,11 @@ module ActionDispatch
routes.map! { |r|
match_data = r.path.match(req.path_info)
- match_names = match_data.names.map { |n| n.to_sym }
- match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
- info = Hash[match_names.zip(match_values).find_all { |_, y| y }]
-
- [match_data, r.defaults.merge(info), r]
+ path_parameters = r.defaults.dup
+ match_data.names.zip(match_data.captures) { |name,val|
+ path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
+ }
+ [match_data, path_parameters, r]
}
end
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index d9f634623d..52b4c8b489 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -1,14 +1,57 @@
# 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[param.name]
+ return ''.freeze unless value
+ 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 +81,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 +125,8 @@ module ActionDispatch
end
def visit(node)
- super
block.call(node)
+ super
end
end
@@ -77,90 +150,6 @@ module ActionDispatch
end
end
- class OptimizedPath < Visitor # :nodoc:
- def accept(node)
- Array(visit(node))
- end
-
- private
-
- def visit_CAT(node)
- [visit(node.left), visit(node.right)].flatten
- end
-
- def visit_SYMBOL(node)
- node.left[1..-1].to_sym
- end
-
- def visit_STAR(node)
- visit(node.left)
- end
-
- def visit_GROUP(node)
- []
- end
-
- %w{ LITERAL SLASH DOT }.each do |t|
- class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__
- 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/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
index cbb2d475b1..6c8944e067 100644
--- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
@@ -32,9 +32,8 @@ module ActionDispatch
end
def render_html(status)
- found = false
- path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
- path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
+ path = "#{public_path}/#{status}.#{I18n.locale}.html"
+ path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
if found || File.exist?(path)
render_format(status, 'text/html', File.read(path))
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 9cd884daa3..ce03164ca9 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -1,6 +1,7 @@
# encoding: UTF-8
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/regexp'
+require 'active_support/dependencies/autoload'
module ActionDispatch
# The routing module provides URL rewriting in native Ruby. It's a way to
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index fd163a47f4..8c2e821573 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,11 +1,13 @@
require 'action_dispatch/journey'
require 'forwardable'
require 'thread_safe'
+require 'active_support/concern'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/array/extract_options'
require 'action_controller/metal/exceptions'
+require 'action_dispatch/http/request'
module ActionDispatch
module Routing
@@ -155,7 +157,7 @@ module ActionDispatch
end
def self.optimize_helper?(route)
- !route.glob? && route.requirements.except(:controller, :action, :host).empty?
+ !route.glob? && route.path.requirements.empty?
end
class OptimizedUrlHelper < UrlHelper # :nodoc:
@@ -163,10 +165,8 @@ module ActionDispatch
def initialize(route, options)
super
- @klass = Journey::Router::Utils
@required_parts = @route.required_parts
@arg_size = @required_parts.size
- @optimized_path = @route.optimized_path
end
def call(t, args)
@@ -182,18 +182,14 @@ module ActionDispatch
private
def optimized_helper(args)
- params = Hash[parameterize_args(args)]
+ params = parameterize_args(args)
missing_keys = missing_keys(params)
unless missing_keys.empty?
raise_generation_error(params, missing_keys)
end
- @optimized_path.map{ |segment| replace_segment(params, segment) }.join
- end
-
- def replace_segment(params, segment)
- Symbol === segment ? @klass.escape_segment(params[segment]) : segment
+ @route.format params
end
def optimize_routes_generation?(t)
@@ -201,7 +197,9 @@ module ActionDispatch
end
def parameterize_args(args)
- @required_parts.zip(args.map(&:to_param))
+ params = {}
+ @required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v }
+ params
end
def missing_keys(args)
@@ -224,21 +222,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)
@@ -596,7 +596,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?
@@ -669,15 +669,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)
@@ -696,7 +699,7 @@ module ActionDispatch
end
req = @request_class.new(env)
- @router.recognize(req) do |route, _matches, params|
+ @router.recognize(req) do |route, params|
params.merge!(extras)
params.each do |key, value|
if value.is_a?(String)