aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb1
-rw-r--r--actionpack/lib/action_controller/metal.rb5
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb69
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb1
-rw-r--r--actionpack/lib/action_controller/metal/live.rb29
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb1
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb2
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb3
-rw-r--r--actionpack/lib/action_controller/test_case.rb31
-rw-r--r--actionpack/lib/action_dispatch.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb70
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/parser.y1
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb53
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/public_exceptions.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb59
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb9
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/buffers.rb6
-rw-r--r--actionpack/lib/action_view/dependency_tracker.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb23
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb57
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb25
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tags/base.rb19
-rw-r--r--actionpack/lib/action_view/helpers/tags/check_box.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tags/checkable.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_helpers.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_select.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tags/color_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_select.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_local_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_select.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/email_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/file_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/hidden_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/label.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/month_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/number_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/password_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/radio_button.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/range_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/search_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/select.rb5
-rw-r--r--actionpack/lib/action_view/helpers/tags/tel_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_area.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_select.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_zone_select.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/url_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/week_field.rb4
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb24
-rw-r--r--actionpack/lib/action_view/lookup_context.rb1
-rw-r--r--actionpack/lib/action_view/path_set.rb6
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb15
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb14
-rw-r--r--actionpack/lib/action_view/renderer/renderer.rb6
-rw-r--r--actionpack/lib/action_view/template.rb11
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb32
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
86 files changed, 557 insertions, 259 deletions
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index bac994496e..8e7bdf620e 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -309,6 +309,7 @@ module AbstractController
RUBY
when Proc
define_method :_layout_from_proc, &_layout
+ protected :_layout_from_proc
<<-RUBY
result = _layout_from_proc(#{_layout.arity == 0 ? '' : 'self'})
return #{default_behavior} if result.nil?
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 143b3e0cbd..b84c9e78c3 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -36,8 +36,7 @@ module ActionController
raise "MiddlewareStack#build requires an app" unless app
middlewares.reverse.inject(app) do |a, middleware|
- middleware.valid?(action) ?
- middleware.build(a) : a
+ middleware.valid?(action) ? middleware.build(a) : a
end
end
end
@@ -57,7 +56,7 @@ module ActionController
# And then to route requests to your metal controller, you would add
# something like this to <tt>config/routes.rb</tt>:
#
- # match 'hello', to: HelloController.action(:index)
+ # get 'hello', to: HelloController.action(:index)
#
# The +action+ method returns a valid Rack application for the \Rails
# router to dispatch to.
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index f1e8714a86..b8afce42c9 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
+
module ActionController
# This module provides a method which will redirect browser to use HTTPS
# protocol. This will ensure that user's sensitive information will be
@@ -14,6 +17,10 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Callbacks
+ ACTION_OPTIONS = [:only, :except, :if, :unless]
+ URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path]
+ REDIRECT_OPTIONS = [:status, :flash, :alert, :notice]
+
module ClassMethods
# Force the request to this particular controller or specified actions to be
# under HTTPS protocol.
@@ -29,18 +36,34 @@ module ActionController
# end
# end
#
- # ==== Options
- # * <tt>host</tt> - Redirect to a different host name
- # * <tt>only</tt> - The callback should be run only for this action
- # * <tt>except</tt> - The callback should be run for all actions except this action
- # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
- # will be called only when it returns a true value.
- # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
- # will be called only when it returns a false value.
+ # ==== URL Options
+ # You can pass any of the following options to affect the redirect url
+ # * <tt>host</tt> - Redirect to a different host name
+ # * <tt>subdomain</tt> - Redirect to a different subdomain
+ # * <tt>domain</tt> - Redirect to a different domain
+ # * <tt>port</tt> - Redirect to a non-standard port
+ # * <tt>path</tt> - Redirect to a different path
+ #
+ # ==== Redirect Options
+ # You can pass any of the following options to affect the redirect status and response
+ # * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently)
+ # * <tt>flash</tt> - Set a flash message when redirecting
+ # * <tt>alert</tt> - Set a alert message when redirecting
+ # * <tt>notice</tt> - Set a notice message when redirecting
+ #
+ # ==== Action Options
+ # You can pass any of the following options to affect the before_action callback
+ # * <tt>only</tt> - The callback should be run only for this action
+ # * <tt>except</tt> - The callback should be run for all actions except this action
+ # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
+ # will be called only when it returns a true value.
+ # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
+ # will be called only when it returns a false value.
def force_ssl(options = {})
- host = options.delete(:host)
- before_action(options) do
- force_ssl_redirect(host)
+ action_options = options.slice(*ACTION_OPTIONS)
+ redirect_options = options.except(*ACTION_OPTIONS)
+ before_action(action_options) do
+ force_ssl_redirect(redirect_options)
end
end
end
@@ -48,14 +71,26 @@ module ActionController
# Redirect the existing request to use the HTTPS protocol.
#
# ==== Parameters
- # * <tt>host</tt> - Redirect to a different host name
- def force_ssl_redirect(host = nil)
+ # * <tt>host_or_options</tt> - Either a host name or any of the url & redirect options
+ # available to the <tt>force_ssl</tt> method.
+ def force_ssl_redirect(host_or_options = nil)
unless request.ssl?
- redirect_options = {:protocol => 'https://', :status => :moved_permanently}
- redirect_options.merge!(:host => host) if host
- redirect_options.merge!(:params => request.query_parameters)
+ options = {
+ :protocol => 'https://',
+ :host => request.host,
+ :path => request.fullpath,
+ :status => :moved_permanently
+ }
+
+ if host_or_options.is_a?(Hash)
+ options.merge!(host_or_options)
+ elsif host_or_options
+ options.merge!(:host => host_or_options)
+ end
+
+ secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS))
flash.keep if respond_to?(:flash)
- redirect_to redirect_options
+ redirect_to secure_url, options.slice(*REDIRECT_OPTIONS)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 35facd13c8..243fd40a7e 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -94,7 +94,6 @@ module ActionController
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
names.sort!
- names
end
helpers.uniq!
helpers
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index fb664a69dd..8092fd639f 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -34,6 +34,7 @@ module ActionController
module Live
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
def initialize(response)
+ @error_callback = nil
super(response, SizedQueue.new(10))
end
@@ -56,6 +57,14 @@ module ActionController
super
@buf.push nil
end
+
+ def on_error(&block)
+ @error_callback = block
+ end
+
+ def call_on_error
+ @error_callback.call
+ end
end
class Response < ActionDispatch::Response #:nodoc: all
@@ -121,6 +130,16 @@ module ActionController
begin
super(name)
+ rescue => e
+ begin
+ @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
+ @_response.stream.call_on_error
+ rescue => exception
+ log_error(exception)
+ ensure
+ log_error(e)
+ @_response.stream.close
+ end
ensure
@_response.commit!
end
@@ -129,6 +148,16 @@ module ActionController
@_response.await_commit
end
+ def log_error(exception)
+ logger = ActionController::Base.logger
+ return unless logger
+
+ message = "\n#{exception.class} (#{exception.message}):\n"
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
+ message << " " << exception.backtrace.join("\n ")
+ logger.fatal("#{message}\n\n")
+ end
+
def response_body=(body)
super
response.stream.close if response
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index c5e7d4e357..bea6b88f91 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -6,7 +6,7 @@ module ActionController
# Before processing, set the request formats in current controller formats.
def process_action(*) #:nodoc:
- self.formats = request.formats.map { |x| x.ref }
+ self.formats = request.formats.map(&:ref).compact
super
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index d275a854fd..573c739da4 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -199,6 +199,7 @@ module ActionController #:nodoc:
params[request_forgery_protection_token]
end
+ # Checks if the controller allows forgery protection.
def protect_against_forgery?
allow_forgery_protection
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 23d70c9ea2..44703221f3 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -230,7 +230,7 @@ module ActionController
# params = ActionController::Parameters.new({
# person: {
# contact: {
- # email: 'none@test.com'
+ # email: 'none@test.com',
# phone: '555-1234'
# }
# }
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 505f3b4e61..754249cbc8 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -32,7 +32,8 @@ module ActionController
if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
- (original_script_name = env['SCRIPT_NAME'])
+ (original_script_name = env['ORIGINAL_SCRIPT_NAME'])
+
@_url_options.dup.tap do |options|
if original_script_name
options[:original_script_name] = original_script_name
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index b9a5e78fe9..e5f1ad63c2 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -18,7 +18,7 @@ module ActionController
@_layouts = Hash.new(0)
@_files = Hash.new(0)
- ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
+ ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
path = payload[:layout]
if path
@_layouts[path] += 1
@@ -28,7 +28,7 @@ module ActionController
end
end
- ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
+ ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
path = payload[:virtual_path]
next unless path
partial = path =~ /^.*\/_[^\/]*$/
@@ -41,7 +41,7 @@ module ActionController
@_templates[path] += 1
end
- ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
+ ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
next if payload[:virtual_path] # files don't have virtual path
path = payload[:identifier]
@@ -455,13 +455,14 @@ module ActionController
#
# - +action+: The controller action to call.
# - +parameters+: The HTTP parameters that you want to pass. This may
- # be +nil+, a Hash, or a String that is appropriately encoded
+ # be +nil+, a hash, or a string that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
- # - +session+: A Hash of parameters to store in the session. This may be +nil+.
- # - +flash+: A Hash of parameters to store in the flash. This may be +nil+.
+ # - +session+: A hash of parameters to store in the session. This may be +nil+.
+ # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
+ #
+ # You can also simulate POST, PATCH, PUT, DELETE, HEAD, and OPTIONS requests with
+ # +post+, +patch+, +put+, +delete+, +head+, and +options+.
#
- # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
- # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
# Note that the request method is not verified. The different methods are
# available to make the tests more expressive.
def get(action, *args)
@@ -469,37 +470,37 @@ module ActionController
end
# Simulate a POST request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def post(action, *args)
process(action, "POST", *args)
end
# Simulate a PATCH request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def patch(action, *args)
process(action, "PATCH", *args)
end
# Simulate a PUT request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def put(action, *args)
process(action, "PUT", *args)
end
# Simulate a DELETE request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def delete(action, *args)
process(action, "DELETE", *args)
end
# Simulate a HEAD request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def head(action, *args)
process(action, "HEAD", *args)
end
# Simulate a OPTIONS request with the given parameters and set/volley the response.
- # See +#get+ for more details.
+ # See +get+ for more details.
def options(action, *args)
process(action, "OPTIONS", *args)
end
@@ -557,7 +558,7 @@ module ActionController
parameters ||= {}
controller_class_name = @controller.class.anonymous? ?
"anonymous" :
- @controller.class.name.underscore.sub(/_controller$/, '')
+ @controller.class.controller_path
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 2c88bc3b1f..24a3d4741e 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -82,10 +82,10 @@ module ActionDispatch
end
module Session
- autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
- autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
- autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
- autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
+ autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
+ autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
+ autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
+ autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
end
mattr_accessor :test_app
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 912da741b7..f29ad359ac 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -223,8 +223,8 @@ module Mime
Mime.instance_eval { remove_const(symbol) }
SET.delete_if { |v| v.eql?(mime) }
- LOOKUP.delete_if { |k,v| v.eql?(mime) }
- EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
+ LOOKUP.delete_if { |_,v| v.eql?(mime) }
+ EXTENSION_LOOKUP.delete_if { |_,v| v.eql?(mime) }
end
end
@@ -306,12 +306,20 @@ module Mime
method.to_s.ends_with? '?'
end
end
-
+
class NullType
def nil?
true
end
+ def ref
+ nil
+ end
+
+ def respond_to_missing?(method, include_private = false)
+ method.to_s.ends_with? '?'
+ end
+
private
def method_missing(method, *args)
false if method.to_s.ends_with? '?'
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 246d9c121a..20c24ddd85 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -65,7 +65,7 @@ module ActionDispatch
new_hash = {}
params.each do |k, v|
- new_key = k.is_a?(String) ? k.dup.force_encoding("UTF-8").encode! : k
+ new_key = k.is_a?(String) ? k.dup.force_encoding(Encoding::UTF_8).encode! : k
new_hash[new_key] =
case v
when Hash
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 06e936cdb0..60a2cccdc5 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -55,6 +55,7 @@ module ActionDispatch # :nodoc:
CONTENT_TYPE = "Content-Type".freeze
SET_COOKIE = "Set-Cookie".freeze
LOCATION = "Location".freeze
+ NO_CONTENT_CODES = [204, 304]
cattr_accessor(:default_charset) { "utf-8" }
cattr_accessor(:default_headers)
@@ -289,7 +290,7 @@ module ActionDispatch # :nodoc:
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
- if [204, 304].include?(@status)
+ if NO_CONTENT_CODES.include?(@status)
header.delete CONTENT_TYPE
[status, header, []]
else
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index ab5399c8ea..6f5a52c568 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -4,7 +4,9 @@ require 'active_support/core_ext/hash/slice'
module ActionDispatch
module Http
module URL
- IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
+ IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
+ HOST_REGEXP = /(^.*:\/\/)?([^:]+)(?::(\d+$))?/
+ PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
mattr_accessor :tld_length
self.tld_length = 1
@@ -28,6 +30,7 @@ module ActionDispatch
end
def url_for(options = {})
+ options = options.dup
path = options.delete(:script_name).to_s.chomp("/")
path << options.delete(:path).to_s
@@ -59,15 +62,20 @@ module ActionDispatch
result = ""
unless options[:only_path]
- protocol = extract_protocol(options)
- unless options[:protocol] == false
- result << protocol
- result << ":" unless result.match(%r{:|//})
+ 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 << "//" unless result.match("//")
+
+ options[:protocol] = normalize_protocol(options)
+ options[:host] = normalize_host(options)
+ options[:port] = normalize_port(options)
+
+ result << options[:protocol]
result << rewrite_authentication(options)
- result << host_or_subdomain_and_domain(options)
- result << ":#{options.delete(:port)}" if options[:port]
+ result << options[:host]
+ result << ":#{options[:port]}" if options[:port]
end
result
end
@@ -76,6 +84,10 @@ module ActionDispatch
host && IP_HOST_REGEXP !~ host
end
+ def same_host?(options)
+ (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])}@"
@@ -84,29 +96,47 @@ module ActionDispatch
end
end
- # Extracts protocol http:// or https:// from options[:host]
- # needs to be called whether the :protocol is being used or not
- def extract_protocol(options)
- if options[:host] && match = options[:host].match(/(^.*:\/\/)(.*)/)
- options[:protocol] ||= match[1]
- options[:host] = match[2]
+ def normalize_protocol(options)
+ case options[:protocol]
+ when nil
+ "http://"
+ when false, "//"
+ "//"
+ when PROTOCOL_REGEXP
+ "#{$1}://"
+ else
+ raise ArgumentError, "Invalid :protocol option: #{options[:protocol].inspect}"
end
- options[:protocol] || "http"
end
- def host_or_subdomain_and_domain(options)
- return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
+ def normalize_host(options)
+ return options[:host] if !named_host?(options[:host]) || same_host?(options)
tld_length = options[:tld_length] || @@tld_length
host = ""
- unless options[:subdomain] == false
- host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
- host << "."
+ if options[:subdomain] == true || !options.key?(:subdomain)
+ host << extract_subdomain(options[:host], tld_length).to_param
+ elsif options[:subdomain].present?
+ host << options[:subdomain].to_param
end
+ host << "." unless host.empty?
host << (options[:domain] || extract_domain(options[:host], tld_length))
host
end
+
+ def normalize_port(options)
+ return nil if options[:port].nil? || options[:port] == false
+
+ case options[:protocol]
+ when "//"
+ nil
+ when "https://"
+ options[:port].to_i == 443 ? nil : options[:port]
+ else
+ options[:port].to_i == 80 ? nil : options[:port]
+ end
+ end
end
def initialize(env)
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 82c55660ea..e288f026c7 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -3,7 +3,7 @@ require 'action_controller/metal/exceptions'
module ActionDispatch
module Journey
# The Formatter class is used for formatting URLs. For example, parameters
- # passed to +url_for+ in rails will eventually call Formatter#generate.
+ # passed to +url_for+ in Rails will eventually call Formatter#generate.
class Formatter # :nodoc:
attr_reader :routes
@@ -58,7 +58,7 @@ module ActionDispatch
end
end
- parameterized_parts.keep_if { |_, v| v }
+ parameterized_parts.keep_if { |_, v| v }
parameterized_parts
end
diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y
index a2e1afed32..040f8d5922 100644
--- a/actionpack/lib/action_dispatch/journey/parser.y
+++ b/actionpack/lib/action_dispatch/journey/parser.y
@@ -36,6 +36,7 @@ rule
;
literal
: LITERAL { result = Literal.new(val.first) }
+ ;
dot
: DOT { result = Dot.new(val.first) }
;
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 6fda085681..50e1853094 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -102,6 +102,10 @@ module ActionDispatch
value === request.send(method).to_s
when Array
value.include?(request.send(method))
+ when TrueClass
+ request.send(method).present?
+ when FalseClass
+ request.send(method).blank?
else
value === request.send(method)
end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index 31868b1814..419e665d12 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -38,7 +38,9 @@ module ActionDispatch
env['REMOTE_ADDR']
end
- def [](k); env[k]; end
+ def [](k)
+ env[k]
+ end
end
attr_reader :request_class, :formatter
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 08c75632ba..5b914f293d 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -31,7 +31,7 @@ module ActionDispatch
#
# # Sets a signed cookie, which prevents users from tampering with its value.
# # The cookie is signed by your app's <tt>config.secret_key_base</tt> value.
- # # It can be read using the signed method <tt>cookies.signed[:key]</tt>
+ # # It can be read using the signed method <tt>cookies.signed[:name]</tt>
# cookies.signed[:user_id] = current_user.id
#
# # Sets a "permanent" cookie (which expires in 20 years from now).
@@ -53,13 +53,13 @@ module ActionDispatch
#
# Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
#
- # cookies[:key] = {
+ # cookies[:name] = {
# value: 'a yummy cookie',
# expires: 1.year.from_now,
# domain: 'domain.com'
# }
#
- # cookies.delete(:key, domain: 'domain.com')
+ # cookies.delete(:name, domain: 'domain.com')
#
# The option symbols for setting cookies are:
#
@@ -70,7 +70,7 @@ module ActionDispatch
# restrict to the domain level. If you use a schema like www.example.com
# and want to share session with user.example.com set <tt>:domain</tt>
# to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
- # <tt>:all</tt> again when deleting keys.
+ # <tt>:all</tt> again when deleting cookies.
#
# domain: nil # Does not sets cookie domain. (default)
# domain: :all # Allow the cookie for the top most level
@@ -280,7 +280,7 @@ module ActionDispatch
# Sets the cookie named +name+. The second argument may be the very cookie
# value, or a hash of options as documented above.
- def []=(key, options)
+ def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
value = options[:value]
@@ -291,10 +291,10 @@ module ActionDispatch
handle_options(options)
- if @cookies[key.to_s] != value or options[:expires]
- @cookies[key.to_s] = value
- @set_cookies[key.to_s] = options
- @delete_cookies.delete(key.to_s)
+ if @cookies[name.to_s] != value or options[:expires]
+ @cookies[name.to_s] = value
+ @set_cookies[name.to_s] = options
+ @delete_cookies.delete(name.to_s)
end
value
@@ -303,24 +303,24 @@ module ActionDispatch
# Removes the cookie on the client machine by setting the value to an empty string
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
- def delete(key, options = {})
- return unless @cookies.has_key? key.to_s
+ def delete(name, options = {})
+ return unless @cookies.has_key? name.to_s
options.symbolize_keys!
handle_options(options)
- value = @cookies.delete(key.to_s)
- @delete_cookies[key.to_s] = options
+ value = @cookies.delete(name.to_s)
+ @delete_cookies[name.to_s] = options
value
end
# Whether the given cookie is to be deleted by this CookieJar.
# Like <tt>[]=</tt>, you can pass in an options hash to test if a
# deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
- def deleted?(key, options = {})
+ def deleted?(name, options = {})
options.symbolize_keys!
handle_options(options)
- @delete_cookies[key.to_s] == options
+ @delete_cookies[name.to_s] == options
end
# Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
@@ -342,7 +342,6 @@ module ActionDispatch
self.always_write_cookie = false
private
-
def write_cookie?(cookie)
@secure || !cookie[:secure] || always_write_cookie
end
@@ -357,11 +356,11 @@ module ActionDispatch
@options = options
end
- def [](key)
+ def [](name)
@parent_jar[name.to_s]
end
- def []=(key, options)
+ def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
else
@@ -369,7 +368,7 @@ module ActionDispatch
end
options[:expires] = 20.years.from_now
- @parent_jar[key] = options
+ @parent_jar[name] = options
end
end
@@ -389,7 +388,7 @@ module ActionDispatch
end
end
- def []=(key, options)
+ def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
options[:value] = @verifier.generate(options[:value])
@@ -398,11 +397,10 @@ module ActionDispatch
end
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
- @parent_jar[key] = options
+ @parent_jar[name] = options
end
private
-
def verify(signed_message)
@verifier.verify(signed_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature
@@ -428,7 +426,7 @@ module ActionDispatch
include ChainedCookieJars
def initialize(parent_jar, key_generator, options = {})
- if ActiveSupport::DummyKeyGenerator === key_generator
+ if ActiveSupport::LegacyKeyGenerator === key_generator
raise "You didn't set config.secret_key_base, which is required for this cookie jar. " +
"Read the upgrade documentation to learn more about this new config option."
end
@@ -440,13 +438,13 @@ module ActionDispatch
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
end
- def [](key)
- if encrypted_message = @parent_jar[key]
+ def [](name)
+ if encrypted_message = @parent_jar[name]
decrypt_and_verify(encrypted_message)
end
end
- def []=(key, options)
+ def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
else
@@ -455,11 +453,10 @@ module ActionDispatch
options[:value] = @encryptor.encrypt_and_sign(options[:value])
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
- @parent_jar[key] = options
+ @parent_jar[name] = options
end
private
-
def decrypt_and_verify(encrypted_message)
@encryptor.decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 7489ce8028..1de3d14530 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -9,9 +9,11 @@ module ActionDispatch
'ActionController::RoutingError' => :not_found,
'AbstractController::ActionNotFound' => :not_found,
'ActionController::MethodNotAllowed' => :method_not_allowed,
+ 'ActionController::UnknownHttpMethod' => :method_not_allowed,
'ActionController::NotImplemented' => :not_implemented,
'ActionController::UnknownFormat' => :not_acceptable,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
+ 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
'ActionController::BadRequest' => :bad_request,
'ActionController::ParameterMissing' => :bad_request
)
diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
index 53bedaa40a..cbb2d475b1 100644
--- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
@@ -7,11 +7,10 @@ module ActionDispatch
end
def call(env)
- exception = env["action_dispatch.exception"]
status = env["PATH_INFO"][1..-1]
request = ActionDispatch::Request.new(env)
content_type = request.formats.first
- body = { :status => status, :error => exception.message }
+ body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) }
render(status, content_type, body)
end
@@ -19,7 +18,7 @@ module ActionDispatch
private
def render(status, content_type, body)
- format = content_type && "to_#{content_type.to_sym}"
+ format = "to_#{content_type.to_sym}" if content_type
if format && body.respond_to?(format)
render_format(status, content_type, body.public_send(format))
else
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
index 44290445d4..5d1740d0d4 100644
--- a/actionpack/lib/action_dispatch/middleware/request_id.rb
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -18,7 +18,7 @@ module ActionDispatch
def call(env)
env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
- @app.call(env).tap { |status, headers, body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
+ @app.call(env).tap { |_status, headers, _body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 550f4dbd0d..db219c8fa9 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -13,7 +13,7 @@
request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
def debug_hash(object)
- object.to_hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
+ object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
end unless self.class.method_defined?(:debug_hash)
%>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index 9d947aea40..b181909bff 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
@@ -1,10 +1,8 @@
<%
- traces = [
- ["Application Trace", @application_trace],
- ["Framework Trace", @framework_trace],
- ["Full Trace", @full_trace]
- ]
- names = traces.collect {|name, trace| name}
+ traces = { "Application Trace" => @application_trace,
+ "Framework Trace" => @framework_trace,
+ "Full Trace" => @full_trace }
+ names = traces.keys
%>
<p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 891c87ac27..bc5d03dc10 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -34,6 +34,12 @@
padding: 0.5em 1.5em;
}
+ h1 {
+ margin: 0.2em 0;
+ line-height: 1.1em;
+ font-size: 2em;
+ }
+
h2 {
color: #C52F24;
line-height: 25px;
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
index 63216ef7c5..31f46ee340 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
@@ -2,7 +2,7 @@
<header>
<h1>
<%= @exception.original_exception.class.to_s %> in
- <%= @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%= @request.parameters["action"] %>
+ <%= @request.parameters["controller"].camelize if @request.parameters["controller"] %>#<%= @request.parameters["action"] %>
</h1>
</header>
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index c5f2b33602..c3fd0c18ec 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -10,6 +10,9 @@ module ActionDispatch
module Routing
class Mapper
URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
+ SCOPE_OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
+ :controller, :path_names, :constraints, :defaults,
+ :shallow, :blocks, :options]
class Constraints #:nodoc:
def self.new(app, constraints, request = Rack::Request)
@@ -58,8 +61,8 @@ module ActionDispatch
@set, @scope, @path, @options = set, scope, path, options
@requirements, @conditions, @defaults = {}, {}, {}
- normalize_path!
normalize_options!
+ normalize_path!
normalize_requirements!
normalize_conditions!
normalize_defaults!
@@ -296,7 +299,7 @@ module ActionDispatch
end
end
- # Invokes Rack::Mount::Utils.normalize path and ensure that
+ # Invokes Journey::Router::Utils.normalize_path and ensure that
# (:locale) becomes (/:locale) instead of /(:locale). Except
# for root cases, where the latter is the correct one.
def self.normalize_path(path)
@@ -486,7 +489,7 @@ module ActionDispatch
end
options = app
- app, path = options.find { |k, v| k.respond_to?(:call) }
+ app, path = options.find { |k, _| k.respond_to?(:call) }
options.delete(app) if app
end
@@ -589,8 +592,7 @@ module ActionDispatch
private
def map_method(method, args, &block)
options = args.extract_options!
- options[:via] = method
- options[:path] ||= args.first if args.first.is_a?(String)
+ options[:via] = method
match(*args, options, &block)
self
end
@@ -698,19 +700,21 @@ module ActionDispatch
block, options[:constraints] = options[:constraints], {}
end
- scope_options.each do |option|
- if value = options.delete(option)
+ SCOPE_OPTIONS.each do |option|
+ if option == :blocks
+ value = block
+ elsif option == :options
+ value = options
+ else
+ value = options.delete(option)
+ end
+
+ if value
recover[option] = @scope[option]
@scope[option] = send("merge_#{option}_scope", @scope[option], value)
end
end
- recover[:blocks] = @scope[:blocks]
- @scope[:blocks] = merge_blocks_scope(@scope[:blocks], block)
-
- recover[:options] = @scope[:options]
- @scope[:options] = merge_options_scope(@scope[:options], options)
-
yield
self
ensure
@@ -841,10 +845,6 @@ module ActionDispatch
end
private
- def scope_options #:nodoc:
- @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
- end
-
def merge_path_scope(parent, child) #:nodoc:
Mapper.normalize_path("#{parent}/#{child}")
end
@@ -945,6 +945,8 @@ module ActionDispatch
VALID_ON_OPTIONS = [:new, :collection, :member]
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
CANONICAL_ACTIONS = %w(index create new show update destroy)
+ RESOURCE_METHOD_SCOPES = [:collection, :member, :new]
+ RESOURCE_SCOPES = [:resource, :resources]
class Resource #:nodoc:
attr_reader :controller, :path, :options, :param
@@ -1361,7 +1363,7 @@ module ActionDispatch
def match(path, *rest)
if rest.empty? && Hash === path
options = path
- path, to = options.find { |name, value| name.is_a?(String) }
+ path, to = options.find { |name, _value| name.is_a?(String) }
options[:to] = to
options.delete(path)
paths = [path]
@@ -1370,18 +1372,23 @@ module ActionDispatch
paths = [path] + rest
end
- path_without_format = path.to_s.sub(/\(\.:format\)$/, '')
- if using_match_shorthand?(path_without_format, options)
- options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
- end
-
options[:anchor] = true unless options.key?(:anchor)
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
end
- paths.each { |_path| decomposed_match(_path, options.dup) }
+ paths.each do |_path|
+ route_options = options.dup
+ route_options[:path] ||= _path if _path.is_a?(String)
+
+ path_without_format = _path.to_s.sub(/\(\.:format\)$/, '')
+ if using_match_shorthand?(path_without_format, route_options)
+ route_options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
+ end
+
+ decomposed_match(_path, route_options)
+ end
self
end
@@ -1494,11 +1501,11 @@ module ActionDispatch
end
def resource_scope? #:nodoc:
- [:resource, :resources].include? @scope[:scope_level]
+ RESOURCE_SCOPES.include? @scope[:scope_level]
end
def resource_method_scope? #:nodoc:
- [:collection, :member, :new].include? @scope[:scope_level]
+ RESOURCE_METHOD_SCOPES.include? @scope[:scope_level]
end
def with_exclusive_scope
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 07203428d4..342b6ec23d 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -170,9 +170,10 @@ module ActionDispatch
def call(t, args)
if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
- @options.merge!(t.url_options) if t.respond_to?(:url_options)
- @options[:path] = optimized_helper(args)
- ActionDispatch::Http::URL.url_for(@options)
+ options = @options.dup
+ options.merge!(t.url_options) if t.respond_to?(:url_options)
+ options[:path] = optimized_helper(args)
+ ActionDispatch::Http::URL.url_for(options)
else
super
end
@@ -665,7 +666,7 @@ module ActionDispatch
end
req = @request_class.new(env)
- @router.recognize(req) do |route, matches, params|
+ @router.recognize(req) do |route, _matches, params|
params.merge!(extras)
params.each do |key, value|
if value.is_a?(String)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 9210bffd1d..496682e8bd 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -81,7 +81,7 @@ module ActionDispatch
# Load routes.rb if it hasn't been loaded.
generated_path, extra_keys = @routes.generate_extras(options, defaults)
- found_extras = options.reject {|k, v| ! extra_keys.include? k}
+ found_extras = options.reject { |k, _| ! extra_keys.include? k }
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
assert_equal(extras, found_extras, msg)
@@ -120,7 +120,7 @@ module ActionDispatch
options[:controller] = "/#{controller}"
end
- generate_options = options.dup.delete_if{ |k,v| defaults.key?(k) }
+ generate_options = options.dup.delete_if{ |k, _| defaults.key?(k) }
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index e481f3b245..3253a3d424 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -377,8 +377,8 @@ module ActionDispatch
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
end
- selected = elements.map do |_element|
- text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
+ selected = elements.map do |elem|
+ text = elem.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
css_select(root, "encoded:root", &block)[0]
end
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index e657283cec..630e6a9b78 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -6,7 +6,7 @@ module ActionDispatch
module TestProcess
def assigns(key = nil)
assigns = {}.with_indifferent_access
- @controller.view_assigns.each {|k, v| assigns.regular_writer(k, v)}
+ @controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
key.nil? ? assigns : assigns[key]
end
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index b5e47d78d1..fd08f392aa 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -1,7 +1,7 @@
module ActionPack
# Returns the version of the currently loaded ActionPack as a Gem::Version
def self.version
- Gem::Version.new "4.0.0.beta1"
+ Gem::Version.new "4.1.0.beta"
end
module VERSION #:nodoc:
diff --git a/actionpack/lib/action_view/buffers.rb b/actionpack/lib/action_view/buffers.rb
index 2372d3c433..361a0dccbe 100644
--- a/actionpack/lib/action_view/buffers.rb
+++ b/actionpack/lib/action_view/buffers.rb
@@ -8,9 +8,15 @@ module ActionView
end
def <<(value)
+ return self if value.nil?
super(value.to_s)
end
alias :append= :<<
+
+ def safe_concat(value)
+ return self if value.nil?
+ super(value.to_s)
+ end
alias :safe_append= :safe_concat
end
diff --git a/actionpack/lib/action_view/dependency_tracker.rb b/actionpack/lib/action_view/dependency_tracker.rb
index a2a555dfcb..45d17be605 100644
--- a/actionpack/lib/action_view/dependency_tracker.rb
+++ b/actionpack/lib/action_view/dependency_tracker.rb
@@ -39,7 +39,7 @@ module ActionView
render\s* # render, followed by optional whitespace
\(? # start an optional parenthesis for the render call
(partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
- ([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture
+ ([@a-z"'][@\w\/\."']+) # the template name itself -- 2nd capture
/x
def self.call(name, template)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 31e37893c6..3a6f449eb8 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -14,7 +14,6 @@ module ActionView
# # => <img alt="Rails" src="/assets/rails.png" />
# stylesheet_link_tag("application")
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
- #
module AssetTagHelper
extend ActiveSupport::Concern
@@ -50,7 +49,6 @@ module ActionView
#
# javascript_include_tag "http://www.example.com/xmlhr.js"
# # => <script src="http://www.example.com/xmlhr.js"></script>
- #
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol').symbolize_keys
@@ -58,7 +56,7 @@ module ActionView
sources.uniq.map { |source|
tag_options = {
"src" => path_to_javascript(source, path_options)
- }.merge(options)
+ }.merge!(options)
content_tag(:script, "", tag_options)
}.join("\n").html_safe
end
@@ -67,7 +65,7 @@ module ActionView
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
# You can modify the link attributes by passing a hash as the last argument.
# For historical reasons, the 'media' attribute will always be present and defaults
- # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
+ # to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
# apply to all media types.
#
# stylesheet_link_tag "style"
@@ -88,7 +86,6 @@ module ActionView
# stylesheet_link_tag "random.styles", "/css/stylish"
# # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
# # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
- #
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol').symbolize_keys
@@ -98,7 +95,7 @@ module ActionView
"rel" => "stylesheet",
"media" => "screen",
"href" => path_to_stylesheet(source, path_options)
- }.merge(options)
+ }.merge!(options)
tag(:link, tag_options)
}.join("\n").html_safe
end
@@ -109,10 +106,13 @@ module ActionView
# +url_options+. You can modify the LINK tag itself in +tag_options+.
#
# ==== Options
+ #
# * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
# * <tt>:type</tt> - Override the auto-generated mime type
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
#
+ # ==== Examples
+ #
# auto_discovery_link_tag
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
# auto_discovery_link_tag(:atom)
@@ -148,11 +148,14 @@ module ActionView
# you can override "rel" and "type".
#
# ==== Options
+ #
# * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon'
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon'
#
+ # ==== Examples
+ #
# favicon_link_tag '/myicon.ico'
- # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ # # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# Mobile Safari looks for a different <link> tag, pointing to an image that
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
@@ -160,19 +163,19 @@ module ActionView
#
# favicon_link_tag '/mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
- #
def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
:type => 'image/vnd.microsoft.icon',
:href => path_to_image(source)
- }.merge(options.symbolize_keys))
+ }.merge!(options.symbolize_keys))
end
# Returns an HTML image tag for the +source+. The +source+ can be a full
# path or a file.
#
# ==== Options
+ #
# You can add HTML attributes using the +options+. The +options+ supports
# three additional keys for convenience and conformance:
#
@@ -250,6 +253,8 @@ module ActionView
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
#
+ # ==== Examples
+ #
# video_tag("trailer")
# # => <video src="/videos/trailer" />
# video_tag("trailer.ogg")
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index d3953c26b7..8fb5eb1548 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -808,7 +808,7 @@ module ActionView
options[:max_years_allowed] = @options[:max_years_allowed] || 1000
if (options[:end] - options[:start]).abs > options[:max_years_allowed]
- raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter"
+ raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
end
build_options_and_select(:year, val, options)
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 36cfb7fca7..36dedf0676 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1152,12 +1152,65 @@ module ActionView
end
end
+ # A +FormBuilder+ object is associated with a particular model object and
+ # allows you to generate fields associated with the model object. The
+ # +FormBuilder+ object is yielded when using +form_for+ or +fields_for+.
+ # For example:
+ #
+ # <%= form_for @person do |person_form| %>
+ # Name: <%= person_form.text_field :name %>
+ # Admin: <%= person_form.check_box :admin %>
+ # <% end %>
+ #
+ # In the above block, the a +FormBuilder+ object is yielded as the
+ # +person_form+ variable. This allows you to generate the +text_field+
+ # and +check_box+ fields by specifying their eponymous methods, which
+ # modify the underlying template and associates the +@person+ model object
+ # with the form.
+ #
+ # The +FormBuilder+ object can be thought of as serving as a proxy for the
+ # methods in the +FormHelper+ module. This class, however, allows you to
+ # call methods with the model object you are building the form for.
+ #
+ # You can create your own custom FormBuilder templates by subclasses this
+ # class. For example:
+ #
+ # class MyFormBuilder < ActionView::Helpers::FormBuilder
+ # def div_radio_button(method, tag_value, options = {})
+ # @template.content_tag(:div,
+ # @template.radio_button(
+ # @object_name, method, tag_value, objectify_options(options)
+ # )
+ # )
+ # end
+ #
+ # The above code creates a new method +div_radio_button+ which wraps a div
+ # around the a new radio button. Note that when options are passed in, you
+ # must called +objectify_options+ in order for the model object to get
+ # correctly passed to the method. If +objectify_options+ is not called,
+ # then the newly created helper will not be linked back to the model.
+ #
+ # The +div_radio_button+ code from above can now be used as follows:
+ #
+ # <%= form_for @person, :builder => MyFormBuilder do |f| %>
+ # I am a child: <%= f.div_radio_button(:admin, "child") %>
+ # I am an adult: <%= f.div_radio_button(:admin, "adult") %>
+ # <% end -%>
+ #
+ # The standard set of helper methods for form building are located in the
+ # +field_helpers+ class attribute.
class FormBuilder
include ModelNaming
# The methods which wrap a form helper call.
class_attribute :field_helpers
- self.field_helpers = FormHelper.instance_methods - [:form_for, :convert_to_model, :model_name_from_record_or_class]
+ self.field_helpers = [:fields_for, :label, :text_field, :password_field,
+ :hidden_field, :file_field, :text_area, :check_box,
+ :radio_button, :color_field, :search_field,
+ :telephone_field, :phone_field, :date_field,
+ :time_field, :datetime_field, :datetime_local_field,
+ :month_field, :week_field, :url_field, :email_field,
+ :number_field, :range_field]
attr_accessor :object_name, :object, :options
@@ -1239,7 +1292,7 @@ module ActionView
# Admin? : <%= permission_fields.check_box :admin %>
# <% end %>
#
- # <%= f.submit %>
+ # <%= person_form.submit %>
# <% end %>
#
# In this case, the checkbox field will be represented by an HTML +input+
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 7e65ebb4e4..ad26505086 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -380,7 +380,7 @@ module ActionView
# should produce the desired results.
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options = collection.map do |element|
- [value_for_collection(element, text_method), value_for_collection(element, value_method)]
+ [value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
end
selected, disabled = extract_selected_and_disabled(selected)
select_deselect = {
@@ -515,7 +515,6 @@ module ActionView
divider = options[:divider]
else
prompt = options
- options = {}
message = "Passing the prompt to grouped_options_for_select as an argument is deprecated. " \
"Please use an options hash like `{ prompt: #{prompt.inspect} }`."
ActiveSupport::Deprecation.warn message
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 8abd5d6e9c..c10566a87d 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -81,7 +81,7 @@ module ActionView
# ==== Options
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
- # * <tt>:include_blank</tt> - If set to true, an empty option will be create
+ # * <tt>:include_blank</tt> - If set to true, an empty option will be created.
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
# * Any other key creates standard HTML attributes for the tag.
#
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index f7e15d0913..edff98ddaa 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -81,7 +81,7 @@ module ActionView
# # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
#
def button_to_function(name, function=nil, html_options={})
- message = "button_to_function is deprecated and will be removed from Rails 4.1. We recommend to use Unobtrusive JavaScript instead. " +
+ message = "button_to_function is deprecated and will be removed from Rails 4.1. We recommend using Unobtrusive JavaScript instead. " +
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
ActiveSupport::Deprecation.warn message
@@ -103,7 +103,7 @@ module ActionView
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
#
def link_to_function(name, function, html_options={})
- message = "link_to_function is deprecated and will be removed from Rails 4.1. We recommend to use Unobtrusive JavaScript instead. " +
+ message = "link_to_function is deprecated and will be removed from Rails 4.1. We recommend using Unobtrusive JavaScript instead. " +
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
ActiveSupport::Deprecation.warn message
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 9e1be65b1a..fda7038a5d 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,6 +28,8 @@ module ActionView
# Formats a +number+ into a US phone number (e.g., (555)
# 123-9876). You can customize the format in the +options+ hash.
#
+ # ==== Options
+ #
# * <tt>:area_code</tt> - Adds parentheses around the area code.
# * <tt>:delimiter</tt> - Specifies the delimiter to use
# (defaults to "-").
@@ -38,6 +40,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_to_phone(5551234) # => 555-1234
# number_to_phone("5551234") # => 555-1234
# number_to_phone(1235551234) # => 123-555-1234
@@ -61,6 +65,8 @@ module ActionView
# Formats a +number+ into a currency string (e.g., $13.65). You
# can customize the format in the +options+ hash.
#
+ # ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the level of precision (defaults
@@ -82,6 +88,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_to_currency(1234567890.50) # => $1,234,567,890.50
# number_to_currency(1234567890.506) # => $1,234,567,890.51
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
@@ -108,6 +116,7 @@ module ActionView
# Formats a +number+ as a percentage string (e.g., 65%). You can
# customize the format in the +options+ hash.
#
+ # ==== Options
#
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
@@ -128,6 +137,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_to_percentage(100) # => 100.000%
# number_to_percentage("98") # => 98.000%
# number_to_percentage(100, precision: 0) # => 100%
@@ -151,6 +162,8 @@ module ActionView
# (e.g., 12,324). You can customize the format in the +options+
# hash.
#
+ # ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
@@ -160,6 +173,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_with_delimiter(12345678) # => 12,345,678
# number_with_delimiter("123456") # => 123,456
# number_with_delimiter(12345678.05) # => 12,345,678.05
@@ -185,6 +200,8 @@ module ActionView
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
# You can customize the format in the +options+ hash.
#
+ # ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
@@ -202,6 +219,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_with_precision(111.2345) # => 111.235
# number_with_precision(111.2345, precision: 2) # => 111.23
# number_with_precision(13, precision: 5) # => 13.00000
@@ -233,6 +252,8 @@ module ActionView
# See <tt>number_to_human</tt> if you want to pretty-print a
# generic number.
#
+ # ==== Options
+ #
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
@@ -252,6 +273,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_to_human_size(123) # => 123 Bytes
# number_to_human_size(1234) # => 1.21 KB
# number_to_human_size(12345) # => 12.1 KB
@@ -324,6 +347,8 @@ module ActionView
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
+ # ==== Examples
+ #
# number_to_human(123) # => "123"
# number_to_human(1234) # => "1.23 Thousand"
# number_to_human(12345) # => "12.3 Thousand"
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 8c7524e795..3939e4737b 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -148,7 +148,7 @@ module ActionView
attrs << tag_option(key, value, escape)
end
end
- " #{attrs.sort * ' '}".html_safe unless attrs.empty?
+ " #{attrs.sort! * ' '}".html_safe unless attrs.empty?
end
def data_tag_option(key, value, escape)
diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb
index aef1572290..3fe3f4e9df 100644
--- a/actionpack/lib/action_view/helpers/tags/base.rb
+++ b/actionpack/lib/action_view/helpers/tags/base.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class Base #:nodoc:
+ module Tags # :nodoc:
+ class Base # :nodoc:
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
include FormOptionsHelper
@@ -73,27 +73,26 @@ module ActionView
def add_default_name_and_id(options)
if options.has_key?("index")
- options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"]) }
+ options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"], options["multiple"]) }
options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
options.delete("index")
elsif defined?(@auto_index)
- options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index) }
+ options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index, options["multiple"]) }
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
else
- options["name"] ||= options.fetch("name"){ tag_name }
+ options["name"] ||= options.fetch("name"){ tag_name(options["multiple"]) }
options["id"] = options.fetch("id"){ tag_id }
end
- options["name"] += "[]" if options["multiple"] && !options["name"].ends_with?("[]")
options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
end
- def tag_name
- "#{@object_name}[#{sanitized_method_name}]"
+ def tag_name(multiple = false)
+ "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
end
- def tag_name_with_index(index)
- "#{@object_name}[#{index}][#{sanitized_method_name}]"
+ def tag_name_with_index(index, multiple = false)
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
end
def tag_id
diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb
index e21cc07746..6d51f2629a 100644
--- a/actionpack/lib/action_view/helpers/tags/check_box.rb
+++ b/actionpack/lib/action_view/helpers/tags/check_box.rb
@@ -2,7 +2,7 @@ require 'action_view/helpers/tags/checkable'
module ActionView
module Helpers
- module Tags
+ module Tags # :nodoc:
class CheckBox < Base #:nodoc:
include Checkable
diff --git a/actionpack/lib/action_view/helpers/tags/checkable.rb b/actionpack/lib/action_view/helpers/tags/checkable.rb
index b97c0c68d7..052e9df662 100644
--- a/actionpack/lib/action_view/helpers/tags/checkable.rb
+++ b/actionpack/lib/action_view/helpers/tags/checkable.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- module Checkable
+ module Tags # :nodoc:
+ module Checkable # :nodoc:
def input_checked?(object, options)
if options.has_key?("checked")
checked = options.delete "checked"
diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
index 9655008fe2..52006d856b 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -2,11 +2,11 @@ require 'action_view/helpers/tags/collection_helpers'
module ActionView
module Helpers
- module Tags
- class CollectionCheckBoxes < Base
+ module Tags # :nodoc:
+ class CollectionCheckBoxes < Base # :nodoc:
include CollectionHelpers
- class CheckBoxBuilder < Builder
+ class CheckBoxBuilder < Builder # :nodoc:
def check_box(extra_html_options={})
html_options = extra_html_options.merge(@input_html_options)
@template_object.check_box(@object_name, @method_name, html_options, @value, nil)
diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
index e92a318c73..cd12ddaf65 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
@@ -1,8 +1,8 @@
module ActionView
module Helpers
- module Tags
- module CollectionHelpers
- class Builder
+ module Tags # :nodoc:
+ module CollectionHelpers # :nodoc:
+ class Builder # :nodoc:
attr_reader :object, :text, :value
def initialize(template_object, object_name, method_name, object,
diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
index 893f4411e7..20be34c1f2 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -2,11 +2,11 @@ require 'action_view/helpers/tags/collection_helpers'
module ActionView
module Helpers
- module Tags
- class CollectionRadioButtons < Base
+ module Tags # :nodoc:
+ class CollectionRadioButtons < Base # :nodoc:
include CollectionHelpers
- class RadioButtonBuilder < Builder
+ class RadioButtonBuilder < Builder # :nodoc:
def radio_button(extra_html_options={})
html_options = extra_html_options.merge(@input_html_options)
@template_object.radio_button(@object_name, @method_name, @value, html_options)
diff --git a/actionpack/lib/action_view/helpers/tags/collection_select.rb b/actionpack/lib/action_view/helpers/tags/collection_select.rb
index ec78e6e5f9..6cb2b2e0d3 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_select.rb
@@ -1,6 +1,6 @@
module ActionView
module Helpers
- module Tags
+ module Tags # :nodoc:
class CollectionSelect < Base #:nodoc:
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
@collection = collection
diff --git a/actionpack/lib/action_view/helpers/tags/color_field.rb b/actionpack/lib/action_view/helpers/tags/color_field.rb
index 6f08f8483a..d8fc797035 100644
--- a/actionpack/lib/action_view/helpers/tags/color_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/color_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class ColorField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class ColorField < TextField # :nodoc:
def render
options = @options.stringify_keys
options["value"] = @options.fetch("value") { validate_color_string(value(object)) }
diff --git a/actionpack/lib/action_view/helpers/tags/date_field.rb b/actionpack/lib/action_view/helpers/tags/date_field.rb
index 64c29dea3d..c22be0db29 100644
--- a/actionpack/lib/action_view/helpers/tags/date_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/date_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class DateField < DatetimeField #:nodoc:
+ module Tags # :nodoc:
+ class DateField < DatetimeField # :nodoc:
private
def format_date(value)
diff --git a/actionpack/lib/action_view/helpers/tags/date_select.rb b/actionpack/lib/action_view/helpers/tags/date_select.rb
index 734591394b..0c4ac40070 100644
--- a/actionpack/lib/action_view/helpers/tags/date_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/date_select.rb
@@ -2,8 +2,8 @@ require 'active_support/core_ext/time/calculations'
module ActionView
module Helpers
- module Tags
- class DateSelect < Base #:nodoc:
+ module Tags # :nodoc:
+ class DateSelect < Base # :nodoc:
def initialize(object_name, method_name, template_object, options, html_options)
@html_options = html_options
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_field.rb
index e407146e96..9a2279c611 100644
--- a/actionpack/lib/action_view/helpers/tags/datetime_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/datetime_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class DatetimeField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class DatetimeField < TextField # :nodoc:
def render
options = @options.stringify_keys
options["value"] = @options.fetch("value") { format_date(value(object)) }
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb
index 6668d6d718..b4a74185d1 100644
--- a/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class DatetimeLocalField < DatetimeField #:nodoc:
+ module Tags # :nodoc:
+ class DatetimeLocalField < DatetimeField # :nodoc:
class << self
def field_type
@field_type ||= "datetime-local"
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_select.rb b/actionpack/lib/action_view/helpers/tags/datetime_select.rb
index a32c840bce..563de1840e 100644
--- a/actionpack/lib/action_view/helpers/tags/datetime_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/datetime_select.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class DatetimeSelect < DateSelect #:nodoc:
+ module Tags # :nodoc:
+ class DatetimeSelect < DateSelect # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/email_field.rb b/actionpack/lib/action_view/helpers/tags/email_field.rb
index 45cde507d7..7ce3ccb9bf 100644
--- a/actionpack/lib/action_view/helpers/tags/email_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/email_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class EmailField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class EmailField < TextField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/file_field.rb b/actionpack/lib/action_view/helpers/tags/file_field.rb
index 59f2ff71b4..476b820d84 100644
--- a/actionpack/lib/action_view/helpers/tags/file_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/file_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class FileField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class FileField < TextField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb
index 507ba8835f..2ed4712dac 100644
--- a/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/grouped_collection_select.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class GroupedCollectionSelect < Base #:nodoc:
+ module Tags # :nodoc:
+ class GroupedCollectionSelect < Base # :nodoc:
def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
@collection = collection
@group_method = group_method
diff --git a/actionpack/lib/action_view/helpers/tags/hidden_field.rb b/actionpack/lib/action_view/helpers/tags/hidden_field.rb
index a8d13dc1b1..c3757c2461 100644
--- a/actionpack/lib/action_view/helpers/tags/hidden_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/hidden_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class HiddenField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class HiddenField < TextField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/label.rb b/actionpack/lib/action_view/helpers/tags/label.rb
index 16135fcd5a..35d3ba8434 100644
--- a/actionpack/lib/action_view/helpers/tags/label.rb
+++ b/actionpack/lib/action_view/helpers/tags/label.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class Label < Base #:nodoc:
+ module Tags # :nodoc:
+ class Label < Base # :nodoc:
def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
options ||= {}
diff --git a/actionpack/lib/action_view/helpers/tags/month_field.rb b/actionpack/lib/action_view/helpers/tags/month_field.rb
index 3d3c32d847..4c0fb846ee 100644
--- a/actionpack/lib/action_view/helpers/tags/month_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/month_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class MonthField < DatetimeField #:nodoc:
+ module Tags # :nodoc:
+ class MonthField < DatetimeField # :nodoc:
private
def format_date(value)
diff --git a/actionpack/lib/action_view/helpers/tags/number_field.rb b/actionpack/lib/action_view/helpers/tags/number_field.rb
index 9cd04434f0..4f95b1b4de 100644
--- a/actionpack/lib/action_view/helpers/tags/number_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/number_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class NumberField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class NumberField < TextField # :nodoc:
def render
options = @options.stringify_keys
diff --git a/actionpack/lib/action_view/helpers/tags/password_field.rb b/actionpack/lib/action_view/helpers/tags/password_field.rb
index 6e7a4d3c36..6099fa6f19 100644
--- a/actionpack/lib/action_view/helpers/tags/password_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/password_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class PasswordField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class PasswordField < TextField # :nodoc:
def render
@options = {:value => nil}.merge!(@options)
super
diff --git a/actionpack/lib/action_view/helpers/tags/radio_button.rb b/actionpack/lib/action_view/helpers/tags/radio_button.rb
index 8a0421f061..4849c537a5 100644
--- a/actionpack/lib/action_view/helpers/tags/radio_button.rb
+++ b/actionpack/lib/action_view/helpers/tags/radio_button.rb
@@ -2,8 +2,8 @@ require 'action_view/helpers/tags/checkable'
module ActionView
module Helpers
- module Tags
- class RadioButton < Base #:nodoc:
+ module Tags # :nodoc:
+ class RadioButton < Base # :nodoc:
include Checkable
def initialize(object_name, method_name, template_object, tag_value, options)
diff --git a/actionpack/lib/action_view/helpers/tags/range_field.rb b/actionpack/lib/action_view/helpers/tags/range_field.rb
index 47db4680e7..f98ae88043 100644
--- a/actionpack/lib/action_view/helpers/tags/range_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/range_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class RangeField < NumberField #:nodoc:
+ module Tags # :nodoc:
+ class RangeField < NumberField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/search_field.rb b/actionpack/lib/action_view/helpers/tags/search_field.rb
index 818fd4b887..c09e2f1be7 100644
--- a/actionpack/lib/action_view/helpers/tags/search_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/search_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class SearchField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class SearchField < TextField # :nodoc:
def render
options = @options.stringify_keys
diff --git a/actionpack/lib/action_view/helpers/tags/select.rb b/actionpack/lib/action_view/helpers/tags/select.rb
index 53a108b7e6..d64e2f68ef 100644
--- a/actionpack/lib/action_view/helpers/tags/select.rb
+++ b/actionpack/lib/action_view/helpers/tags/select.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class Select < Base #:nodoc:
+ module Tags # :nodoc:
+ class Select < Base # :nodoc:
def initialize(object_name, method_name, template_object, choices, options, html_options)
@choices = choices
@choices = @choices.to_a if @choices.is_a?(Range)
@@ -31,7 +31,6 @@ module ActionView
#
# [nil, []]
# { nil => [] }
- #
def grouped_choices?
!@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
end
diff --git a/actionpack/lib/action_view/helpers/tags/tel_field.rb b/actionpack/lib/action_view/helpers/tags/tel_field.rb
index 87c1f6b6b6..987bb9e67a 100644
--- a/actionpack/lib/action_view/helpers/tags/tel_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/tel_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TelField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class TelField < TextField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/text_area.rb b/actionpack/lib/action_view/helpers/tags/text_area.rb
index f74652c5e7..c81156c0c8 100644
--- a/actionpack/lib/action_view/helpers/tags/text_area.rb
+++ b/actionpack/lib/action_view/helpers/tags/text_area.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TextArea < Base #:nodoc:
+ module Tags # :nodoc:
+ class TextArea < Base # :nodoc:
def render
options = @options.stringify_keys
add_default_name_and_id(options)
diff --git a/actionpack/lib/action_view/helpers/tags/text_field.rb b/actionpack/lib/action_view/helpers/tags/text_field.rb
index 024a1a8af2..baa5ff768e 100644
--- a/actionpack/lib/action_view/helpers/tags/text_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/text_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TextField < Base #:nodoc:
+ module Tags # :nodoc:
+ class TextField < Base # :nodoc:
def render
options = @options.stringify_keys
options["size"] = options["maxlength"] unless options.key?("size")
diff --git a/actionpack/lib/action_view/helpers/tags/time_field.rb b/actionpack/lib/action_view/helpers/tags/time_field.rb
index a3941860c9..0e90a3aed7 100644
--- a/actionpack/lib/action_view/helpers/tags/time_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/time_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TimeField < DatetimeField #:nodoc:
+ module Tags # :nodoc:
+ class TimeField < DatetimeField # :nodoc:
private
def format_date(value)
diff --git a/actionpack/lib/action_view/helpers/tags/time_select.rb b/actionpack/lib/action_view/helpers/tags/time_select.rb
index 9e97deb706..0b06311d25 100644
--- a/actionpack/lib/action_view/helpers/tags/time_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/time_select.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TimeSelect < DateSelect #:nodoc:
+ module Tags # :nodoc:
+ class TimeSelect < DateSelect # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/time_zone_select.rb b/actionpack/lib/action_view/helpers/tags/time_zone_select.rb
index 0a176157c3..80d165ec7e 100644
--- a/actionpack/lib/action_view/helpers/tags/time_zone_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/time_zone_select.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class TimeZoneSelect < Base #:nodoc:
+ module Tags # :nodoc:
+ class TimeZoneSelect < Base # :nodoc:
def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
@priority_zones = priority_zones
@html_options = html_options
diff --git a/actionpack/lib/action_view/helpers/tags/url_field.rb b/actionpack/lib/action_view/helpers/tags/url_field.rb
index 1ffdfe0b3c..d76340178d 100644
--- a/actionpack/lib/action_view/helpers/tags/url_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/url_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class UrlField < TextField #:nodoc:
+ module Tags # :nodoc:
+ class UrlField < TextField # :nodoc:
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/week_field.rb b/actionpack/lib/action_view/helpers/tags/week_field.rb
index 1e13939a0a..5b3d0494e9 100644
--- a/actionpack/lib/action_view/helpers/tags/week_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/week_field.rb
@@ -1,7 +1,7 @@
module ActionView
module Helpers
- module Tags
- class WeekField < DatetimeField #:nodoc:
+ module Tags # :nodoc:
+ class WeekField < DatetimeField # :nodoc:
private
def format_date(value)
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 2e124cf085..147f9fd8ed 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -126,8 +126,8 @@ module ActionView
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
- # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
- # <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
+ # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
+ # <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
# isn't found, nil is returned.
#
# excerpt('This is an example', 'an', radius: 5)
@@ -145,7 +145,7 @@ module ActionView
# excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
# # => <chop> is also an example
#
- # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
+ # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
# # => ...a very beautiful...
def excerpt(text, phrase, options = {})
return unless text && phrase
@@ -250,8 +250,11 @@ module ActionView
# simple_format("Look ma! A class!", class: 'description')
# # => "<p class='description'>Look ma! A class!</p>"
#
- # simple_format("<span>I'm allowed!</span> It's true.", {}, sanitize: false)
- # # => "<p><span>I'm allowed!</span> It's true.</p>"
+ # simple_format("<blink>Unblinkable.</blink>")
+ # # => "<p>Unblinkable.</p>"
+ #
+ # simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
+ # # => "<p><blink>Blinkable!</span> It's true.</p>"
def simple_format(text, html_options = {}, options = {})
wrapper_tag = options.fetch(:wrapper_tag, :p)
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 775d93ed39..22059a0170 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -425,8 +425,8 @@ module ActionView
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
#
# ==== Obfuscation
- # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
- # in order to hinder email harvesters. To take advantage of these options,
+ # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
+ # in order to hinder email harvesters. To take advantage of these options,
# install the +actionview-encoded_mail_to+ gem.
#
# ==== Examples
@@ -439,18 +439,30 @@ module ActionView
# mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
# subject: "This is an example email"
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
- def mail_to(email_address, name = nil, html_options = {})
+ #
+ # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
+ #
+ # <%= mail_to "me@domain.com" do %>
+ # <strong>Email me:</strong> <span>me@domain.com</span>
+ # <% end %>
+ # # => <a href="mailto:me@domain.com">
+ # <strong>Email me:</strong> <span>me@domain.com</span>
+ # </a>
+ def mail_to(email_address, name = nil, html_options = {}, &block)
email_address = ERB::Util.html_escape(email_address)
- html_options.stringify_keys!
+ html_options, name = name, nil if block_given?
+ html_options = (html_options || {}).stringify_keys
extras = %w{ cc bcc body subject }.map { |item|
option = html_options.delete(item) || next
"#{item}=#{Rack::Utils.escape_path(option)}"
}.compact
extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
-
- content_tag "a", name || email_address.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
+
+ html_options["href"] = "mailto:#{email_address}#{extras}".html_safe
+
+ content_tag(:a, name || email_address.html_safe, html_options, &block)
end
# True if the current request URI was generated by the given +options+.
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index d61cc0f304..f9d5b97fe3 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -1,5 +1,6 @@
require 'thread_safe'
require 'active_support/core_ext/module/remove_method'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
# = Action View Lookup Context
diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb
index d9c76366f8..91ee2ea8f5 100644
--- a/actionpack/lib/action_view/path_set.rb
+++ b/actionpack/lib/action_view/path_set.rb
@@ -1,5 +1,11 @@
module ActionView #:nodoc:
# = Action View PathSet
+ #
+ # This class is used to store and access paths in Action View. A number of
+ # operations are defined so that you can search among the paths in this
+ # set and also perform operations on other +PathSet+ objects.
+ #
+ # A +LookupContext+ will use a +PathSet+ to store the paths in its context.
class PathSet #:nodoc:
include Enumerable
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index 6fb8cbb46c..73c19a0ae2 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -1,4 +1,19 @@
module ActionView
+ # This class defines the interface for a renderer. Each class that
+ # subclasses +AbstractRenderer+ is used by the base +Renderer+ class to
+ # render a specific type of object.
+ #
+ # The base +Renderer+ class uses its +render+ method to delegate to the
+ # renderers. These currently consist of
+ #
+ # PartialRenderer - Used for rendering partials
+ # TemplateRenderer - Used for rendering other types of templates
+ # StreamingTemplateRenderer - Used for streaming
+ #
+ # Whenever the +render+ method is called on the base +Renderer+ class, a new
+ # renderer object of the correct type is created, and the +render+ method on
+ # that new object is called in turn. This abstracts the setup and rendering
+ # into a separate classes for partials and templates.
class AbstractRenderer #:nodoc:
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 43a88b0623..821026268a 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -313,6 +313,13 @@ module ActionView
private
+ # Sets up instance variables needed for rendering a partial. This method
+ # finds the options and details and extracts them. The method also contains
+ # logic that handles the type of object passed in as the partial.
+ #
+ # If +options[:partial]+ is a string, then the +@path+ instance variable is
+ # set to that string. Otherwise, the +options[:partial]+ object must
+ # respond to +to_partial_path+ in order to setup the path.
def setup(context, options, block)
@view = context
partial = options[:partial]
@@ -413,6 +420,13 @@ module ActionView
end
end
+ # Obtains the path to where the object's partial is located. If the object
+ # responds to +to_partial_path+, then +to_partial_path+ will be called and
+ # will provide the path. If the object does not respond to +to_partial_path+,
+ # then an +ArgumentError+ is raised.
+ #
+ # If +prefix_partial_path_with_controller_namespace+ is true, then this
+ # method will prefix the partial paths with a namespace.
def partial_path(object = @object)
object = object.to_model if object.respond_to?(:to_model)
diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb
index 30a0c4be70..964b18337e 100644
--- a/actionpack/lib/action_view/renderer/renderer.rb
+++ b/actionpack/lib/action_view/renderer/renderer.rb
@@ -2,6 +2,12 @@ module ActionView
# This is the main entry point for rendering. It basically delegates
# to other objects like TemplateRenderer and PartialRenderer which
# actually renders the template.
+ #
+ # The Renderer will parse the options from the +render+ or +render_body+
+ # method and render a partial or a template based on the options. The
+ # +TemplateRenderer+ and +PartialRenderer+ objects are wrappers which do all
+ # the setup and logic necessary to render a view and a new object is created
+ # each time +render+ is called.
class Renderer
attr_accessor :lookup_context
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 946db1df79..ebbc1c79d6 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -138,7 +138,7 @@ module ActionView
# we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
def render(view, locals, buffer=nil, &block)
- ActiveSupport::Notifications.instrument("!render_template.action_view", virtual_path: @virtual_path, identifier: @identifier) do
+ instrument("!render_template") do
compile!(view)
view.send(method_name, locals, buffer, &block)
end
@@ -245,7 +245,9 @@ module ActionView
mod = view.singleton_class
end
- compile(view, mod)
+ instrument("!compile_template") do
+ compile(view, mod)
+ end
# Just discard the source if we have a virtual path. This
# means we can get the template back.
@@ -335,5 +337,10 @@ module ActionView
def identifier_method_name #:nodoc:
inspect.gsub(/[^a-z_]/, '_')
end
+
+ def instrument(action, &block)
+ payload = { virtual_path: @virtual_path, identifier: @identifier }
+ ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block)
+ end
end
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 5aaafc15c1..7d7a7af51d 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -6,12 +6,23 @@ module ActionView
module Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
+ @newline_pending = 0
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
end
def add_text(src, text)
return if text.empty?
- src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
+
+ if text == "\n"
+ @newline_pending += 1
+ else
+ src << "@output_buffer.safe_append='"
+ src << "\n" * @newline_pending if @newline_pending > 0
+ src << escape_text(text)
+ src << "';"
+
+ @newline_pending = 0
+ end
end
# Erubis toggles <%= and <%== behavior when escaping is enabled.
@@ -28,24 +39,39 @@ module ActionView
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
def add_expr_literal(src, code)
+ flush_newline_if_pending(src)
if code =~ BLOCK_EXPR
src << '@output_buffer.append= ' << code
else
- src << '@output_buffer.append= (' << code << ');'
+ src << '@output_buffer.append=(' << code << ');'
end
end
def add_expr_escaped(src, code)
+ flush_newline_if_pending(src)
if code =~ BLOCK_EXPR
src << "@output_buffer.safe_append= " << code
else
- src << "@output_buffer.safe_concat((" << code << ").to_s);"
+ src << "@output_buffer.safe_append=(" << code << ");"
end
end
+ def add_stmt(src, code)
+ flush_newline_if_pending(src)
+ super
+ end
+
def add_postamble(src)
+ flush_newline_if_pending(src)
src << '@output_buffer.to_s'
end
+
+ def flush_newline_if_pending(src)
+ if @newline_pending > 0
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}';"
+ @newline_pending = 0
+ end
+ end
end
class ERB
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 6b9b0a826b..3304605c1a 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -47,7 +47,7 @@ module ActionView
NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
- # usually a majority of template look ups return nothing, use this canonical preallocated array to safe memory
+ # usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
NO_TEMPLATES = [].freeze
def initialize