aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md43
-rw-r--r--actionpack/actionpack.gemspec1
-rw-r--r--actionpack/lib/abstract_controller/base.rb16
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb22
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb4
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb8
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb10
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb79
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb52
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb214
-rw-r--r--actionpack/lib/action_view/helpers/tags.rb5
-rw-r--r--actionpack/lib/action_view/helpers/tags/color_field.rb25
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_field.rb13
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_field.rb22
-rw-r--r--actionpack/lib/action_view/helpers/tags/datetime_local_field.rb19
-rw-r--r--actionpack/lib/action_view/helpers/tags/month_field.rb13
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_field.rb13
-rw-r--r--actionpack/lib/action_view/helpers/tags/week_field.rb13
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb34
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb51
-rw-r--r--actionpack/lib/action_view/locale/en.yml98
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb2
-rw-r--r--actionpack/test/controller/force_ssl_test.rb31
-rw-r--r--actionpack/test/controller/helper_test.rb30
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb4
-rw-r--r--actionpack/test/dispatch/request/session_test.rb16
-rw-r--r--actionpack/test/dispatch/routing_test.rb2
-rw-r--r--actionpack/test/fixtures/helpers1_pack/pack1_helper.rb5
-rw-r--r--actionpack/test/fixtures/helpers2_pack/pack2_helper.rb5
-rw-r--r--actionpack/test/lib/controller/fake_models.rb3
-rw-r--r--actionpack/test/template/form_helper_test.rb171
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb25
-rw-r--r--actionpack/test/template/number_helper_test.rb11
-rw-r--r--actionpack/test/template/text_helper_test.rb67
-rw-r--r--actionpack/test/template/translation_helper_test.rb16
-rw-r--r--actionpack/test/template/url_helper_test.rb27
39 files changed, 810 insertions, 377 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 1e6e84ea4a..b4d50b7072 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,48 @@
## Rails 4.0.0 (unreleased) ##
+* Extracted redirect logic from `ActionController::ForceSSL::ClassMethods.force_ssl` into `ActionController::ForceSSL#force_ssl_redirect`
+
+ *Jeremy Friesen*
+
+* Make possible to use a block in button_to helper if button text is hard
+ to fit into the name parameter, e.g.:
+
+ <%= button_to [:make_happy, @user] do %>
+ Make happy <strong><%= @user.name %></strong>
+ <% end %>
+ # => "<form method="post" action="/users/1/make_happy" class="button_to">
+ # <div>
+ # <button type="submit">
+ # Make happy <strong>Name</strong>
+ # </button>
+ # </div>
+ # </form>"
+
+ *Sergey Nartimov*
+
+* change a way of ordering helpers from several directories. Previously,
+ when loading helpers from multiple paths, all of the helpers files were
+ gathered into one array an then they were sorted. Helpers from different
+ directories should not be mixed before loading them to make loading more
+ predictable. The most common use case for such behavior is loading helpers
+ from engines. When you load helpers from application and engine Foo, in
+ that order, first rails will load all of the helpers from application,
+ sorted alphabetically and then it will do the same for Foo engine.
+
+ *Piotr Sarnacki*
+
+* `truncate` now always returns an escaped HTMl-safe string. The option `:escape` can be used as
+ false to not escape the result.
+
+ *Li Ellis Gallardo + Rafael Mendonça França*
+
+* `truncate` now accepts a block to show extra content when the text is truncated. *Li Ellis Gallardo*
+
+* Add `week_field`, `week_field_tag`, `month_field`, `month_field_tag`, `datetime_local_field`,
+ `datetime_local_field_tag`, `datetime_field` and `datetime_field_tag` helpers. *Carlos Galdino*
+
+* Add `color_field` and `color_field_tag` helpers. *Carlos Galdino*
+
* `assert_generates`, `assert_recognizes`, and `assert_routing` all raise
`Assertion` instead of `RoutingError` *David Chelimsky*
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 589a67dc02..ae26d6f9e5 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -7,6 +7,7 @@ Gem::Specification.new do |s|
s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).'
s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.'
s.required_ruby_version = '>= 1.9.3'
+ s.license = 'MIT'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 97a9eec144..9c3960961b 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -51,7 +51,7 @@ module AbstractController
# to specify particular actions as hidden.
#
# ==== Returns
- # * <tt>array</tt> - An array of method names that should not be considered actions.
+ # * <tt>Array</tt> - An array of method names that should not be considered actions.
def hidden_actions
[]
end
@@ -63,7 +63,7 @@ module AbstractController
# itself. Finally, #hidden_actions are removed.
#
# ==== Returns
- # * <tt>set</tt> - A set of all methods that should be considered actions.
+ # * <tt>Set</tt> - A set of all methods that should be considered actions.
def action_methods
@action_methods ||= begin
# All public instance methods of this class, including ancestors
@@ -92,11 +92,12 @@ module AbstractController
# controller_path.
#
# ==== Returns
- # * <tt>string</tt>
+ # * <tt>String</tt>
def controller_path
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
end
+ # Refresh the cached action_methods when a new action_method is added.
def method_added(name)
super
clear_action_methods!
@@ -130,6 +131,7 @@ module AbstractController
self.class.controller_path
end
+ # Delegates to the class' #action_methods
def action_methods
self.class.action_methods
end
@@ -139,8 +141,14 @@ module AbstractController
#
# Notice that <tt>action_methods.include?("foo")</tt> may return
# false and <tt>available_action?("foo")</tt> returns true because
- # available action consider actions that are also available
+ # this method considers actions that are also available
# through other means, for example, implicit render ones.
+ #
+ # ==== Parameters
+ # * <tt>action_name</tt> - The name of an action to be tested
+ #
+ # ==== Returns
+ # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
def available_action?(action_name)
method_for_action(action_name).present?
end
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index ac12cbb625..77d799a38a 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -40,15 +40,23 @@ module ActionController
def force_ssl(options = {})
host = options.delete(:host)
before_filter(options) do
- unless request.ssl?
- redirect_options = {:protocol => 'https://', :status => :moved_permanently}
- redirect_options.merge!(:host => host) if host
- redirect_options.merge!(:params => request.query_parameters)
- flash.keep if respond_to?(:flash)
- redirect_to redirect_options
- end
+ force_ssl_redirect(host)
end
end
end
+
+ # 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)
+ unless request.ssl?
+ redirect_options = {:protocol => 'https://', :status => :moved_permanently}
+ redirect_options.merge!(:host => host) if host
+ redirect_options.merge!(:params => request.query_parameters)
+ flash.keep if respond_to?(:flash)
+ redirect_to redirect_options
+ end
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 86d061e3b7..66cdfd40ff 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -95,9 +95,9 @@ module ActionController
helpers = []
Array(path).each do |_path|
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
- helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
+ helpers += names.sort
end
- helpers.sort!
helpers.uniq!
helpers
end
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index 4ad7071820..d8bcc28613 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -87,6 +87,14 @@ module ActionDispatch
alias :key? :has_key?
alias :include? :has_key?
+ def keys
+ @delegate.keys
+ end
+
+ def values
+ @delegate.values
+ end
+
def []=(key, value)
load_for_write!
@delegate[key.to_s] = value
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 67a208263b..e43e897783 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1318,7 +1318,7 @@ module ActionDispatch
def draw(name)
path = @draw_paths.find do |_path|
- _path.join("#{name}.rb").file?
+ File.exists? "#{_path}/#{name}.rb"
end
unless path
@@ -1328,8 +1328,8 @@ module ActionDispatch
raise ArgumentError, msg
end
- route_path = path.join("#{name}.rb")
- instance_eval(route_path.read, route_path.to_s)
+ route_path = "#{path}/#{name}.rb"
+ instance_eval(File.read(route_path), route_path.to_s)
end
# match 'path' => 'controller#action'
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 7872f4007e..64b1d58ae9 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -252,15 +252,11 @@ module ActionDispatch
self.draw_paths = []
self.request_class = request_class
- @valid_conditions = {}
-
+ @valid_conditions = { :controller => true, :action => true }
request_class.public_instance_methods.each { |m|
- @valid_conditions[m.to_sym] = true
+ @valid_conditions[m] = true
}
- @valid_conditions[:controller] = true
- @valid_conditions[:action] = true
-
- self.valid_conditions.delete(:id)
+ @valid_conditions.delete(:id)
@append = []
@prepend = []
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 08fd28d72d..3fdc6688c2 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -17,8 +17,8 @@ module ActionDispatch
# a Hash, or a String that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or
# <tt>multipart/form-data</tt>).
- # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
- # automatically be upcased, with the prefix 'HTTP_' added if needed.
+ # - +headers+: Additional headers to pass, as a Hash. The headers will be
+ # merged into the Rack env hash.
#
# This method returns an Response object, which one can use to
# inspect the details of the response. Furthermore, if this method was
@@ -73,8 +73,7 @@ module ActionDispatch
#
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
- # string; the headers are a hash. Keys are automatically upcased and
- # prefixed with 'HTTP_' if not already.
+ # string; the headers are a hash.
def xml_http_request(request_method, path, parameters = nil, headers = nil)
headers ||= {}
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 6510610034..ac150882b1 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -342,7 +342,7 @@ module ActionView
# Example:
#
# <%= form_for(@post) do |f| %>
- # <% f.fields_for(:comments, :include_id => false) do |cf| %>
+ # <%= f.fields_for(:comments, :include_id => false) do |cf| %>
# ...
# <% end %>
# <% end %>
@@ -939,6 +939,15 @@ module ActionView
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
end
+ # Returns a text_field of type "color".
+ #
+ # color_field("car", "color")
+ # # => <input id="car_color" name="car[color]" type="color" value="#000000" />
+ #
+ def color_field(object_name, method, options = {})
+ Tags::ColorField.new(object_name, method, self, options).render
+ end
+
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
# some browsers.
@@ -1007,6 +1016,74 @@ module ActionView
Tags::TimeField.new(object_name, method, self, options).render
end
+ # Returns a text_field of type "datetime".
+ #
+ # datetime_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime" />
+ #
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
+ # on the object's value, which makes it behave as expected for instances
+ # of DateTime and ActiveSupport::TimeWithZone.
+ #
+ # @user.born_on = Date.new(1984, 1, 12)
+ # datetime_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
+ #
+ def datetime_field(object_name, method, options = {})
+ Tags::DatetimeField.new(object_name, method, self, options).render
+ end
+
+ # Returns a text_field of type "datetime-local".
+ #
+ # datetime_local_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
+ #
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
+ # on the object's value, which makes it behave as expected for instances
+ # of DateTime and ActiveSupport::TimeWithZone.
+ #
+ # @user.born_on = Date.new(1984, 1, 12)
+ # datetime_local_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
+ #
+ def datetime_local_field(object_name, method, options = {})
+ Tags::DatetimeLocalField.new(object_name, method, self, options).render
+ end
+
+ # Returns a text_field of type "month".
+ #
+ # month_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="month" />
+ #
+ # The default value is generated by trying to call +strftime+ with "%Y-%m"
+ # on the object's value, which makes it behave as expected for instances
+ # of DateTime and ActiveSupport::TimeWithZone.
+ #
+ # @user.born_on = Date.new(1984, 1, 27)
+ # month_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
+ #
+ def month_field(object_name, method, options = {})
+ Tags::MonthField.new(object_name, method, self, options).render
+ end
+
+ # Returns a text_field of type "week".
+ #
+ # week_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="week" />
+ #
+ # The default value is generated by trying to call +strftime+ with "%Y-W%W"
+ # on the object's value, which makes it behave as expected for instances
+ # of DateTime and ActiveSupport::TimeWithZone.
+ #
+ # @user.born_on = Date.new(1984, 5, 12)
+ # week_field("user", "born_on")
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />
+ #
+ def week_field(object_name, method, options = {})
+ Tags::WeekField.new(object_name, method, self, options).render
+ end
+
# Returns a text_field of type "url".
#
# url_field("user", "homepage")
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index e65b4e3e95..1a0019a48c 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -524,6 +524,14 @@ module ActionView
output.safe_concat("</fieldset>")
end
+ # Creates a text field of type "color".
+ #
+ # ==== Options
+ # * Accepts the same options as text_field_tag.
+ def color_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
+ end
+
# Creates a text field of type "search".
#
# ==== Options
@@ -560,6 +568,50 @@ module ActionView
text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
end
+ # Creates a text field of type "datetime".
+ #
+ # === Options
+ # * <tt>:min</tt> - The minimum acceptable value.
+ # * <tt>:max</tt> - The maximum acceptable value.
+ # * <tt>:step</tt> - The acceptable value granularity.
+ # * Otherwise accepts the same options as text_field_tag.
+ def datetime_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
+ end
+
+ # Creates a text field of type "datetime-local".
+ #
+ # === Options
+ # * <tt>:min</tt> - The minimum acceptable value.
+ # * <tt>:max</tt> - The maximum acceptable value.
+ # * <tt>:step</tt> - The acceptable value granularity.
+ # * Otherwise accepts the same options as text_field_tag.
+ def datetime_local_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
+ end
+
+ # Creates a text field of type "month".
+ #
+ # === Options
+ # * <tt>:min</tt> - The minimum acceptable value.
+ # * <tt>:max</tt> - The maximum acceptable value.
+ # * <tt>:step</tt> - The acceptable value granularity.
+ # * Otherwise accepts the same options as text_field_tag.
+ def month_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
+ end
+
+ # Creates a text field of type "week".
+ #
+ # === Options
+ # * <tt>:min</tt> - The minimum acceptable value.
+ # * <tt>:max</tt> - The maximum acceptable value.
+ # * <tt>:step</tt> - The acceptable value granularity.
+ # * Otherwise accepts the same options as text_field_tag.
+ def week_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
+ end
+
# Creates a text field of type "url".
#
# ==== Options
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index dfc26acfad..8f97d1f014 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -1,8 +1,8 @@
# encoding: utf-8
-require 'active_support/core_ext/big_decimal/conversions'
-require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/number_helper'
module ActionView
# = Action View Number Helpers
@@ -16,9 +16,6 @@ module ActionView
# unchanged if can't be converted into a valid number.
module NumberHelper
- DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
- :precision => 2, :significant => false, :strip_insignificant_zeros => false }
-
# Raised when argument +number+ param given to the helpers is invalid and
# the option :raise is set to +true+.
class InvalidNumberError < StandardError
@@ -63,25 +60,7 @@ module ActionView
options = options.symbolize_keys
parse_float(number, true) if options[:raise]
-
- number = number.to_s.strip
- area_code = options[:area_code]
- delimiter = options[:delimiter] || "-"
- extension = options[:extension]
- country_code = options[:country_code]
-
- if area_code
- number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
- else
- number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
- number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
- end
-
- str = ''
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
- str << number
- str << " x #{extension}" unless extension.blank?
- ERB::Util.html_escape(str)
+ ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
end
# Formats a +number+ into a currency string (e.g., $13.65). You
@@ -128,34 +107,9 @@ module ActionView
# # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
return unless number
- options = options.symbolize_keys
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- currency = translations_for('currency', options[:locale])
- currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
-
- defaults = DEFAULT_CURRENCY_VALUES.merge(defaults_translations(options[:locale])).merge!(currency)
- defaults[:negative_format] = "-" + options[:format] if options[:format]
- options = defaults.merge!(options)
-
- unit = options.delete(:unit)
- format = options.delete(:format)
-
- if number.to_f < 0
- format = options.delete(:negative_format)
- number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
- end
-
- begin
- value = number_with_precision(number, options.merge(:raise => true))
- format.gsub('%n', value).gsub('%u', unit).html_safe
- rescue InvalidNumberError => e
- if options[:raise]
- raise
- else
- formatted_number = format.gsub('%n', e.number).gsub('%u', unit)
- e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
- end
- end
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_currency(number, options) }
end
# Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -196,24 +150,9 @@ module ActionView
# number_to_percentage("98a", :raise => true) # => InvalidNumberError
def number_to_percentage(number, options = {})
return unless number
- options = options.symbolize_keys
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- defaults = format_translations('percentage', options[:locale])
- options = defaults.merge!(options)
-
- format = options[:format] || "%n%"
-
- begin
- value = number_with_precision(number, options.merge(:raise => true))
- format.gsub(/%n/, value).html_safe
- rescue InvalidNumberError => e
- if options[:raise]
- raise
- else
- formatted_number = format.gsub(/%n/, e.number)
- e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
- end
- end
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_percentage(number, options) }
end
# Formats a +number+ with grouped thousands using +delimiter+
@@ -246,15 +185,9 @@ module ActionView
#
# number_with_delimiter("112a", :raise => true) # => raise InvalidNumberError
def number_with_delimiter(number, options = {})
- options = options.symbolize_keys
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- parse_float(number, options[:raise]) or return number
-
- options = defaults_translations(options[:locale]).merge(options)
-
- parts = number.to_s.to_str.split('.')
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
- safe_join(parts, options[:separator])
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_delimited(number, options) }
end
# Formats a +number+ with the specified level of
@@ -299,41 +232,11 @@ module ActionView
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
# # => 1.111,23
def number_with_precision(number, options = {})
- options = options.symbolize_keys
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- number = (parse_float(number, options[:raise]) or return number)
-
- defaults = format_translations('precision', options[:locale])
- options = defaults.merge!(options)
-
- precision = options.delete :precision
- significant = options.delete :significant
- strip_insignificant_zeros = options.delete :strip_insignificant_zeros
-
- if significant and precision > 0
- if number == 0
- digits, rounded_number = 1, 0
- else
- digits = (Math.log10(number.abs) + 1).floor
- rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
- digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
- end
- precision -= digits
- precision = precision > 0 ? precision : 0 #don't let it be negative
- else
- rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
- rounded_number = rounded_number.zero? ? rounded_number.abs : rounded_number #prevent showing negative zeros
- end
- formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
- if strip_insignificant_zeros
- escaped_separator = Regexp.escape(options[:separator])
- formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '').html_safe
- else
- formatted_number
- end
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_rounded(number, options) }
end
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
# Formats the bytes in +number+ into a more understandable
# representation (e.g., giving it 1500 yields 1.5 KB). This
@@ -383,40 +286,11 @@ module ActionView
# number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
# number_to_human_size(524288000, :precision => 5) # => "500 MB"
def number_to_human_size(number, options = {})
- options = options.symbolize_keys
-
- number = (parse_float(number, options[:raise]) or return number)
-
- defaults = format_translations('human', options[:locale])
- options = defaults.merge!(options)
-
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
-
- storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- base = options[:prefix] == :si ? 1000 : 1024
-
- if number.to_i < base
- unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
- storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe
- else
- max_exp = STORAGE_UNITS.size - 1
- exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
- exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
- number /= base ** exponent
-
- unit_key = STORAGE_UNITS[exponent]
- unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
-
- formatted_number = number_with_precision(number, options)
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).html_safe
- end
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_human_size(number, options) }
end
- DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
- -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
-
# Pretty prints (formats and approximates) a number in a way it
# is more readable by humans (eg.: 1200000000 becomes "1.2
# Billion"). This is useful for numbers that can get very large
@@ -516,60 +390,34 @@ module ActionView
# number_to_human(0.34, :units => :distance) # => "34 centimeters"
#
def number_to_human(number, options = {})
- options = options.symbolize_keys
+ options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- number = (parse_float(number, options[:raise]) or return number)
+ wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_human(number, options) }
+ end
- defaults = format_translations('human', options[:locale])
- options = defaults.merge!(options)
+ private
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
+ def escape_unsafe_delimiters_and_separators(options)
+ options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe?
+ options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe?
+ options
+ end
- inverted_du = DECIMAL_UNITS.invert
+ def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
+ valid_float = valid_float?(number)
+ raise InvalidNumberError, number if raise_on_invalid && !valid_float
- units = options.delete :units
- unit_exponents = case units
- when Hash
- units
- when String, Symbol
- I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
- when nil
- I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
- else
- raise ArgumentError, ":units must be a Hash or String translation scope."
- end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
+ formatted_number = yield
- number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
- display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
- number /= 10 ** display_exponent
-
- unit = case units
- when Hash
- units[DECIMAL_UNITS[display_exponent]]
- when String, Symbol
- I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
+ if valid_float || number.html_safe?
+ formatted_number.html_safe
else
- I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
+ formatted_number
end
-
- decimal_format = options[:format] || I18n.translate(:'number.human.decimal_units.format', :locale => options[:locale], :default => "%n %u")
- formatted_number = number_with_precision(number, options)
- decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip.html_safe
- end
-
- private
-
- def format_translations(namespace, locale)
- defaults_translations(locale).merge(translations_for(namespace, locale))
- end
-
- def defaults_translations(locale)
- I18n.translate(:'number.format', :locale => locale, :default => {})
end
- def translations_for(namespace, locale)
- I18n.translate(:"number.#{namespace}.format", :locale => locale, :default => {})
+ def valid_float?(number)
+ !parse_float(number, false).nil?
end
def parse_float(number, raise_error)
diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb
index 5cd77c8ec3..a05e16979a 100644
--- a/actionpack/lib/action_view/helpers/tags.rb
+++ b/actionpack/lib/action_view/helpers/tags.rb
@@ -8,14 +8,18 @@ module ActionView
autoload :CollectionCheckBoxes
autoload :CollectionRadioButtons
autoload :CollectionSelect
+ autoload :ColorField
autoload :DateField
autoload :DateSelect
+ autoload :DatetimeField
+ autoload :DatetimeLocalField
autoload :DatetimeSelect
autoload :EmailField
autoload :FileField
autoload :GroupedCollectionSelect
autoload :HiddenField
autoload :Label
+ autoload :MonthField
autoload :NumberField
autoload :PasswordField
autoload :RadioButton
@@ -29,6 +33,7 @@ module ActionView
autoload :TimeSelect
autoload :TimeZoneSelect
autoload :UrlField
+ autoload :WeekField
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/color_field.rb b/actionpack/lib/action_view/helpers/tags/color_field.rb
new file mode 100644
index 0000000000..6f08f8483a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/color_field.rb
@@ -0,0 +1,25 @@
+module ActionView
+ module Helpers
+ module Tags
+ class ColorField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options["value"] = @options.fetch("value") { validate_color_string(value(object)) }
+ @options = options
+ super
+ end
+
+ private
+
+ def validate_color_string(string)
+ regex = /#[0-9a-fA-F]{6}/
+ if regex.match(string)
+ string.downcase
+ else
+ "#000000"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/date_field.rb b/actionpack/lib/action_view/helpers/tags/date_field.rb
index 0e79609d52..64c29dea3d 100644
--- a/actionpack/lib/action_view/helpers/tags/date_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/date_field.rb
@@ -1,13 +1,12 @@
module ActionView
module Helpers
module Tags
- class DateField < TextField #:nodoc:
- def render
- options = @options.stringify_keys
- options["value"] = @options.fetch("value") { value(object).try(:to_date) }
- @options = options
- super
- end
+ class DateField < DatetimeField #:nodoc:
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%Y-%m-%d")
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_field.rb
new file mode 100644
index 0000000000..e407146e96
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/datetime_field.rb
@@ -0,0 +1,22 @@
+module ActionView
+ module Helpers
+ module Tags
+ class DatetimeField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options["value"] = @options.fetch("value") { format_date(value(object)) }
+ options["min"] = format_date(options["min"])
+ options["max"] = format_date(options["max"])
+ @options = options
+ super
+ end
+
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%Y-%m-%dT%T.%L%z")
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb
new file mode 100644
index 0000000000..6668d6d718
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/datetime_local_field.rb
@@ -0,0 +1,19 @@
+module ActionView
+ module Helpers
+ module Tags
+ class DatetimeLocalField < DatetimeField #:nodoc:
+ class << self
+ def field_type
+ @field_type ||= "datetime-local"
+ end
+ end
+
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%Y-%m-%dT%T")
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/month_field.rb b/actionpack/lib/action_view/helpers/tags/month_field.rb
new file mode 100644
index 0000000000..3d3c32d847
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/month_field.rb
@@ -0,0 +1,13 @@
+module ActionView
+ module Helpers
+ module Tags
+ class MonthField < DatetimeField #:nodoc:
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%Y-%m")
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/tags/time_field.rb b/actionpack/lib/action_view/helpers/tags/time_field.rb
index 271dc00c54..a3941860c9 100644
--- a/actionpack/lib/action_view/helpers/tags/time_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/time_field.rb
@@ -1,13 +1,12 @@
module ActionView
module Helpers
module Tags
- class TimeField < TextField #:nodoc:
- def render
- options = @options.stringify_keys
- options["value"] = @options.fetch("value") { value(object).try(:strftime, "%T.%L") }
- @options = options
- super
- end
+ class TimeField < DatetimeField #:nodoc:
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%T.%L")
+ end
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
new file mode 100644
index 0000000000..1e13939a0a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/week_field.rb
@@ -0,0 +1,13 @@
+module ActionView
+ module Helpers
+ module Tags
+ class WeekField < DatetimeField #:nodoc:
+ private
+
+ def format_date(value)
+ value.try(:strftime, "%Y-W%W")
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 8cd7cf0052..0cc0d069ea 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -62,9 +62,11 @@ module ActionView
#
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
#
- # The result is not marked as HTML-safe, so will be subject to the default escaping when
- # used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags
- # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags).
+ # Pass a block if you want to show extra content when the text is truncated.
+ #
+ # The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
+ # +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
+ # may produce invalid HTML (such as unbalanced or incomplete tags).
#
# truncate("Once upon a time in a world far far away")
# # => "Once upon a time in a world..."
@@ -80,8 +82,18 @@ module ActionView
#
# truncate("<p>Once upon a time in a world far far away</p>")
# # => "<p>Once upon a time in a wo..."
- def truncate(text, options = {})
- text.truncate(options.fetch(:length, 30), options) if text
+ #
+ # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
+ # # => "Once upon a time in a wo...<a href="#">Continue</a>"
+ def truncate(text, options = {}, &block)
+ if text
+ length = options.fetch(:length, 30)
+
+ content = text.truncate(length, options)
+ content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
+ content << capture(&block) if block_given? && text.length > length
+ content
+ end
end
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
@@ -102,7 +114,7 @@ module ActionView
# # => You searched for: <a href="search?q=rails">rails</a>
def highlight(text, phrases, options = {})
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
-
+
text = sanitize(text) if options.fetch(:sanitize, true)
if text.blank? || phrases.blank?
text
@@ -165,12 +177,12 @@ module ActionView
# pluralize(0, 'person')
# # => 0 people
def pluralize(count, singular, plural = nil)
- word = if (count == 1 || count =~ /^1(\.0+)?$/)
- singular
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
+ singular
else
plural || singular.pluralize
end
-
+
"#{count || 0} #{word}"
end
@@ -215,7 +227,7 @@ module ActionView
#
# simple_format(my_text)
# # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
- #
+ #
# simple_format(my_text, {}, :wrapper_tag => "div")
# # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
#
@@ -231,7 +243,7 @@ module ActionView
# # => "<p><span>I'm allowed!</span> It's true.</p>"
def simple_format(text, html_options = {}, options = {})
wrapper_tag = options.fetch(:wrapper_tag, :p)
-
+
text = sanitize(text) if options.fetch(:sanitize, true)
paragraphs = split_paragraphs(text)
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 8171bea8ed..552c9ba660 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -64,7 +64,7 @@ module ActionView
# Delegates to <tt>I18n.localize</tt> with no additional functionality.
#
- # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
+ # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
# for more information.
def localize(*args)
I18n.localize(*args)
@@ -96,7 +96,7 @@ module ActionView
new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
break
else
- new_defautls << key
+ new_defaults << key
end
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 7e69547dab..7f5b3c8a0f 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -233,25 +233,15 @@ module ActionView
#
# link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
- def link_to(*args, &block)
- if block_given?
- options = args.first || {}
- html_options = args.second
- link_to(capture(&block), options, html_options)
- else
- name = args[0]
- options = args[1] || {}
- html_options = args[2]
-
- html_options = convert_options_to_data_attributes(options, html_options)
- url = url_for(options)
+ def link_to(name = nil, options = nil, html_options = nil, &block)
+ html_options, options = options, name if block_given?
+ options ||= {}
+ url = url_for(options)
- href = html_options['href']
- tag_options = tag_options(html_options)
+ html_options = convert_options_to_data_attributes(options, html_options)
+ html_options['href'] ||= url
- href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
- "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe
- end
+ content_tag(:a, name || url, html_options, &block)
end
# Generates a form containing a single button that submits to the URL created
@@ -294,6 +284,16 @@ module ActionView
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
+ # <%= button_to [:make_happy, @user] do %>
+ # Make happy <strong><%= @user.name %></strong>
+ # <% end %>
+ # # => "<form method="post" action="/users/1/make_happy" class="button_to">
+ # # <div>
+ # # <button type="submit">
+ # # Make happy <strong><%= @user.name %></strong>
+ # # </button>
+ # # </div>
+ # # </form>"
#
# <%= button_to "New", :action => "new", :form_class => "new-thing" %>
# # => "<form method="post" action="/controller/new" class="new-thing">
@@ -331,7 +331,11 @@ module ActionView
# # </div>
# # </form>"
# #
- def button_to(name, options = {}, html_options = {})
+ def button_to(name = nil, options = nil, html_options = nil, &block)
+ html_options, options = options, name if block_given?
+ options ||= {}
+ html_options ||= {}
+
html_options = html_options.stringify_keys
convert_boolean_attributes!(html_options, %w(disabled))
@@ -350,9 +354,16 @@ module ActionView
request_token_tag = form_method == 'post' ? token_tag : ''
html_options = convert_options_to_data_attributes(options, html_options)
- html_options.merge!("type" => "submit", "value" => name || url)
+ html_options['type'] = 'submit'
+
+ button = if block_given?
+ content_tag('button', html_options, &block)
+ else
+ html_options['value'] = name || url
+ tag('input', html_options)
+ end
- inner_tags = method_tag.safe_concat tag('input', html_options).safe_concat request_token_tag
+ inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
content_tag('form', content_tag('div', inner_tags), form_options)
end
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index 8e9db634fb..8a56f147b8 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -1,102 +1,4 @@
"en":
- number:
- # Used in number_with_delimiter()
- # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
- format:
- # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
- separator: "."
- # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
- delimiter: ","
- # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
- precision: 3
- # If set to true, precision will mean the number of significant digits instead
- # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
- significant: false
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
- strip_insignificant_zeros: false
-
- # Used in number_to_currency()
- currency:
- format:
- # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
- format: "%u%n"
- unit: "$"
- # These five are to override number.format and are optional
- separator: "."
- delimiter: ","
- precision: 2
- significant: false
- strip_insignificant_zeros: false
-
- # Used in number_to_percentage()
- percentage:
- format:
- # These five are to override number.format and are optional
- # separator:
- delimiter: ""
- # precision:
- # significant: false
- # strip_insignificant_zeros: false
- format: "%n%"
-
- # Used in number_to_precision()
- precision:
- format:
- # These five are to override number.format and are optional
- # separator:
- delimiter: ""
- # precision:
- # significant: false
- # strip_insignificant_zeros: false
-
- # Used in number_to_human_size() and number_to_human()
- human:
- format:
- # These five are to override number.format and are optional
- # separator:
- delimiter: ""
- precision: 3
- significant: true
- strip_insignificant_zeros: true
- # Used in number_to_human_size()
- storage_units:
- # Storage units output formatting.
- # %u is the storage unit, %n is the number (default: 2 MB)
- format: "%n %u"
- units:
- byte:
- one: "Byte"
- other: "Bytes"
- kb: "KB"
- mb: "MB"
- gb: "GB"
- tb: "TB"
- # Used in number_to_human()
- decimal_units:
- format: "%n %u"
- # Decimal units output formatting
- # By default we will only quantify some of the exponents
- # but the commented ones might be defined or overridden
- # by the user.
- units:
- # femto: Quadrillionth
- # pico: Trillionth
- # nano: Billionth
- # micro: Millionth
- # mili: Thousandth
- # centi: Hundredth
- # deci: Tenth
- unit: ""
- # ten:
- # one: Ten
- # other: Tens
- # hundred: Hundred
- thousand: Thousand
- million: Million
- billion: Billion
- trillion: Trillion
- quadrillion: Quadrillion
-
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index ae923de24e..82892593f8 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -35,7 +35,7 @@ module ActionView
end
end
- # Renders the given template. An string representing the layout can be
+ # Renders the given template. A string representing the layout can be
# supplied as well.
def render_template(template, layout_name = nil, locals = {}) #:nodoc:
view, locals = @view, locals || {}
diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb
index 5b423c8151..6758668b7a 100644
--- a/actionpack/test/controller/force_ssl_test.rb
+++ b/actionpack/test/controller/force_ssl_test.rb
@@ -49,6 +49,15 @@ class ForceSSLFlash < ForceSSLController
end
end
+class RedirectToSSL < ForceSSLController
+ def banana
+ force_ssl_redirect || render(:text => 'monkey')
+ end
+ def cheeseburger
+ force_ssl_redirect('secure.cheeseburger.host') || render(:text => 'ihaz')
+ end
+end
+
class ForceSSLControllerLevelTest < ActionController::TestCase
tests ForceSSLControllerLevel
@@ -149,3 +158,25 @@ class ForceSSLFlashTest < ActionController::TestCase
assert_equal "hello", assigns["flashy"]
end
end
+
+class RedirectToSSLTest < ActionController::TestCase
+ tests RedirectToSSL
+ def test_banana_redirects_to_https_if_not_https
+ get :banana
+ assert_response 301
+ assert_equal "https://test.host/redirect_to_ssl/banana", redirect_to_url
+ end
+
+ def test_cheeseburgers_redirects_to_https_with_new_host_if_not_https
+ get :cheeseburger
+ assert_response 301
+ assert_equal "https://secure.cheeseburger.host/redirect_to_ssl/cheeseburger", redirect_to_url
+ end
+
+ def test_banana_does_not_redirect_if_already_https
+ request.env['HTTPS'] = 'on'
+ get :cheeseburger
+ assert_response 200
+ assert_equal 'ihaz', response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index 757661d8d0..deb234b04f 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -46,12 +46,42 @@ end
class MeTooController < JustMeController
end
+class HelpersPathsController < ActionController::Base
+ paths = ["helpers2_pack", "helpers1_pack"].map do |path|
+ File.join(File.expand_path('../../fixtures', __FILE__), path)
+ end
+ $:.unshift(*paths)
+
+ self.helpers_path = paths
+ helper :all
+
+ def index
+ render :inline => "<%= conflicting_helper %>"
+ end
+end
+
module LocalAbcHelper
def a() end
def b() end
def c() end
end
+class HelperPathsTest < ActiveSupport::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_helpers_paths_priority
+ request = ActionController::TestRequest.new
+ responses = HelpersPathsController.action(:index).call(request.env)
+
+ # helpers1_pack was given as a second path, so pack1_helper should be
+ # included as the second one
+ assert_equal "pack1", responses.last.body
+ end
+end
+
class HelperTest < ActiveSupport::TestCase
class TestController < ActionController::Base
attr_accessor :delegate_attr
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 066cd523be..0289f4070b 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -9,7 +9,7 @@ module RequestForgeryProtectionActions
end
def show_button
- render :inline => "<%= button_to('New', '/') {} %>"
+ render :inline => "<%= button_to('New', '/') %>"
end
def external_form
@@ -79,7 +79,7 @@ class FreeCookieController < RequestForgeryProtectionController
end
def show_button
- render :inline => "<%= button_to('New', '/') {} %>"
+ render :inline => "<%= button_to('New', '/') %>"
end
end
diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb
index 4d24456ba6..80d5a13171 100644
--- a/actionpack/test/dispatch/request/session_test.rb
+++ b/actionpack/test/dispatch/request/session_test.rb
@@ -36,6 +36,22 @@ module ActionDispatch
assert_equal s, Session.find(env)
end
+ def test_keys
+ env = {}
+ s = Session.create(store, env, {})
+ s['rails'] = 'ftw'
+ s['adequate'] = 'awesome'
+ assert_equal %w[rails adequate], s.keys
+ end
+
+ def test_values
+ env = {}
+ s = Session.create(store, env, {})
+ s['rails'] = 'ftw'
+ s['adequate'] = 'awesome'
+ assert_equal %w[ftw awesome], s.values
+ end
+
private
def store
Class.new {
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 00d09282ca..fa4cb301eb 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -2331,7 +2331,7 @@ class TestDrawExternalFile < ActionDispatch::IntegrationTest
end
end
- DRAW_PATH = Pathname.new(File.expand_path('../../fixtures/routes', __FILE__))
+ DRAW_PATH = File.expand_path('../../fixtures/routes', __FILE__)
DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw_paths << DRAW_PATH
diff --git a/actionpack/test/fixtures/helpers1_pack/pack1_helper.rb b/actionpack/test/fixtures/helpers1_pack/pack1_helper.rb
new file mode 100644
index 0000000000..9faa427736
--- /dev/null
+++ b/actionpack/test/fixtures/helpers1_pack/pack1_helper.rb
@@ -0,0 +1,5 @@
+module Pack1Helper
+ def conflicting_helper
+ "pack1"
+ end
+end
diff --git a/actionpack/test/fixtures/helpers2_pack/pack2_helper.rb b/actionpack/test/fixtures/helpers2_pack/pack2_helper.rb
new file mode 100644
index 0000000000..cf56697dfb
--- /dev/null
+++ b/actionpack/test/fixtures/helpers2_pack/pack2_helper.rb
@@ -0,0 +1,5 @@
+module Pack2Helper
+ def conflicting_helper
+ "pack2"
+ end
+end
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index bbb4cc5ef3..82f38b5309 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -214,3 +214,6 @@ class RenderJsonTestException < Exception
return { :error => self.class.name, :message => self.to_s }.to_json
end
end
+
+class Car < Struct.new(:color)
+end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 27cc3ad48a..c9b39ed18f 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -83,6 +83,8 @@ class FormHelperTest < ActionView::TestCase
@post.tags << Tag.new
@blog_post = Blog::Post.new("And his name will be forty and four.", 44)
+
+ @car = Car.new("#000FFF")
end
Routes = ActionDispatch::Routing::RouteSet.new
@@ -610,6 +612,17 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_color_field_with_valid_hex_color_string
+ expected = %{<input id="car_color" name="car[color]" type="color" value="#000fff" />}
+ assert_dom_equal(expected, color_field("car", "color"))
+ end
+
+ def test_color_field_with_invalid_hex_color_string
+ expected = %{<input id="car_color" name="car[color]" type="color" value="#000000" />}
+ @car.color = "#1234TR"
+ assert_dom_equal(expected, color_field("car", "color"))
+ end
+
def test_search_field
expected = %{<input id="contact_notes_query" name="contact[notes_query]" type="search" />}
assert_dom_equal(expected, search_field("contact", "notes_query"))
@@ -631,6 +644,15 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal(expected, date_field("post", "written_on"))
end
+ def test_date_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="2" max="2010-08-15" min="2000-06-15" name="post[written_on]" type="date" value="2004-06-15" />}
+ @post.written_on = DateTime.new(2004, 6, 15)
+ min_value = DateTime.new(2000, 6, 15)
+ max_value = DateTime.new(2010, 8, 15)
+ step = 2
+ assert_dom_equal(expected, date_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
def test_date_field_with_timewithzone_value
previous_time_zone, Time.zone = Time.zone, 'UTC'
expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2004-06-15" />}
@@ -657,6 +679,15 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal(expected, time_field("post", "written_on"))
end
+ def test_time_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="60" max="10:25:00.000" min="20:45:30.000" name="post[written_on]" type="time" value="01:02:03.000" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
+ max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
+ step = 60
+ assert_dom_equal(expected, time_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
def test_time_field_with_timewithzone_value
previous_time_zone, Time.zone = Time.zone, 'UTC'
expected = %{<input id="post_written_on" name="post[written_on]" type="time" value="01:02:03.000" />}
@@ -672,6 +703,146 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal(expected, time_field("post", "written_on"))
end
+ def test_datetime_field
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T00:00:00.000+0000" />}
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
+
+ def test_datetime_field_with_datetime_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
+
+ def test_datetime_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="60" max="2010-08-15T10:25:00.000+0000" min="2000-06-15T20:45:30.000+0000" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
+ max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
+ step = 60
+ assert_dom_equal(expected, datetime_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
+ def test_datetime_field_with_timewithzone_value
+ previous_time_zone, Time.zone = Time.zone, 'UTC'
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T15:30:45.000+0000" />}
+ @post.written_on = Time.zone.parse('2004-06-15 15:30:45')
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ ensure
+ Time.zone = previous_time_zone
+ end
+
+ def test_datetime_field_with_nil_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" />}
+ @post.written_on = nil
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
+
+ def test_datetime_local_field
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T00:00:00" />}
+ assert_dom_equal(expected, datetime_local_field("post", "written_on"))
+ end
+
+ def test_datetime_local_field_with_datetime_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ assert_dom_equal(expected, datetime_local_field("post", "written_on"))
+ end
+
+ def test_datetime_local_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="60" max="2010-08-15T10:25:00" min="2000-06-15T20:45:30" name="post[written_on]" type="datetime-local" value="2004-06-15T01:02:03" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
+ max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
+ step = 60
+ assert_dom_equal(expected, datetime_local_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
+ def test_datetime_local_field_with_timewithzone_value
+ previous_time_zone, Time.zone = Time.zone, 'UTC'
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2004-06-15T15:30:45" />}
+ @post.written_on = Time.zone.parse('2004-06-15 15:30:45')
+ assert_dom_equal(expected, datetime_local_field("post", "written_on"))
+ ensure
+ Time.zone = previous_time_zone
+ end
+
+ def test_datetime_local_field_with_nil_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" />}
+ @post.written_on = nil
+ assert_dom_equal(expected, datetime_local_field("post", "written_on"))
+ end
+
+ def test_month_field
+ expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />}
+ assert_dom_equal(expected, month_field("post", "written_on"))
+ end
+
+ def test_month_field_with_nil_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="month" />}
+ @post.written_on = nil
+ assert_dom_equal(expected, month_field("post", "written_on"))
+ end
+
+ def test_month_field_with_datetime_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ assert_dom_equal(expected, month_field("post", "written_on"))
+ end
+
+ def test_month_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="2" max="2010-12" min="2000-02" name="post[written_on]" type="month" value="2004-06" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ min_value = DateTime.new(2000, 2, 13)
+ max_value = DateTime.new(2010, 12, 23)
+ step = 2
+ assert_dom_equal(expected, month_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
+ def test_month_field_with_timewithzone_value
+ previous_time_zone, Time.zone = Time.zone, 'UTC'
+ expected = %{<input id="post_written_on" name="post[written_on]" type="month" value="2004-06" />}
+ @post.written_on = Time.zone.parse('2004-06-15 15:30:45')
+ assert_dom_equal(expected, month_field("post", "written_on"))
+ ensure
+ Time.zone = previous_time_zone
+ end
+
+ def test_week_field
+ expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />}
+ assert_dom_equal(expected, week_field("post", "written_on"))
+ end
+
+ def test_week_field_with_nil_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="week" />}
+ @post.written_on = nil
+ assert_dom_equal(expected, week_field("post", "written_on"))
+ end
+
+ def test_week_field_with_datetime_value
+ expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ assert_dom_equal(expected, week_field("post", "written_on"))
+ end
+
+ def test_week_field_with_extra_attrs
+ expected = %{<input id="post_written_on" step="2" max="2010-W51" min="2000-W06" name="post[written_on]" type="week" value="2004-W24" />}
+ @post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
+ min_value = DateTime.new(2000, 2, 13)
+ max_value = DateTime.new(2010, 12, 23)
+ step = 2
+ assert_dom_equal(expected, week_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ end
+
+ def test_week_field_with_timewithzone_value
+ previous_time_zone, Time.zone = Time.zone, 'UTC'
+ expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2004-W24" />}
+ @post.written_on = Time.zone.parse('2004-06-15 15:30:45')
+ assert_dom_equal(expected, week_field("post", "written_on"))
+ ensure
+ Time.zone = previous_time_zone
+ end
+
def test_url_field
expected = %{<input id="user_homepage" name="user[homepage]" type="url" />}
assert_dom_equal(expected, url_field("user", "homepage"))
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 6574e13558..5d19e3274d 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -444,6 +444,11 @@ class FormTagHelperTest < ActionView::TestCase
)
end
+ def test_color_field_tag
+ expected = %{<input id="car" name="car" type="color" />}
+ assert_dom_equal(expected, color_field_tag("car"))
+ end
+
def test_search_field_tag
expected = %{<input id="query" name="query" type="search" />}
assert_dom_equal(expected, search_field_tag("query"))
@@ -464,6 +469,26 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal(expected, time_field_tag("cell"))
end
+ def test_datetime_field_tag
+ expected = %{<input id="appointment" name="appointment" type="datetime" />}
+ assert_dom_equal(expected, datetime_field_tag("appointment"))
+ end
+
+ def test_datetime_local_field_tag
+ expected = %{<input id="appointment" name="appointment" type="datetime-local" />}
+ assert_dom_equal(expected, datetime_local_field_tag("appointment"))
+ end
+
+ def test_month_field_tag
+ expected = %{<input id="birthday" name="birthday" type="month" />}
+ assert_dom_equal(expected, month_field_tag("birthday"))
+ end
+
+ def test_week_field_tag
+ expected = %{<input id="birthday" name="birthday" type="week" />}
+ assert_dom_equal(expected, week_field_tag("birthday"))
+ end
+
def test_url_field_tag
expected = %{<input id="homepage" name="homepage" type="url" />}
assert_dom_equal(expected, url_field_tag("homepage"))
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 14ca6d9879..057cb47f53 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -33,6 +33,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("+18005551212", number_to_phone(8005551212, :country_code => 1, :delimiter => ''))
assert_equal("22-555-1212", number_to_phone(225551212))
assert_equal("+45-22-555-1212", number_to_phone(225551212, :country_code => 45))
+ assert_equal '111&lt;script&gt;&lt;/script&gt;111&lt;script&gt;&lt;/script&gt;1111', number_to_phone(1111111111, :delimiter => "<script></script>")
end
def test_number_to_currency
@@ -47,6 +48,8 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50"))
assert_equal("1,234,567,890.50 K&#269;", number_to_currency("1234567890.50", {:unit => "K&#269;", :format => "%n %u"}))
assert_equal("1,234,567,890.50 - K&#269;", number_to_currency("-1234567890.50", {:unit => "K&#269;", :format => "%n %u", :negative_format => "%n - %u"}))
+ assert_equal '$1&lt;script&gt;&lt;/script&gt;01', number_to_currency(1.01, :separator => "<script></script>")
+ assert_equal '$1&lt;script&gt;&lt;/script&gt;000.00', number_to_currency(1000, :delimiter => "<script></script>")
end
def test_number_to_percentage
@@ -58,6 +61,8 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("123.4%", number_to_percentage(123.400, :precision => 3, :strip_insignificant_zeros => true))
assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ','))
assert_equal("1000.000 %", number_to_percentage(1000, :format => "%n %"))
+ assert_equal '1&lt;script&gt;&lt;/script&gt;010%', number_to_percentage(1.01, :separator => "<script></script>")
+ assert_equal '1&lt;script&gt;&lt;/script&gt;000.000%', number_to_percentage(1000, :delimiter => "<script></script>")
end
def test_number_with_delimiter
@@ -104,6 +109,8 @@ class NumberHelperTest < ActionView::TestCase
def test_number_with_precision_with_custom_delimiter_and_separator
assert_equal '31,83', number_with_precision(31.825, :precision => 2, :separator => ',')
assert_equal '1.231,83', number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.')
+ assert_equal '1&lt;script&gt;&lt;/script&gt;010', number_with_precision(1.01, :separator => "<script></script>")
+ assert_equal '1&lt;script&gt;&lt;/script&gt;000.000', number_with_precision(1000, :delimiter => "<script></script>")
end
def test_number_with_precision_with_significant_digits
@@ -193,6 +200,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '1.0 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :strip_insignificant_zeros => false)
assert_equal '1.012 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :significant => false)
assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 0, :significant => true) #ignores significant it precision is 0
+ assert_equal '9&lt;script&gt;&lt;/script&gt;86 KB', number_to_human_size(10100, :separator => "<script></script>")
end
def test_number_to_human_size_with_custom_delimiter_and_separator
@@ -253,6 +261,9 @@ class NumberHelperTest < ActionView::TestCase
#Spaces are stripped from the resulting string
assert_equal '4', number_to_human(4, :units => {:unit => "", :ten => 'tens '})
assert_equal '4.5 tens', number_to_human(45, :units => {:unit => "", :ten => ' tens '})
+
+ assert_equal '1&lt;script&gt;&lt;/script&gt;01', number_to_human(1.01, :separator => "<script></script>")
+ assert_equal '100&lt;script&gt;&lt;/script&gt;000 Quadrillion', number_to_human(10**20, :delimiter => "<script></script>")
end
def test_number_to_human_with_custom_format
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index f58e474759..a3ab091c6c 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -60,14 +60,14 @@ class TextHelperTest < ActionView::TestCase
simple_format(text)
assert_equal text_clone, text
end
-
+
def test_simple_format_does_not_modify_the_html_options_hash
options = { :class => "foobar"}
passed_options = options.dup
simple_format("some text", passed_options)
assert_equal options, passed_options
end
-
+
def test_simple_format_does_not_modify_the_options_hash
options = { :wrapper_tag => :div, :sanitize => false }
passed_options = options.dup
@@ -75,19 +75,11 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
- def test_truncate_should_not_be_html_safe
- assert !truncate("Hello World!", :length => 12).html_safe?
- end
-
def test_truncate
assert_equal "Hello World!", truncate("Hello World!", :length => 12)
assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
end
- def test_truncate_should_not_escape_input
- assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12)
- end
-
def test_truncate_should_use_default_length_of_30
str = "This is a string that will go longer then the default truncate length of 30"
assert_equal str[0...27] + "...", truncate(str)
@@ -106,7 +98,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
end
-
+
def test_truncate_does_not_modify_the_options_hash
options = { :length => 10 }
passed_options = options.dup
@@ -114,6 +106,53 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
+ def test_truncate_with_link_options
+ assert_equal "Here's a long test and I...<a href=\"#\">Continue</a>",
+ truncate("Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_should_be_html_safe
+ assert truncate("Hello World!", :length => 12).html_safe?
+ end
+
+ def test_truncate_should_escape_the_input
+ assert_equal "Hello &lt;sc...", truncate("Hello <script>code!</script>World!!", :length => 12)
+ end
+
+ def test_truncate_should_not_escape_the_input_with_escape_false
+ assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12, :escape => false)
+ end
+
+ def test_truncate_with_escape_false_should_be_html_safe
+ truncated = truncate("Hello <script>code!</script>World!!", :length => 12, :escape => false)
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_be_html_safe
+ truncated = truncate("Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_escape_the_input
+ assert_equal "&lt;script&gt;code!&lt;/script&gt;He...<a href=\"#\">Continue</a>",
+ truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_with_block_should_not_escape_the_input_with_escape_false
+ assert_equal "<script>code!</script>He...<a href=\"#\">Continue</a>",
+ truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27, :escape => false) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_with_block_with_escape_false_should_be_html_safe
+ truncated = truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27, :escape => false) { link_to 'Continue', '#' }
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_escape_the_block
+ assert_equal "Here's a long test and I...&lt;script&gt;alert('foo');&lt;/script&gt;",
+ truncate("Here's a long test and I need a continue to read link", :length => 27) { "<script>alert('foo');</script>" }
+ end
+
def test_highlight_should_be_html_safe
assert highlight("This is a beautiful morning", "beautiful").html_safe?
end
@@ -203,7 +242,7 @@ class TextHelperTest < ActionView::TestCase
highlight("<div>abc div</div>", "div", :highlighter => '<b>\1</b>')
)
end
-
+
def test_highlight_does_not_modify_the_options_hash
options = { :highlighter => '<b>\1</b>', :sanitize => false }
passed_options = options.dup
@@ -256,7 +295,7 @@ class TextHelperTest < ActionView::TestCase
def test_excerpt_with_utf8
assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8))
end
-
+
def test_excerpt_does_not_modify_the_options_hash
options = { :omission => "[...]",:radius => 5 }
passed_options = options.dup
@@ -271,7 +310,7 @@ class TextHelperTest < ActionView::TestCase
def test_word_wrap_with_extra_newlines
assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", :line_width => 15))
end
-
+
def test_word_wrap_does_not_modify_the_options_hash
options = { :line_width => 15 }
passed_options = options.dup
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 97777ccff0..d496dbb35e 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -111,18 +111,28 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_translate_with_default_named_html
translation = translate(:'translations.missing', :default => :'translations.hello_html')
assert_equal '<a>Hello World</a>', translation
- assert translation.html_safe?
+ assert_equal true, translation.html_safe?
end
def test_translate_with_two_defaults_named_html
translation = translate(:'translations.missing', :default => [:'translations.missing_html', :'translations.hello_html'])
assert_equal '<a>Hello World</a>', translation
- assert translation.html_safe?
+ assert_equal true, translation.html_safe?
end
def test_translate_with_last_default_named_html
translation = translate(:'translations.missing', :default => [:'translations.missing', :'translations.hello_html'])
assert_equal '<a>Hello World</a>', translation
- assert translation.html_safe?
+ assert_equal true, translation.html_safe?
+ end
+
+ def test_translate_with_string_default
+ translation = translate(:'translations.missing', default: 'A Generic String')
+ assert_equal 'A Generic String', translation
+ end
+
+ def test_translate_with_array_of_string_defaults
+ translation = translate(:'translations.missing', default: ['A Generic String', 'Second generic string'])
+ assert_equal 'A Generic String', translation
end
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index fb5b35bac6..62608a727f 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -144,6 +144,13 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_button_to_with_block
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><button type=\"submit\"><span>Hello</span></button></div></form>",
+ button_to("http://www.example.com") { content_tag(:span, 'Hello') }
+ )
+ end
+
def test_link_tag_with_straight_url
assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", "http://www.example.com")
end
@@ -270,6 +277,16 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_link_tag_with_block
+ assert_dom_equal '<a href="/"><span>Example site</span></a>',
+ link_to('/') { content_tag(:span, 'Example site') }
+ end
+
+ def test_link_tag_with_block_and_html_options
+ assert_dom_equal '<a class="special" href="/"><span>Example site</span></a>',
+ link_to('/', :class => "special") { content_tag(:span, 'Example site') }
+ end
+
def test_link_tag_using_block_in_erb
out = render_erb %{<%= link_to('/') do %>Example site<% end %>}
assert_equal '<a href="/">Example site</a>', out
@@ -282,6 +299,16 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_link_tag_escapes_content
+ assert_dom_equal '<a href="/">Malicious &lt;script&gt;content&lt;/script&gt;</a>',
+ link_to("Malicious <script>content</script>", "/")
+ end
+
+ def test_link_tag_does_not_escape_html_safe_content
+ assert_dom_equal '<a href="/">Malicious <script>content</script></a>',
+ link_to("Malicious <script>content</script>".html_safe, "/")
+ end
+
def test_link_to_unless
assert_equal "Showing", link_to_unless(true, "Showing", url_hash)