aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/controller_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb68
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb2
-rw-r--r--actionpack/lib/sprockets/railtie.rb15
11 files changed, 87 insertions, 50 deletions
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 1e43104f0a..505d5560b1 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -3,9 +3,10 @@ require 'active_support/memoizable'
module ActionDispatch
module Http
class Headers < ::Hash
- extend ActiveSupport::Memoizable
+ @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }
def initialize(*args)
+
if args.size == 1 && args[0].is_a?(Hash)
super()
update(args[0])
@@ -25,9 +26,8 @@ module ActionDispatch
private
# Converts a HTTP header name to an environment variable name.
def env_name(header_name)
- "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
+ @@env_cache[header_name]
end
- memoize :env_name
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 37effade4f..a15ad28f16 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -4,7 +4,7 @@ module ActionDispatch
attr_accessor :original_filename, :content_type, :tempfile, :headers
def initialize(hash)
- @original_filename = hash[:filename]
+ @original_filename = encode_filename(hash[:filename])
@content_type = hash[:type]
@headers = hash[:head]
@tempfile = hash[:tempfile]
@@ -30,6 +30,16 @@ module ActionDispatch
def size
@tempfile.size
end
+
+ private
+ def encode_filename(filename)
+ # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
+ if "ruby".encoding_aware? && filename
+ filename.force_encoding("UTF-8").encode!
+ else
+ filename
+ end
+ end
end
module Upload
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 74c090f260..1dcd83ceb5 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -161,7 +161,7 @@ module ActionDispatch
# Consider the following route, which you will find commented out at the
# bottom of your generated <tt>config/routes.rb</tt>:
#
- # match ':controller(/:action(/:id(.:format)))'
+ # match ':controller(/:action(/:id))(.:format)'
#
# This route states that it expects requests to consist of a
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 70ea419e81..65895590bf 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -87,7 +87,7 @@ module ActionDispatch
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
end
end
- end
+ end
# match "account/overview"
def using_match_shorthand?(path, options)
diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb
index db59bca159..1a583e62ae 100644
--- a/actionpack/lib/action_view/helpers/controller_helper.rb
+++ b/actionpack/lib/action_view/helpers/controller_helper.rb
@@ -20,4 +20,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 56e3af683b..6a724749f4 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -621,7 +621,6 @@ module ActionView
end
class DateTimeSelector #:nodoc:
- extend ActiveSupport::Memoizable
include ActionView::Helpers::TagHelper
DEFAULT_PREFIX = 'date'.freeze
@@ -786,11 +785,12 @@ module ActionView
# Returns translated month names, but also ensures that a custom month
# name array has a leading nil element.
def month_names
- month_names = @options[:use_month_names] || translated_month_names
- month_names.unshift(nil) if month_names.size < 13
- month_names
+ @month_names ||= begin
+ month_names = @options[:use_month_names] || translated_month_names
+ month_names.unshift(nil) if month_names.size < 13
+ month_names
+ end
end
- memoize :month_names
# Returns translated month names.
# => [nil, "January", "February", "March",
@@ -825,9 +825,8 @@ module ActionView
end
def date_order
- @options[:order] || translated_date_order
+ @date_order ||= @options[:order] || translated_date_order
end
- memoize :date_order
def translated_date_order
I18n.translate(:'date.order', :locale => @options[:locale]) || []
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 6513edcf6e..3dc6d65432 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -128,6 +128,28 @@ module ActionView
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
# or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
+ #
+ # ==== Gotcha
+ #
+ # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
+ # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
+ # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
+ # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
+ # any mass-assignment idiom like
+ #
+ # @user.update_attributes(params[:user])
+ #
+ # wouldn't update roles.
+ #
+ # To prevent this the helper generates an auxiliary hidden field before
+ # every multiple select. The hidden field has the same name as multiple select and blank value.
+ #
+ # This way, the client either sends only the hidden field (representing
+ # the deselected multiple select box), or both fields. Since the HTML specification
+ # says key/value pairs have to be sent in the same order they appear in the
+ # form, and parameters extraction gets the last occurrence of any repeated
+ # key in the query string, that works for ordinary forms.
+ #
def select(object, method, choices, options = {}, html_options = {})
InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
end
@@ -552,43 +574,26 @@ module ActionView
include FormOptionsHelper
def to_select_tag(choices, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- selected_value = options.has_key?(:selected) ? options[:selected] : value
- disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
- content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options)
+ selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
+ select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, html_options)
end
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
- selected_value = options.has_key?(:selected) ? options[:selected] : value
- content_tag(
- "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options
+ selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
+ select_content_tag(
+ options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
)
end
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- content_tag(
- "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
+ select_content_tag(
+ option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
)
end
def to_time_zone_select_tag(priority_zones, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- content_tag("select",
- add_options(
- time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
- options, value
- ), html_options
+ select_content_tag(
+ time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
)
end
@@ -603,6 +608,17 @@ module ActionView
end
option_tags.html_safe
end
+
+ def select_content_tag(option_tags, options, html_options)
+ html_options = html_options.stringify_keys
+ add_default_name_and_id(html_options)
+ select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
+ if html_options["multiple"]
+ tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
+ else
+ select
+ end
+ end
end
class FormBuilder
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index a91e86f4db..72bc4510b5 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -597,6 +597,12 @@ module ActionView
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
end
+ # Creates the hidden UTF8 enforcer tag. Override this method in a helper
+ # to customize the tag.
+ def utf8_enforcer_tag
+ tag(:input, :type => "hidden", :name => "utf8", :value => "&#x2713;".html_safe)
+ end
+
private
def html_options_for_form(url_for_options, options, *parameters_for_url)
options.stringify_keys.tap do |html_options|
@@ -611,9 +617,6 @@ module ActionView
end
def extra_tags_for_form(html_options)
- snowman_tag = tag(:input, :type => "hidden",
- :name => "utf8", :value => "&#x2713;".html_safe)
-
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
@@ -629,7 +632,7 @@ module ActionView
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
- tags = snowman_tag << method_tag
+ tags = utf8_enforcer_tag << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 472b996a5e..4be95d8f7e 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -255,7 +255,7 @@ module ActionView
# simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
# # => "<p><span>I'm allowed!</span> It's true.</p>"
def simple_format(text, html_options={}, options={})
- text = ''.html_safe if text.nil?
+ text = '' if text.nil?
start_tag = tag('p', html_options, true)
text = sanitize(text) unless options[:sanitize] == false
text = text.to_str
@@ -263,7 +263,7 @@ module ActionView
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
text.insert 0, start_tag
- text = ActiveSupport::SafeBuffer.new(text).safe_concat("</p>")
+ text.html_safe.safe_concat("</p>")
end
# Creates a Cycle object whose _to_s_ method cycles through elements of an
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index fd8fe417d0..26b6e8b599 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -5,7 +5,7 @@ module I18n
class ExceptionHandler
include Module.new {
def call(exception, locale, key, options)
- exception.is_a?(MissingTranslation) ? super.html_safe : super
+ exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
end
}
end
diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb
index 4b497d142d..38eb00ce01 100644
--- a/actionpack/lib/sprockets/railtie.rb
+++ b/actionpack/lib/sprockets/railtie.rb
@@ -54,12 +54,21 @@ module Sprockets
env = Sprockets::Environment.new(app.root.to_s)
env.static_root = File.join(app.root.join("public"), assets.prefix)
- env.paths.concat assets.paths
+
+ if env.respond_to?(:append_path)
+ assets.paths.each { |path| env.append_path(path) }
+ else
+ env.paths.concat assets.paths
+ end
env.logger = Rails.logger
- env.js_compressor = expand_js_compressor(assets.js_compressor)
- env.css_compressor = expand_css_compressor(assets.css_compressor)
+ if assets.compress
+ # temporarily hardcode default JS compressor to uglify. Soon, it will work
+ # the same as SCSS, where a default plugin sets the default.
+ env.js_compressor = expand_js_compressor(assets.js_compressor || :uglifier)
+ env.css_compressor = expand_css_compressor(assets.css_compressor)
+ end
env
end