aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb10
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb33
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb64
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb28
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb14
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb2
-rw-r--r--actionpack/lib/action_controller/metal.rb14
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb9
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb6
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb2
-rw-r--r--actionpack/lib/action_controller/railtie.rb17
-rw-r--r--actionpack/lib/action_controller/railties/url_helpers.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb103
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb1
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb53
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb26
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb52
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb18
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb41
-rw-r--r--actionpack/lib/action_view.rb4
-rw-r--r--actionpack/lib/action_view/base.rb67
-rw-r--r--actionpack/lib/action_view/context.rb2
-rw-r--r--actionpack/lib/action_view/helpers.rb50
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb49
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb23
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb32
-rw-r--r--actionpack/lib/action_view/helpers/deprecated_block_helpers.rb52
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb88
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb402
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb49
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb12
-rw-r--r--actionpack/lib/action_view/locale/en.yml51
-rw-r--r--actionpack/lib/action_view/lookup_context.rb105
-rw-r--r--actionpack/lib/action_view/railtie.rb12
-rw-r--r--actionpack/lib/action_view/render/layouts.rb17
-rw-r--r--actionpack/lib/action_view/render/rendering.rb13
-rw-r--r--actionpack/lib/action_view/template.rb24
-rw-r--r--actionpack/lib/action_view/template/error.rb24
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb19
-rw-r--r--actionpack/lib/action_view/template/resolver.rb45
-rw-r--r--actionpack/lib/action_view/test_case.rb29
-rw-r--r--actionpack/test/abstract_unit.rb54
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb13
-rw-r--r--actionpack/test/controller/base_test.rb39
-rw-r--r--actionpack/test/controller/caching_test.rb38
-rw-r--r--actionpack/test/controller/cookie_test.rb11
-rw-r--r--actionpack/test/controller/filters_test.rb4
-rw-r--r--actionpack/test/controller/integration_test.rb8
-rw-r--r--actionpack/test/controller/layout_test.rb16
-rw-r--r--actionpack/test/controller/mime_responds_test.rb2
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb27
-rw-r--r--actionpack/test/controller/new_base/metal_test.rb6
-rw-r--r--actionpack/test/controller/new_base/middleware_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_action_test.rb4
-rw-r--r--actionpack/test/controller/new_base/render_rjs_test.rb18
-rw-r--r--actionpack/test/controller/render_test.rb24
-rw-r--r--actionpack/test/dispatch/routing_test.rb58
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb7
-rw-r--r--actionpack/test/fixtures/functional_caching/_partial.erb4
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb4
-rw-r--r--actionpack/test/fixtures/functional_caching/fragment_cached.html.erb2
-rw-r--r--actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb2
-rw-r--r--actionpack/test/fixtures/layouts/block_with_layout.erb4
-rw-r--r--actionpack/test/fixtures/test/array_translation.erb1
-rw-r--r--actionpack/test/fixtures/test/deprecated_nested_layout.erb3
-rw-r--r--actionpack/test/fixtures/test/nested_layout.erb2
-rw-r--r--actionpack/test/fixtures/test/scoped_array_translation.erb1
-rw-r--r--actionpack/test/fixtures/test/using_layout_around_block.html.erb2
-rw-r--r--actionpack/test/lib/fixture_template.rb8
-rw-r--r--actionpack/test/template/active_model_helper_test.rb18
-rw-r--r--actionpack/test/template/body_parts_test.rb3
-rw-r--r--actionpack/test/template/capture_helper_test.rb106
-rw-r--r--actionpack/test/template/erb/form_for_test.rb11
-rw-r--r--actionpack/test/template/erb/helper.rb30
-rw-r--r--actionpack/test/template/erb/tag_helper_test.rb58
-rw-r--r--actionpack/test/template/javascript_helper_test.rb31
-rw-r--r--actionpack/test/template/lookup_context_test.rb45
-rw-r--r--actionpack/test/template/number_helper_i18n_test.rb128
-rw-r--r--actionpack/test/template/number_helper_test.rb295
-rw-r--r--actionpack/test/template/output_buffer_test.rb19
-rw-r--r--actionpack/test/template/render_test.rb10
-rw-r--r--actionpack/test/template/translation_helper_test.rb15
-rw-r--r--actionpack/test/template/url_helper_test.rb5
96 files changed, 1890 insertions, 960 deletions
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index f875213afb..53cf6b3931 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -6,16 +6,10 @@ module AbstractController
include Rendering
- def self.next_serial
- @helper_serial ||= 0
- @helper_serial += 1
- end
-
included do
- class_attribute :_helpers, :_helper_serial
+ class_attribute :_helpers
delegate :_helpers, :to => :'self.class'
self._helpers = Module.new
- self._helper_serial = ::AbstractController::Helpers.next_serial
end
module ClassMethods
@@ -95,8 +89,6 @@ module AbstractController
# helper(:three, BlindHelper) { def mice() 'mice' end }
#
def helper(*args, &block)
- self._helper_serial = AbstractController::Helpers.next_serial + 1
-
modules_for_helpers(args).each do |mod|
add_template_helper(mod)
end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 95a6101109..319472c937 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/remove_method"
+
module AbstractController
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
# repeated setups. The inclusion pattern has pages that look like this:
@@ -182,7 +184,9 @@ module AbstractController
#
# ==== Returns
# Boolean:: True if the action has a layout, false otherwise.
- def _action_has_layout?
+ def action_has_layout?
+ return unless super
+
conditions = _layout_conditions
if only = conditions[:only]
@@ -237,6 +241,8 @@ module AbstractController
# name, return that string. Otherwise, use the superclass'
# layout (which might also be implied)
def _write_layout_method
+ remove_possible_method(:_layout)
+
case defined?(@_layout) ? @_layout : nil
when String
self.class_eval %{def _layout; #{@_layout.inspect} end}
@@ -284,15 +290,20 @@ module AbstractController
layout = options.key?(:layout) ? options.delete(:layout) : :default
value = _layout_for_option(layout)
options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
-
- # TODO Layout for partials should be handled here, because inside the
- # partial renderer it looks for the layout as a partial.
- if options.key?(:partial) && options[:layout]
- options[:layout] = view_context.find_layout(options[:layout])
- end
end
end
+ attr_writer :action_has_layout
+
+ def initialize(*)
+ @action_has_layout = true
+ super
+ end
+
+ def action_has_layout?
+ @action_has_layout
+ end
+
private
# This will be overwritten by _write_layout_method
@@ -332,13 +343,13 @@ module AbstractController
# Template:: The template object for the default layout (or nil)
def _default_layout(require_layout = false)
begin
- layout_name = _layout if _action_has_layout?
+ layout_name = _layout if action_has_layout?
rescue NameError => e
raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
- if require_layout && _action_has_layout? && !layout_name
+ if require_layout && action_has_layout? && !layout_name
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
@@ -349,9 +360,5 @@ module AbstractController
def _include_layout?(options)
(options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
end
-
- def _action_has_layout?
- true
- end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 16664098e5..b251bd6405 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -32,7 +32,6 @@ module AbstractController
module Rendering
extend ActiveSupport::Concern
- include AbstractController::Assigns
include AbstractController::ViewPaths
# Overwrite process to setup I18n proxy.
@@ -43,17 +42,48 @@ module AbstractController
I18n.config = old_config
end
+ module ClassMethods
+ def view_context_class
+ @view_context_class ||= begin
+ controller = self
+ Class.new(ActionView::Base) do
+ if controller.respond_to?(:_helpers)
+ include controller._helpers
+
+ if controller.respond_to?(:_router)
+ include controller._router.url_helpers
+ end
+
+ # TODO: Fix RJS to not require this
+ self.helpers = controller._helpers
+ end
+ end
+ end
+ end
+ end
+
+ attr_writer :view_context_class
+
+ def view_context_class
+ @view_context_class || self.class.view_context_class
+ end
+
+ def initialize(*)
+ @view_context_class = nil
+ super
+ end
+
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
- # View.for_controller[controller]
+ # View.new[lookup_context, assigns, controller]
# Create a new ActionView instance for a controller
- # View#render_template[options]
+ # View#render[options]
# Returns String with the rendered template
#
# Override this method in a module to change the default behavior.
def view_context
- @_view_context ||= ActionView::Base.for_controller(self)
+ view_context_class.new(lookup_context, view_assigns, self)
end
# Normalize arguments, options and then delegates render_to_body and
@@ -76,13 +106,12 @@ module AbstractController
# :api: plugin
def render_to_string(options={})
_normalize_options(options)
- AbstractController::Rendering.body_to_s(render_to_body(options))
+ render_to_body(options)
end
# Find and renders a template based on the options given.
# :api: private
def _render_template(options) #:nodoc:
- _evaluate_assigns(view_context)
view_context.render(options)
end
@@ -91,20 +120,19 @@ module AbstractController
controller_path
end
- # Return a string representation of a Rack-compatible response body.
- def self.body_to_s(body)
- if body.respond_to?(:to_str)
- body
- else
- strings = []
- body.each { |part| strings << part.to_s }
- body.close if body.respond_to?(:close)
- strings.join
- end
- end
-
private
+ # This method should return a hash with assigns.
+ # You can overwrite this configuration per controller.
+ # :api: public
+ def view_assigns
+ hash = {}
+ variables = instance_variable_names
+ variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
+ variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) }
+ hash
+ end
+
# Normalize options by converting render "foo" to render :action => "foo" and
# render "foo/bar" to render :file => "foo/bar".
def _normalize_args(action=nil, options={})
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index ad2b68af21..5797282b41 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -19,6 +19,7 @@ module ActionController
include SessionManagement
include ActionController::Caching
include ActionController::MimeResponds
+ include ActionController::PolymorphicRoutes
# Rails 2.x compatibility
include ActionController::Compatibility
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 35111a4b92..43ddf6435a 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -80,6 +80,7 @@ module ActionController #:nodoc:
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
+ options[:layout] = true unless options.key?(:layout)
filter_options = options.extract!(:if, :unless).merge(:only => actions)
cache_options = options.extract!(:layout, :cache_path).merge(:store_options => options)
@@ -87,14 +88,12 @@ module ActionController #:nodoc:
end
end
- def _render_cache_fragment(cache, extension, layout)
- render :text => cache, :layout => layout, :content_type => Mime[extension || :html]
- end
-
- def _save_fragment(name, layout, options)
+ def _save_fragment(name, options)
return unless caching_allowed?
- content = layout ? view_context.content_for(:layout) : response_body
+ content = response_body
+ content = content.join if content.is_a?(Array)
+
write_fragment(name, content, options)
end
@@ -112,7 +111,7 @@ module ActionController #:nodoc:
class ActionCacheFilter #:nodoc:
def initialize(options, &block)
- @cache_path, @store_options, @layout =
+ @cache_path, @store_options, @cache_layout =
options.values_at(:cache_path, :store_options, :layout)
end
@@ -125,12 +124,19 @@ module ActionController #:nodoc:
cache_path = ActionCachePath.new(controller, path_options || {})
- if cache = controller.read_fragment(cache_path.path, @store_options)
- controller._render_cache_fragment(cache, cache_path.extension, @layout == false)
- else
+ body = controller.read_fragment(cache_path.path, @store_options)
+
+ unless body
+ controller.action_has_layout = false unless @cache_layout
yield
- controller._save_fragment(cache_path.path, @layout == false, @store_options)
+ controller.action_has_layout = true
+ body = controller._save_fragment(cache_path.path, @store_options)
end
+
+ body = controller.render_to_string(:text => cache, :layout => true) unless @cache_layout
+
+ controller.response_body = body
+ controller.content_type = Mime[cache_path.extension || :html]
end
end
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 89787727bd..473a2fe214 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -34,20 +34,6 @@ module ActionController #:nodoc:
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
- def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
- if perform_caching
- if fragment_exist?(name, options)
- buffer.safe_concat(read_fragment(name, options))
- else
- pos = buffer.length
- block.call
- write_fragment(name, buffer[pos..-1], options)
- end
- else
- block.call
- end
- end
-
# Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
def write_fragment(key, content, options = nil)
return content unless cache_configured?
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
index 1d05b3fbd6..bbde570ca9 100644
--- a/actionpack/lib/action_controller/deprecated/base.rb
+++ b/actionpack/lib/action_controller/deprecated/base.rb
@@ -63,7 +63,7 @@ module ActionController
def ip_spoofing_check
ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
"Configuring ip_spoofing_check on the application configures a middleware.", caller
- Rails.application.config.action_disaptch.ip_spoofing_check
+ Rails.application.config.action_dispatch.ip_spoofing_check
end
def trusted_proxies=(value)
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index eebd2c943a..30aa34d956 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -34,11 +34,12 @@ module ActionController
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
- attr_internal :status, :headers, :content_type, :response, :request
+ attr_internal :headers, :response, :request
delegate :session, :to => "@_request"
def initialize(*)
- @_headers = {}
+ @_headers = {"Content-Type" => "text/html"}
+ @_status = 200
super
end
@@ -62,10 +63,19 @@ module ActionController
headers["Location"] = url
end
+ def status
+ @_status
+ end
+
def status=(status)
@_status = Rack::Utils.status_code(status)
end
+ def response_body=(val)
+ body = val.respond_to?(:each) ? val : [val]
+ super body
+ end
+
# :api: private
def dispatch(name, request)
@_request = request
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index ab8d87b2c4..e6cea483bb 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -40,15 +40,6 @@ module ActionController
def initialize_template_class(*) end
def assign_shortcuts(*) end
- def template
- @template ||= view_context
- end
-
- def process_action(*)
- template
- super
- end
-
def _normalize_options(options)
if options[:action] && options[:action].to_s.include?(?/)
ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 2ac199265d..4f384d1ec5 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -262,7 +262,7 @@ module ActionController #:nodoc:
if format = request.negotiate_mime(collector.order)
self.content_type ||= format.to_s
- self.formats = [format.to_sym]
+ lookup_context.freeze_formats([format.to_sym])
collector.response_for(format)
else
head :not_acceptable
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index 37106733cb..060117756e 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -5,10 +5,8 @@ module ActionController
module RackDelegation
extend ActiveSupport::Concern
- included do
- delegate :headers, :status=, :location=, :content_type=,
- :status, :location, :content_type, :to => "@_response"
- end
+ delegate :headers, :status=, :location=, :content_type=,
+ :status, :location, :content_type, :to => "@_response"
def dispatch(action, request)
@_response = ActionDispatch::Response.new
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 25e4e18493..b5f1d23ef0 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -76,7 +76,7 @@ module ActionController
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
- when %r{^\w[\w\d+.-]*:.*}
+ when %r{^\w[\w+.-]*:.*}
options
when String
request.protocol + request.host_with_port + options
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 08325b468c..d906e1fb5b 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -87,7 +87,7 @@ module ActionController
end
add :update do |proc, options|
- _evaluate_assigns(view_context)
+ view_context = self.view_context
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
self.content_type = Mime::JS
self.response_body = generator.to_s
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 6178a59029..0b2cee6868 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -1,3 +1,5 @@
+require 'active_support/json'
+
module ActionController #:nodoc:
# Responder is responsible for exposing a resource to different mime requests,
# usually depending on the HTTP verb. The responder is triggered when
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 6a3afbb157..0ec89928af 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -1,16 +1,17 @@
require "rails"
require "action_controller"
+require "action_dispatch/railtie"
require "action_view/railtie"
require "active_support/core_ext/class/subclasses"
require "active_support/deprecation/proxy_wrappers"
require "active_support/deprecation"
+require "action_controller/railties/log_subscriber"
+require "action_controller/railties/url_helpers"
+
module ActionController
class Railtie < Rails::Railtie
- railtie_name :action_controller
-
- require "action_controller/railties/log_subscriber"
- require "action_controller/railties/url_helpers"
+ config.action_controller = ActiveSupport::OrderedOptions.new
ad = config.action_dispatch
config.action_controller.singleton_class.send(:define_method, :session) do
@@ -37,7 +38,7 @@ module ActionController
ad.session_store = val
end
- log_subscriber ActionController::Railties::LogSubscriber.new
+ log_subscriber :action_controller, ActionController::Railties::LogSubscriber.new
initializer "action_controller.logger" do
ActionController.base_hook { self.logger ||= Rails.logger }
@@ -52,7 +53,9 @@ module ActionController
ac.stylesheets_dir = paths.public.stylesheets.to_a.first
ac.secret = app.config.cookie_secret
- ActionController.base_hook { self.config.replace(ac) }
+ ActionController.base_hook do
+ self.config.merge!(ac)
+ end
end
initializer "action_controller.initialize_framework_caches" do
@@ -67,7 +70,7 @@ module ActionController
initializer "action_controller.url_helpers" do |app|
ActionController.base_hook do
- extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
+ extend ::ActionController::Railties::UrlHelpers.with(app.routes)
end
message = "ActionController::Routing::Routes is deprecated. " \
diff --git a/actionpack/lib/action_controller/railties/url_helpers.rb b/actionpack/lib/action_controller/railties/url_helpers.rb
index ad2a8d4ef3..5f95e1c621 100644
--- a/actionpack/lib/action_controller/railties/url_helpers.rb
+++ b/actionpack/lib/action_controller/railties/url_helpers.rb
@@ -1,5 +1,5 @@
module ActionController
- class Railtie
+ module Railties
module UrlHelpers
def self.with(router)
Module.new do
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index cdb5db32aa..120f34460e 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,7 +1,107 @@
require 'rack/session/abstract/id'
-require 'action_view/test_case'
module ActionController
+ module TemplateAssertions
+ extend ActiveSupport::Concern
+
+ included do
+ setup :setup_subscriptions
+ teardown :teardown_subscriptions
+ end
+
+ def setup_subscriptions
+ @partials = Hash.new(0)
+ @templates = Hash.new(0)
+ @layouts = Hash.new(0)
+
+ ActiveSupport::Notifications.subscribe("action_view.render_template") do |name, start, finish, id, payload|
+ path = payload[:layout]
+ @layouts[path] += 1
+ end
+
+ ActiveSupport::Notifications.subscribe("action_view.render_template!") do |name, start, finish, id, payload|
+ path = payload[:virtual_path]
+ next unless path
+ partial = path =~ /^.*\/_[^\/]*$/
+ if partial
+ @partials[path] += 1
+ @partials[path.split("/").last] += 1
+ @templates[path] += 1
+ else
+ @templates[path] += 1
+ end
+ end
+ end
+
+ def teardown_subscriptions
+ ActiveSupport::Notifications.unsubscribe("action_view.render_template!")
+ end
+
+ # Asserts that the request was rendered with the appropriate template file or partials
+ #
+ # ==== Examples
+ #
+ # # assert that the "new" view template was rendered
+ # assert_template "new"
+ #
+ # # assert that the "_customer" partial was rendered twice
+ # assert_template :partial => '_customer', :count => 2
+ #
+ # # assert that no partials were rendered
+ # assert_template :partial => false
+ #
+ def assert_template(options = {}, message = nil)
+ validate_request!
+
+ case options
+ when NilClass, String
+ rendered = @templates
+ msg = build_message(message,
+ "expecting <?> but rendering with <?>",
+ options, rendered.keys.join(', '))
+ assert_block(msg) do
+ if options.nil?
+ @templates.blank?
+ else
+ rendered.any? { |t,num| t.match(options) }
+ end
+ end
+ when Hash
+ if expected_partial = options[:partial]
+ if expected_count = options[:count]
+ actual_count = @partials[expected_partial]
+ # actual_count = found.nil? ? 0 : found[1]
+ msg = build_message(message,
+ "expecting ? to be rendered ? time(s) but rendered ? time(s)",
+ expected_partial, expected_count, actual_count)
+ assert(actual_count == expected_count.to_i, msg)
+ elsif options.key?(:layout)
+ msg = build_message(message,
+ "expecting layout <?> but action rendered <?>",
+ expected_layout, @layouts.keys)
+
+ case layout = options[:layout]
+ when String
+ assert(@layouts.include?(expected_layout), msg)
+ when Regexp
+ assert(@layouts.any? {|l| l =~ layout }, msg)
+ when nil
+ assert(@layouts.empty?, msg)
+ end
+ else
+ msg = build_message(message,
+ "expecting partial <?> but action rendered <?>",
+ options[:partial], @partials.keys)
+ assert(@partials.include?(expected_partial), msg)
+ end
+ else
+ assert @partials.empty?,
+ "Expected no partials to be rendered"
+ end
+ end
+ end
+ end
+
class TestRequest < ActionDispatch::TestRequest #:nodoc:
def initialize(env = {})
super
@@ -181,6 +281,7 @@ module ActionController
# assert_redirected_to page_url(:title => 'foo')
class TestCase < ActiveSupport::TestCase
include ActionDispatch::TestProcess
+ include ActionController::TemplateAssertions
# Executes a request simulating GET HTTP method and set/volley the response
def get(action, parameters = nil, session = nil, flash = nil)
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 451b79b190..e42b4d09b0 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -25,7 +25,6 @@ module ActionDispatch
module FilterParameters
extend ActiveSupport::Concern
- mattr_reader :compiled_parameter_filter_for
@@compiled_parameter_filter_for = {}
# Return a hash of parameters with all sensitive data replaced.
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 13c0f2bad0..3f1a77295d 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -52,12 +52,6 @@ module Mime
cattr_reader :browser_generated_types
attr_reader :symbol
- @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
- def self.unverifiable_types
- ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
- @@unverifiable_types
- end
-
# A simple helper class used in parsing the accept header
class AcceptItem #:nodoc:
attr_accessor :order, :name, :q
@@ -100,7 +94,7 @@ module Mime
end
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
- Mime.instance_eval { const_set symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms) }
+ Mime.const_set(symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms))
SET << Mime.const_get(symbol.to_s.upcase)
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 0dc03a1a7e..ab7130ab08 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -84,6 +84,7 @@ module ActionDispatch
options[:path] ||= "/"
@set_cookies[key] = options
+ @delete_cookies.delete(key)
value
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index e486bd4079..563df0f256 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -3,9 +3,8 @@ require "rails"
module ActionDispatch
class Railtie < Rails::Railtie
- railtie_name :action_dispatch
-
- config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ config.action_dispatch = ActiveSupport::OrderedOptions.new
+ config.action_dispatch.x_sendfile_header = ""
config.action_dispatch.ip_spoofing_check = true
# Prepare dispatcher callbacks and run 'prepare' callbacks
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 5bc3205c51..c6e942555f 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/regexp'
+require 'action_controller/polymorphic_routes'
module ActionDispatch
# == Routing
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index f9b27a5a03..5a3868e1d4 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -32,6 +32,8 @@ module ActionDispatch
end
class Mapping
+ IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor]
+
def initialize(set, scope, args)
@set, @scope = set, scope
@path, @options = extract_path_and_options(args)
@@ -45,18 +47,21 @@ module ActionDispatch
def extract_path_and_options(args)
options = args.extract_options!
- case
- when using_to_shorthand?(args, options)
+ if using_to_shorthand?(args, options)
path, to = options.find { |name, value| name.is_a?(String) }
options.merge!(:to => to).delete(path) if path
- when using_match_shorthand?(args, options)
- path = args.first
- options = { :to => path.gsub("/", "#"), :as => path.gsub("/", "_") }
else
path = args.first
end
- [ normalize_path(path), options ]
+ path = normalize_path(path)
+
+ if using_match_shorthand?(path, options)
+ options[:to] ||= path[1..-1].sub(%r{/([^/]*)$}, '#\1')
+ options[:as] ||= path[1..-1].gsub("/", "_")
+ end
+
+ [ path, options ]
end
# match "account" => "account#index"
@@ -65,14 +70,13 @@ module ActionDispatch
end
# match "account/overview"
- def using_match_shorthand?(args, options)
- args.present? && options.except(:via, :anchor).empty? && !args.first.include?(':')
+ def using_match_shorthand?(path, options)
+ path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$}
end
def normalize_path(path)
- path = "#{@scope[:path]}/#{path}"
- raise ArgumentError, "path is required" if path.empty?
- Mapper.normalize_path(path)
+ raise ArgumentError, "path is required" if @scope[:path].blank? && path.blank?
+ Mapper.normalize_path("#{@scope[:path]}/#{path}")
end
def app
@@ -94,7 +98,15 @@ module ActionDispatch
end
def defaults
- @defaults ||= if to.respond_to?(:call)
+ @defaults ||= (@options[:defaults] || {}).tap do |defaults|
+ defaults.merge!(default_controller_and_action)
+ defaults.reverse_merge!(@scope[:defaults]) if @scope[:defaults]
+ @options.each { |k, v| defaults[k] = v unless v.is_a?(Regexp) || IGNORE_OPTIONS.include?(k.to_sym) }
+ end
+ end
+
+ def default_controller_and_action
+ if to.respond_to?(:call)
{ }
else
defaults = case to
@@ -144,8 +156,8 @@ module ActionDispatch
def segment_keys
@segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new(
- Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS)
- ).names
+ Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS)
+ ).names
end
def to
@@ -297,11 +309,14 @@ module ActionDispatch
scope(:constraints => constraints) { yield }
end
+ def defaults(defaults = {})
+ scope(:defaults => defaults) { yield }
+ end
+
def match(*args)
options = args.extract_options!
options = (@scope[:options] || {}).merge(options)
- options[:anchor] = true unless options.key?(:anchor)
if @scope[:name_prefix] && !options[:as].blank?
options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}"
@@ -342,6 +357,10 @@ module ActionDispatch
merge_options_scope(parent, child)
end
+ def merge_defaults_scope(parent, child)
+ merge_options_scope(parent, child)
+ end
+
def merge_blocks_scope(parent, child)
(parent || []) + [child]
end
@@ -460,7 +479,7 @@ module ActionDispatch
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
- scope(:name_prefix => resource.name.to_s) do
+ scope(:name_prefix => resource.name.to_s, :as => "") do
yield if block_given?
end
@@ -563,6 +582,8 @@ module ActionDispatch
def match(*args)
options = args.extract_options!
+ options[:anchor] = true unless options.key?(:anchor)
+
if args.length > 1
args.each { |path| match(path, options) }
return self
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 722be432c7..bb689beed9 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -65,7 +65,7 @@ module ActionDispatch
# named routes.
class NamedRouteCollection #:nodoc:
include Enumerable
- attr_reader :routes, :helpers
+ attr_reader :routes, :helpers, :module
def initialize
clear!
@@ -179,6 +179,7 @@ module ActionDispatch
url_for(options)
end
+ protected :#{selector}
END_EVAL
helpers << selector
end
@@ -219,14 +220,16 @@ module ActionDispatch
end
def finalize!
+ return if @finalized
+ @finalized = true
@set.add_route(NotFound)
- install_helpers
@set.freeze
end
def clear!
# Clear the controller cache so we may discover new ones
@controller_constraints = nil
+ @finalized = false
routes.clear
named_routes.clear
@set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY)
@@ -239,21 +242,30 @@ module ActionDispatch
def url_helpers
@url_helpers ||= begin
- router = self
+ routes = self
- Module.new do
+ helpers = Module.new do
extend ActiveSupport::Concern
include UrlFor
+ @routes = routes
+ class << self
+ delegate :url_for, :to => '@routes'
+ end
+ extend routes.named_routes.module
+
# ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
# we can include?
# Yes plz - JP
included do
- router.install_helpers(self)
+ routes.install_helpers(self)
+ singleton_class.send(:define_method, :_router) { routes }
end
- define_method(:_router) { router }
+ define_method(:_router) { routes }
end
+
+ helpers
end
end
@@ -406,6 +418,7 @@ module ActionDispatch
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
def url_for(options)
+ finalize!
options = default_url_options.merge(options || {})
handle_positional_args(options)
@@ -437,6 +450,7 @@ module ActionDispatch
end
def call(env)
+ finalize!
@set.call(env)
end
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index ec78f53fa6..b8c02f402c 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -72,7 +72,7 @@ module ActionDispatch
# you can do that by including ActionController::UrlFor in your class:
#
# class User < ActiveRecord::Base
- # include ActionController::UrlFor
+ # include Rails.application.routes.url_helpers
#
# def base_uri
# user_path(self)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 937c9f48d2..ec5e9efe44 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -73,58 +73,6 @@ module ActionDispatch
end
end
- # Asserts that the request was rendered with the appropriate template file or partials
- #
- # ==== Examples
- #
- # # assert that the "new" view template was rendered
- # assert_template "new"
- #
- # # assert that the "_customer" partial was rendered twice
- # assert_template :partial => '_customer', :count => 2
- #
- # # assert that no partials were rendered
- # assert_template :partial => false
- #
- def assert_template(options = {}, message = nil)
- validate_request!
-
- case options
- when NilClass, String
- rendered = (@controller.template.rendered[:template] || []).map { |t| t.identifier }
- msg = build_message(message,
- "expecting <?> but rendering with <?>",
- options, rendered.join(', '))
- assert_block(msg) do
- if options.nil?
- @controller.template.rendered[:template].blank?
- else
- rendered.any? { |t| t.match(options) }
- end
- end
- when Hash
- if expected_partial = options[:partial]
- partials = @controller.template.rendered[:partials]
- if expected_count = options[:count]
- found = partials.detect { |p, _| p.identifier.match(expected_partial) }
- actual_count = found.nil? ? 0 : found.second
- msg = build_message(message,
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
- expected_partial, expected_count, actual_count)
- assert(actual_count == expected_count.to_i, msg)
- else
- msg = build_message(message,
- "expecting partial <?> but action rendered <?>",
- options[:partial], partials.keys)
- assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg)
- end
- else
- assert @controller.template.rendered[:partials].empty?,
- "Expected no partials to be rendered"
- end
- end
- end
-
private
# Proxy to to_param if the object will respond to it.
def parameterize(value)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 1d7e8090e4..1bb81ede3b 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -145,11 +145,25 @@ module ActionDispatch
old_routes, @router = @router, ActionDispatch::Routing::RouteSet.new
old_controller, @controller = @controller, @controller.clone if @controller
_router = @router
- @controller.singleton_class.send(:send, :include, @router.url_helpers) if @controller
+
+ # Unfortunately, there is currently an abstraction leak between AC::Base
+ # and AV::Base which requires having the URL helpers in both AC and AV.
+ # To do this safely at runtime for tests, we need to bump up the helper serial
+ # to that the old AV subclass isn't cached.
+ #
+ # TODO: Make this unnecessary
+ if @controller
+ @controller.singleton_class.send(:include, _router.url_helpers)
+ @controller.view_context_class = Class.new(@controller.view_context_class) do
+ include _router.url_helpers
+ end
+ end
yield @router
ensure
@router = old_routes
- @controller = old_controller if @controller
+ if @controller
+ @controller = old_controller
+ end
end
# ROUTES TODO: These assertions should really work in an integration context
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 0aff4250c1..621d63c5e2 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -2,6 +2,7 @@ require 'stringio'
require 'uri'
require 'active_support/core_ext/object/singleton_class'
require 'rack/test'
+require 'test/unit/assertions'
module ActionDispatch
module Integration #:nodoc:
@@ -177,14 +178,8 @@ module ActionDispatch
reset!
end
- def url_options
- opts = super.reverse_merge(
- :host => host,
- :protocol => https? ? "https" : "http"
- )
-
- opts.merge!(:port => 443) if !opts.key?(:port) && https?
- opts
+ def default_url_options
+ { :host => host, :protocol => https? ? "https" : "http" }
end
# Resets the instance. This can be used to reset the state information
@@ -293,6 +288,8 @@ module ActionDispatch
end
module Runner
+ include ActionDispatch::Assertions
+
def app
@app
end
@@ -300,7 +297,7 @@ module ActionDispatch
# Reset the current session. This is useful for testing multiple sessions
# in a single test case.
def reset!
- @integration_session = open_session
+ @integration_session = Integration::Session.new(app)
end
%w(get post put head delete cookies assigns
@@ -326,30 +323,9 @@ module ActionDispatch
# can use this method to open multiple sessions that ought to be tested
# simultaneously.
def open_session(app = nil)
- session = Integration::Session.new(app || self.app)
-
- # delegate the fixture accessors back to the test instance
- extras = Module.new { attr_accessor :delegate, :test_result }
- if self.class.respond_to?(:fixture_table_names)
- self.class.fixture_table_names.each do |table_name|
- name = table_name.tr(".", "_")
- next unless respond_to?(name)
- extras.__send__(:define_method, name) { |*args|
- delegate.send(name, *args)
- }
- end
+ dup.tap do |session|
+ yield session if block_given?
end
-
- # delegate add_assertion to the test case
- extras.__send__(:define_method, :add_assertion) {
- test_result.add_assertion
- }
- session.extend(extras)
- session.delegate = self
- session.test_result = @_result
-
- yield session if block_given?
- session
end
# Copy the instance variables from the current session instance into the
@@ -460,6 +436,7 @@ module ActionDispatch
# end
class IntegrationTest < ActiveSupport::TestCase
include Integration::Runner
+ include ActionController::TemplateAssertions
@@app = nil
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index afe6386105..5555217ee2 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -44,13 +44,15 @@ module ActionView
autoload :Base
autoload :LookupContext
- autoload :MissingTemplate, 'action_view/base'
autoload :Resolver, 'action_view/template/resolver'
autoload :PathResolver, 'action_view/template/resolver'
autoload :FileSystemResolver, 'action_view/template/resolver'
autoload :PathSet, 'action_view/paths'
+ autoload :MissingTemplate, 'action_view/template/error'
+ autoload :ActionViewError, 'action_view/template/error'
autoload :TemplateError, 'action_view/template/error'
+
autoload :TemplateHandler, 'action_view/template'
autoload :TemplateHandlers, 'action_view/template'
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index feaf45c333..919b1e3470 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -1,27 +1,10 @@
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/array/wrap'
module ActionView #:nodoc:
- class ActionViewError < StandardError #:nodoc:
- end
-
- class MissingTemplate < ActionViewError #:nodoc:
- attr_reader :path
-
- def initialize(paths, path, details, partial)
- @path = path
- display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
- template_type = if partial
- "partial"
- elsif path =~ /layouts/i
- 'layout'
- else
- 'template'
- end
-
- super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
- end
+ class NonConcattingString < ActiveSupport::SafeBuffer
end
# Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
@@ -176,14 +159,13 @@ module ActionView #:nodoc:
include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
extend ActiveSupport::Memoizable
- ActionView.run_base_hooks(self)
-
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
@@debug_rjs = false
class_attribute :helpers
+ remove_method :helpers
attr_reader :helpers
class << self
@@ -191,8 +173,10 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
+ ActionView.run_base_hooks(self)
+
attr_accessor :base_path, :assigns, :template_extension, :lookup_context
- attr_internal :captures, :request, :layout, :controller, :template, :config
+ attr_internal :captures, :request, :controller, :template, :config
delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
:view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
@@ -202,42 +186,17 @@ module ActionView #:nodoc:
delegate :logger, :to => :controller, :allow_nil => true
+ # TODO: HACK FOR RJS
+ def view_context
+ self
+ end
+
def self.xss_safe? #:nodoc:
true
end
def self.process_view_paths(value)
- ActionView::PathSet.new(Array(value))
- end
-
- def self.for_controller(controller)
- @views ||= {}
-
- # TODO: Decouple this so helpers are a separate concern in AV just like
- # they are in AC.
- if controller.class.respond_to?(:_helper_serial)
- klass = @views[controller.class._helper_serial] ||= Class.new(self) do
- # Try to make stack traces clearer
- class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def self.name
- "ActionView for #{controller.class}"
- end
-
- def inspect
- "#<#{self.class.name}>"
- end
- ruby_eval
-
- if controller.respond_to?(:_helpers)
- include controller._helpers
- self.helpers = controller._helpers
- end
- end
- else
- klass = self
- end
-
- klass.new(controller.lookup_context, {}, controller)
+ ActionView::PathSet.new(Array.wrap(value))
end
def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
@@ -246,7 +205,7 @@ module ActionView #:nodoc:
@helpers = self.class.helpers || Module.new
@_controller = controller
- @_config = ActiveSupport::InheritableOptions.new(controller.config) if controller
+ @_config = ActiveSupport::InheritableOptions.new(controller.config) if controller && controller.respond_to?(:config)
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb
index df078a7151..61d2e702a7 100644
--- a/actionpack/lib/action_view/context.rb
+++ b/actionpack/lib/action_view/context.rb
@@ -10,8 +10,6 @@ module ActionView
# In order to work with ActionController, a Context
# must implement:
#
- # Context.for_controller[controller] Create a new ActionView instance for a
- # controller
# Context#render_partial[options]
# - responsible for setting options[:_template]
# - Returns String with the rendered partial
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index e359b0bdac..a50c180f63 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -2,30 +2,32 @@ require 'active_support/benchmarkable'
module ActionView #:nodoc:
module Helpers #:nodoc:
- autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper'
- autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper'
- autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper'
- autoload :CacheHelper, 'action_view/helpers/cache_helper'
- autoload :CaptureHelper, 'action_view/helpers/capture_helper'
- autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
- autoload :DateHelper, 'action_view/helpers/date_helper'
- autoload :DebugHelper, 'action_view/helpers/debug_helper'
- autoload :DeprecatedBlockHelpers, 'action_view/helpers/deprecated_block_helpers'
- autoload :FormHelper, 'action_view/helpers/form_helper'
- autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
- autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
- autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper'
- autoload :NumberHelper, 'action_view/helpers/number_helper'
- autoload :PrototypeHelper, 'action_view/helpers/prototype_helper'
- autoload :RawOutputHelper, 'action_view/helpers/raw_output_helper'
- autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper'
- autoload :RecordTagHelper, 'action_view/helpers/record_tag_helper'
- autoload :SanitizeHelper, 'action_view/helpers/sanitize_helper'
- autoload :ScriptaculousHelper, 'action_view/helpers/scriptaculous_helper'
- autoload :TagHelper, 'action_view/helpers/tag_helper'
- autoload :TextHelper, 'action_view/helpers/text_helper'
- autoload :TranslationHelper, 'action_view/helpers/translation_helper'
- autoload :UrlHelper, 'action_view/helpers/url_helper'
+ extend ActiveSupport::Autoload
+
+ autoload :ActiveModelHelper
+ autoload :AssetTagHelper
+ autoload :AtomFeedHelper
+ autoload :CacheHelper
+ autoload :CaptureHelper
+ autoload :CsrfHelper
+ autoload :DateHelper
+ autoload :DebugHelper
+ autoload :DeprecatedBlockHelpers
+ autoload :FormHelper
+ autoload :FormOptionsHelper
+ autoload :FormTagHelper
+ autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
+ autoload :NumberHelper
+ autoload :PrototypeHelper
+ autoload :RawOutputHelper
+ autoload :RecordIdentificationHelper
+ autoload :RecordTagHelper
+ autoload :SanitizeHelper
+ autoload :ScriptaculousHelper
+ autoload :TagHelper
+ autoload :TextHelper
+ autoload :TranslationHelper
+ autoload :UrlHelper
def self.included(base)
base.extend(ClassMethods)
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index e3db2923f7..80b3d3a664 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -3,6 +3,7 @@ require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/object/blank'
module ActionView
ActionView.base_hook do
@@ -127,7 +128,7 @@ module ActionView
object = convert_to_model(object)
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
- (errors = obj.errors[method])
+ (errors = obj.errors[method]).presence
content_tag("div",
"#{options[:prepend_text]}#{ERB::Util.h(errors.first)}#{options[:append_text]}".html_safe,
:class => options[:css_class]
@@ -295,6 +296,10 @@ module ActionView
end
end
+ def error_message
+ object.errors[@method_name]
+ end
+
def column_type
object.send(:column_for_attribute, @method_name).type
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index b594002629..02ad41719b 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -3,6 +3,7 @@ require 'cgi'
require 'action_view/helpers/url_helper'
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/file'
+require 'active_support/core_ext/object/blank'
module ActionView
module Helpers #:nodoc:
@@ -623,41 +624,37 @@ module ActionView
@@cache_asset_timestamps = true
private
+ def rewrite_extension?(source, dir, ext)
+ source_ext = File.extname(source)[1..-1]
+ ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(config.assets_dir, dir, "#{source}.#{ext}"))))
+ end
+
+ def rewrite_host_and_protocol(source, has_request)
+ host = compute_asset_host(source)
+ if has_request && host.present? && !is_uri?(host)
+ host = "#{controller.request.protocol}#{host}"
+ end
+ "#{host}#{source}"
+ end
+
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
# roots. Rewrite the asset path for cache-busting asset ids. Include
# asset host, if configured, with the correct request protocol.
def compute_public_path(source, dir, ext = nil, include_host = true)
- has_request = controller.respond_to?(:request)
-
- source_ext = File.extname(source)[1..-1]
- if ext && !is_uri?(source) && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(config.assets_dir, dir, "#{source}.#{ext}"))))
- source += ".#{ext}"
- end
+ return source if is_uri?(source)
- unless is_uri?(source)
- source = "/#{dir}/#{source}" unless source[0] == ?/
+ source += ".#{ext}" if rewrite_extension?(source, dir, ext)
+ source = "/#{dir}/#{source}" unless source[0] == ?/
+ source = rewrite_asset_path(source)
- source = rewrite_asset_path(source)
-
- if has_request && include_host
- unless source =~ %r{^#{controller.config.relative_url_root}/}
- source = "#{controller.config.relative_url_root}#{source}"
- end
- end
+ has_request = controller.respond_to?(:request)
+ if has_request && include_host && source !~ %r{^#{controller.config.relative_url_root}/}
+ source = "#{controller.config.relative_url_root}#{source}"
end
+ source = rewrite_host_and_protocol(source, has_request) if include_host
- if include_host && !is_uri?(source)
- host = compute_asset_host(source)
-
- if has_request && !host.blank? && !is_uri?(host)
- host = "#{controller.request.protocol}#{host}"
- end
-
- "#{host}#{source}"
- else
- source
- end
+ source
end
def is_uri?(path)
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index d5cc14b29a..a904af56bb 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -32,7 +32,28 @@ module ActionView
# <i>Topics listed alphabetically</i>
# <% end %>
def cache(name = {}, options = nil, &block)
- controller.fragment_for(output_buffer, name, options, &block)
+ safe_concat fragment_for(name, options, &block)
+ nil
+ end
+
+ private
+ # TODO: Create an object that has caching read/write on it
+ def fragment_for(name = {}, options = nil, &block) #:nodoc:
+ if controller.perform_caching
+ if controller.fragment_exist?(name, options)
+ controller.read_fragment(name, options)
+ else
+ # VIEW TODO: Make #capture usable outside of ERB
+ # This dance is needed because Builder can't use capture
+ pos = output_buffer.length
+ yield
+ fragment = output_buffer.slice!(pos..-1)
+ controller.write_fragment(name, fragment, options)
+ end
+ else
+ ret = yield
+ ActiveSupport::SafeBuffer.new(ret) if ret.is_a?(String)
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 03c7ba5a87..f0be814700 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -2,22 +2,22 @@ module ActionView
module Helpers
# CaptureHelper exposes methods to let you extract generated markup which
# can be used in other parts of a template or layout file.
- # It provides a method to capture blocks into variables through capture and
+ # It provides a method to capture blocks into variables through capture and
# a way to capture a block of markup for use in a layout through content_for.
module CaptureHelper
- # The capture method allows you to extract part of a template into a
- # variable. You can then use this variable anywhere in your templates or layout.
- #
+ # The capture method allows you to extract part of a template into a
+ # variable. You can then use this variable anywhere in your templates or layout.
+ #
# ==== Examples
# The capture method can be used in ERb templates...
- #
+ #
# <% @greeting = capture do %>
# Welcome to my shiny new web page! The date and time is
# <%= Time.now %>
# <% end %>
#
# ...and Builder (RXML) templates.
- #
+ #
# @timestamp = capture do
# "The current timestamp is #{Time.now}."
# end
@@ -32,16 +32,18 @@ module ActionView
#
def capture(*args)
value = nil
- buffer = with_output_buffer { value = yield *args }
- buffer.presence || value
+ buffer = with_output_buffer { value = yield(*args) }
+ if string = buffer.presence || value and string.is_a?(String)
+ NonConcattingString.new(string)
+ end
end
# Calling content_for stores a block of markup in an identifier for later use.
# You can make subsequent calls to the stored content in other templates or the layout
# by passing the identifier as an argument to <tt>yield</tt>.
- #
+ #
# ==== Examples
- #
+ #
# <% content_for :not_authorized do %>
# alert('You are not authorized to do that!')
# <% end %>
@@ -75,7 +77,7 @@ module ActionView
#
# Then, in another view, you could to do something like this:
#
- # <%= link_to_remote 'Logout', :action => 'logout' %>
+ # <%= link_to 'Logout', :action => 'logout', :remote => true %>
#
# <% content_for :script do %>
# <%= javascript_include_tag :defaults %>
@@ -92,7 +94,7 @@ module ActionView
# <% end %>
#
# <%# Add some other content, or use a different template: %>
- #
+ #
# <% content_for :navigation do %>
# <li><%= link_to 'Login', :action => 'login' %></li>
# <% end %>
@@ -109,13 +111,13 @@ module ActionView
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
- return @_content_for[name] << content if content
- @_content_for[name]
+ @_content_for[name] << content if content
+ @_content_for[name] unless content
end
# content_for? simply checks whether any content has been captured yet using content_for
# Useful to render parts of your layout differently based on what is in your views.
- #
+ #
# ==== Examples
#
# Perhaps you will use different css in you layout if no content_for :right_column
diff --git a/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
deleted file mode 100644
index 3d0657e873..0000000000
--- a/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module ActionView
- module Helpers
- module DeprecatedBlockHelpers
- extend ActiveSupport::Concern
-
- include ActionView::Helpers::TagHelper
- include ActionView::Helpers::TextHelper
- include ActionView::Helpers::JavaScriptHelper
- include ActionView::Helpers::FormHelper
-
- def content_tag(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- def javascript_tag(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- def form_for(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- def form_tag(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- def fields_for(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- def field_set_tag(*, &block)
- block_called_from_erb?(block) ? safe_concat(super) : super
- end
-
- BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
-
- if RUBY_VERSION < '1.9.0'
- # Check whether we're called from an erb template.
- # We'd return a string in any other case, but erb <%= ... %>
- # can't take an <% end %> later on, so we have to use <% ... %>
- # and implicitly concat.
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block)
- end
- else
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 6176f1fb06..2ba5339b7d 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1014,7 +1014,7 @@ module ActionView
class FormBuilder #:nodoc:
# The methods which wrap a form helper call.
class_inheritable_accessor :field_helpers
- self.field_helpers = (FormHelper.instance_methods - ['form_for'])
+ self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
attr_accessor :object_name, :object, :options
@@ -1040,7 +1040,7 @@ module ActionView
end
(field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
- src = <<-end_src
+ src, file, line = <<-end_src, __FILE__, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
#{selector.inspect}, # "text_field",
@@ -1049,7 +1049,7 @@ module ActionView
objectify_options(options)) # objectify_options(options))
end # end
end_src
- class_eval src, __FILE__, __LINE__
+ class_eval src, file, line
end
def fields_for(record_or_name_or_array, *args, &block)
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 07eee3b399..b0a7718f22 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -1,5 +1,4 @@
require 'action_view/helpers/tag_helper'
-require 'action_view/helpers/prototype_helper'
module ActionView
module Helpers
@@ -89,6 +88,93 @@ module ActionView
def javascript_cdata_section(content) #:nodoc:
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
+
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
+ # onclick handler.
+ #
+ # The first argument +name+ is used as the button's value or display text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
+ # The +function+ argument can be omitted in favor of an +update_page+
+ # block, which evaluates to a string when the template is rendered
+ # (instead of making an Ajax request first).
+ #
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ # Examples:
+ # button_to_function "Greeting", "alert('Hello world!')"
+ # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
+ # button_to_function "Details" do |page|
+ # page[:details].visual_effect :toggle_slide
+ # end
+ # button_to_function "Details", :class => "details_button" do |page|
+ # page[:details].visual_effect :toggle_slide
+ # end
+ def button_to_function(name, *args, &block)
+ html_options = args.extract_options!.symbolize_keys
+
+ function = block_given? ? update_page(&block) : args[0] || ''
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
+
+ tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
+ end
+
+ # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
+ # onclick handler and return false after the fact.
+ #
+ # The first argument +name+ is used as the link text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
+ # The +function+ argument can be omitted in favor of an +update_page+
+ # block, which evaluates to a string when the template is rendered
+ # (instead of making an Ajax request first).
+ #
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ #
+ # Examples:
+ # link_to_function "Greeting", "alert('Hello world!')"
+ # Produces:
+ # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
+ #
+ # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
+ # Produces:
+ # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
+ # <img src="/images/delete.png?" alt="Delete"/>
+ # </a>
+ #
+ # link_to_function("Show me more", nil, :id => "more_link") do |page|
+ # page[:details].visual_effect :toggle_blind
+ # page[:more_link].replace_html "Show me less"
+ # end
+ # Produces:
+ # <a href="#" id="more_link" onclick="try {
+ # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
+ # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
+ # }
+ # catch (e) {
+ # alert('RJS error:\n\n' + e.toString());
+ # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
+ # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
+ # throw e
+ # };
+ # return false;">Show me more</a>
+ #
+ def link_to_function(name, *args, &block)
+ html_options = args.extract_options!.symbolize_keys
+
+ function = block_given? ? update_page(&block) : args[0] || ''
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
+ href = html_options[:href] || '#'
+
+ content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 46e41bc406..719b64b940 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -3,10 +3,24 @@ require 'active_support/core_ext/float/rounding'
module ActionView
module Helpers #:nodoc:
+
# Provides methods for converting numbers into formatted strings.
# Methods are provided for phone numbers, currency, percentage,
- # precision, positional notation, and file size.
+ # precision, positional notation, file size and pretty printing.
+ #
+ # Most methods expect a +number+ argument, and will return it
+ # unchanged if can't be converted into a valid number.
module NumberHelper
+
+ # Raised when argument +number+ param given to the helpers is invalid and
+ # the option :raise is set to +true+.
+ class InvalidNumberError < StandardError
+ attr_accessor :number
+ def initialize(number)
+ @number = number
+ end
+ end
+
# Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
# in the +options+ hash.
#
@@ -30,6 +44,17 @@ module ActionView
def number_to_phone(number, options = {})
return nil if number.nil?
+ begin
+ Float(number)
+ is_number_html_safe = true
+ rescue ArgumentError, TypeError
+ if options[:raise]
+ raise InvalidNumberError, number
+ else
+ is_number_html_safe = number.to_s.html_safe?
+ end
+ end
+
number = number.to_s.strip
options = options.symbolize_keys
area_code = options[:area_code] || nil
@@ -46,7 +71,7 @@ module ActionView
number.starts_with?('-') ? number.slice!(1..-1) : number
end
str << " x #{extension}" unless extension.blank?
- str
+ is_number_html_safe ? str.html_safe : str
end
# Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
@@ -72,38 +97,42 @@ module ActionView
# number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
# # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
+ return nil if number.nil?
+
options.symbolize_keys!
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
- currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
+ currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
defaults = defaults.merge(currency)
- precision = options[:precision] || defaults[:precision]
- unit = options[:unit] || defaults[:unit]
- separator = options[:separator] || defaults[:separator]
- delimiter = options[:delimiter] || defaults[:delimiter]
- format = options[:format] || defaults[:format]
- separator = '' if precision == 0
+ options = options.reverse_merge(defaults)
- value = number_with_precision(number,
- :precision => precision,
- :delimiter => delimiter,
- :separator => separator)
+ unit = options.delete(:unit)
+ format = options.delete(:format)
- if value
+ begin
+ value = number_with_precision(number, options.merge(:raise => true))
format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
- else
- number
+ 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
+
end
# Formats a +number+ as a percentage string (e.g., 65%). You can customize the
# format in the +options+ hash.
#
# ==== Options
- # * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
- # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
#
# ==== Examples
# number_to_percentage(100) # => 100.000%
@@ -111,21 +140,25 @@ module ActionView
# number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
# number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
def number_to_percentage(number, options = {})
+ return nil if number.nil?
+
options.symbolize_keys!
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
- percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
+ percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :default => {})
defaults = defaults.merge(percentage)
- precision = options[:precision] || defaults[:precision]
- separator = options[:separator] || defaults[:separator]
- delimiter = options[:delimiter] || defaults[:delimiter]
+ options = options.reverse_merge(defaults)
- value = number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter)
- value ? value + "%" : number
+ begin
+ "#{number_with_precision(number, options.merge(:raise => true))}%".html_safe
+ rescue InvalidNumberError => e
+ if options[:raise]
+ raise
+ else
+ e.number.to_s.html_safe? ? "#{e.number}%".html_safe : "#{e.number}%"
+ end
+ end
end
# Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
@@ -133,7 +166,7 @@ module ActionView
#
# ==== Options
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
- # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
#
# ==== Examples
# number_with_delimiter(12345678) # => 12,345,678
@@ -146,148 +179,186 @@ module ActionView
# You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
# +delimiter+ as its optional second and the +separator+ as its
# optional third parameter:
- # number_with_delimiter(12345678, " ") # => 12 345.678
+ # number_with_delimiter(12345678, " ") # => 12 345 678
# number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05
def number_with_delimiter(number, *args)
options = args.extract_options!
options.symbolize_keys!
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+ begin
+ Float(number)
+ rescue ArgumentError, TypeError
+ if options[:raise]
+ raise InvalidNumberError, number
+ else
+ return number
+ end
+ end
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
unless args.empty?
ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
'instead of separate delimiter and precision arguments.', caller)
- delimiter = args[0] || defaults[:delimiter]
- separator = args[1] || defaults[:separator]
+ options[:delimiter] ||= args[0] if args[0]
+ options[:separator] ||= args[1] if args[1]
end
- delimiter ||= (options[:delimiter] || defaults[:delimiter])
- separator ||= (options[:separator] || defaults[:separator])
+ options = options.reverse_merge(defaults)
parts = number.to_s.split('.')
- if parts[0]
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
- parts.join(separator)
- else
- number
- end
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
+ parts.join(options[:separator]).html_safe
+
end
- # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
+ # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision
+ # of 2 if +:significant+ is +false+, and 5 if +:significant+ is +true+).
# You can customize the format in the +options+ hash.
#
# ==== Options
- # * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
- # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+)
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +false+)
#
# ==== 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
- # number_with_precision(389.32314, :precision => 0) # => 389
+ # number_with_precision(111.2345) # => 111.235
+ # number_with_precision(111.2345, :precision => 2) # => 111.23
+ # number_with_precision(13, :precision => 5) # => 13.00000
+ # number_with_precision(389.32314, :precision => 0) # => 389
+ # number_with_precision(111.2345, :significant => true) # => 111
+ # number_with_precision(111.2345, :precision => 1, :significant => true) # => 100
+ # number_with_precision(13, :precision => 5, :significant => true) # => 13.000
+ # number_with_precision(13, :precision => 5, :significant => true, strip_insignificant_zeros => true)
+ # # => 13
+ # number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
# # => 1.111,23
#
# You can still use <tt>number_with_precision</tt> with the old API that accepts the
# +precision+ as its optional second parameter:
- # number_with_precision(number_with_precision(111.2345, 2) # => 111.23
+ # number_with_precision(111.2345, 2) # => 111.23
def number_with_precision(number, *args)
+
options = args.extract_options!
options.symbolize_keys!
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
- precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
- :raise => true) rescue {}
+ number = begin
+ Float(number)
+ rescue ArgumentError, TypeError
+ if options[:raise]
+ raise InvalidNumberError, number
+ else
+ return number
+ end
+ end
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
+ precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale], :default => {})
defaults = defaults.merge(precision_defaults)
+ #Backwards compatibility
unless args.empty?
ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
'instead of a separate precision argument.', caller)
- precision = args[0] || defaults[:precision]
+ options[:precision] ||= args[0] if args[0]
end
- precision ||= (options[:precision] || defaults[:precision])
- separator ||= (options[:separator] || defaults[:separator])
- delimiter ||= (options[:delimiter] || defaults[:delimiter])
+ options = options.reverse_merge(defaults) # Allow the user to unset default values: Eg.: :significant => false
+ precision = options.delete :precision
+ significant = options.delete :significant
+ strip_insignificant_zeros = options.delete :strip_insignificant_zeros
- begin
- value = Float(number)
- rescue ArgumentError, TypeError
- value = nil
+ if significant and precision > 0
+ digits = (Math.log10(number) + 1).floor
+ rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision)
+ precision = precision - digits
+ precision = precision > 0 ? precision : 0 #don't let it be negative
+ else
+ rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision
end
-
- if value
- rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
- number_with_delimiter("%01.#{precision}f" % rounded_number,
- :separator => separator,
- :delimiter => delimiter)
+ 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
- number
+ formatted_number
end
+
end
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
- # Formats the bytes in +size+ into a more understandable representation
+ # Formats the bytes in +number+ into a more understandable representation
# (e.g., giving it 1500 yields 1.5 KB). This method is useful for
- # reporting file sizes to users. This method returns nil if
- # +size+ cannot be converted into a number. You can customize the
+ # reporting file sizes to users. You can customize the
# format in the +options+ hash.
#
+ # See <tt>number_to_human</tt> if you want to pretty-print a generic number.
+ #
# ==== Options
- # * <tt>:precision</tt> - Sets the level of precision (defaults to 1).
- # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
- #
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
# ==== Examples
# number_to_human_size(123) # => 123 Bytes
- # number_to_human_size(1234) # => 1.2 KB
+ # number_to_human_size(1234) # => 1.21 KB
# number_to_human_size(12345) # => 12.1 KB
- # number_to_human_size(1234567) # => 1.2 MB
- # number_to_human_size(1234567890) # => 1.1 GB
- # number_to_human_size(1234567890123) # => 1.1 TB
- # number_to_human_size(1234567, :precision => 2) # => 1.18 MB
- # number_to_human_size(483989, :precision => 0) # => 473 KB
- # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
+ # number_to_human_size(1234567) # => 1.18 MB
+ # number_to_human_size(1234567890) # => 1.15 GB
+ # number_to_human_size(1234567890123) # => 1.12 TB
+ # number_to_human_size(1234567, :precision => 2) # => 1.2 MB
+ # number_to_human_size(483989, :precision => 2) # => 470 KB
+ # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB
#
- # Zeros after the decimal point are always stripped out, regardless of the
- # specified precision:
- # helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB"
- # helper.number_to_human_size(524288000, :precision=>5) # => "500 MB"
+ # Unsignificant zeros after the fractional separator are stripped out by default (set
+ # <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
+ # number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB"
+ # number_to_human_size(524288000, :precision=>5) # => "500 MB"
#
# You can still use <tt>number_to_human_size</tt> with the old API that accepts the
# +precision+ as its optional second parameter:
- # number_to_human_size(1234567, 2) # => 1.18 MB
- # number_to_human_size(483989, 0) # => 473 KB
+ # number_to_human_size(1234567, 1) # => 1 MB
+ # number_to_human_size(483989, 2) # => 470 KB
def number_to_human_size(number, *args)
- return nil if number.nil?
-
options = args.extract_options!
options.symbolize_keys!
- defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
- human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
+ number = begin
+ Float(number)
+ rescue ArgumentError, TypeError
+ if options[:raise]
+ raise InvalidNumberError, number
+ else
+ return number
+ end
+ end
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
+ human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
defaults = defaults.merge(human)
unless args.empty?
ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' +
'instead of a separate precision argument.', caller)
- precision = args[0] || defaults[:precision]
+ options[:precision] ||= args[0] if args[0]
end
- precision ||= (options[:precision] || defaults[:precision])
- separator ||= (options[:separator] || defaults[:separator])
- delimiter ||= (options[:delimiter] || defaults[:delimiter])
+ options = options.reverse_merge(defaults)
+ #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)
if number.to_i < 1024
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)
+ storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe
else
max_exp = STORAGE_UNITS.size - 1
- number = Float(number)
exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
number /= 1024 ** exponent
@@ -295,15 +366,138 @@ module ActionView
unit_key = STORAGE_UNITS[exponent]
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
- escaped_separator = Regexp.escape(separator)
- formatted_number = number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter
- ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
+ formatted_number = number_with_precision(number, options)
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).html_safe
+ end
+ 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 (and too hard to read).
+ #
+ # See <tt>number_to_human_size</tt> if you want to print a file size.
+ #
+ # You can also define you own unit-quantifier names if you want to use other decimal units
+ # (eg.: 1500 becomes "1.5 kilometers", 0.150 becomes "150 mililiters", etc). You may define
+ # a wide range of unit quantifiers, even fractional ones (centi, deci, mili, etc).
+ #
+ # ==== Options
+ # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3).
+ # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+)
+ # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+)
+ # * <tt>:units</tt> - A Hash of unit quantifier names. Or a string containing an i18n scope where to find this hash. It might have the following keys:
+ # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
+ # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
+ # * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
+ #
+ # %u The quantifier (ex.: 'thousand')
+ # %n The number
+ #
+ # ==== Examples
+ # number_to_human(123) # => "123"
+ # number_to_human(1234) # => "1.23 Thousand"
+ # number_to_human(12345) # => "12.3 Thousand"
+ # number_to_human(1234567) # => "1.23 Million"
+ # number_to_human(1234567890) # => "1.23 Billion"
+ # number_to_human(1234567890123) # => "1.23 Trillion"
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
+ # number_to_human(489939, :precision => 2) # => "490 Thousand"
+ # number_to_human(489939, :precision => 4) # => "489.9 Thousand"
+ # number_to_human(1234567, :precision => 4,
+ # :significant => false) # => "1.2346 Million"
+ # number_to_human(1234567, :precision => 1,
+ # :separator => ',',
+ # :significant => false) # => "1,2 Million"
+ #
+ # Unsignificant zeros after the decimal separator are stripped out by default (set
+ # <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
+ # number_to_human(12345012345, :significant_digits => 6) # => "12.345 Billion"
+ # number_to_human(500000000, :precision=>5) # => "500 Million"
+ #
+ # ==== Custom Unit Quantifiers
+ #
+ # You can also use your own custom unit quantifiers:
+ # number_to_human(500000, :units => {:unit => "ml", :thousand => "lt"}) # => "500 lt"
+ #
+ # If in your I18n locale you have:
+ # distance:
+ # centi:
+ # one: "centimeter"
+ # other: "centimeters"
+ # unit:
+ # one: "meter"
+ # other: "meters"
+ # thousand:
+ # one: "kilometer"
+ # other: "kilometers"
+ # billion: "gazilion-distance"
+ #
+ # Then you could do:
+ #
+ # number_to_human(543934, :units => :distance) # => "544 kilometers"
+ # number_to_human(54393498, :units => :distance) # => "54400 kilometers"
+ # number_to_human(54393498000, :units => :distance) # => "54.4 gazilion-distance"
+ # number_to_human(343, :units => :distance, :precision => 1) # => "300 meters"
+ # number_to_human(1, :units => :distance) # => "1 meter"
+ # number_to_human(0.34, :units => :distance) # => "34 centimeters"
+ #
+ def number_to_human(number, options = {})
+ options.symbolize_keys!
+
+ number = begin
+ Float(number)
+ rescue ArgumentError, TypeError
+ if options[:raise]
+ raise InvalidNumberError, number
+ else
+ return number
+ end
end
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
+ human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
+ defaults = defaults.merge(human)
+
+ options = options.reverse_merge(defaults)
+ #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)
+
+ 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| DECIMAL_UNITS.invert[e_name] }.sort_by{|e| -e}
+
+ number_exponent = Math.log10(number).floor
+ display_exponent = unit_exponents.find{|e| number_exponent >= e }
+ 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)
+ else
+ I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
+ 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
+
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index dc3c6d88f5..ccdc8181db 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -102,39 +102,6 @@ module ActionView
:form, :with, :update, :script, :type ]).merge(CALLBACKS)
end
- # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
- # onclick handler.
- #
- # The first argument +name+ is used as the button's value or display text.
- #
- # The next arguments are optional and may include the javascript function definition and a hash of html_options.
- #
- # The +function+ argument can be omitted in favor of an +update_page+
- # block, which evaluates to a string when the template is rendered
- # (instead of making an Ajax request first).
- #
- # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
- #
- # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
- #
- # Examples:
- # button_to_function "Greeting", "alert('Hello world!')"
- # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
- # button_to_function "Details" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- # button_to_function "Details", :class => "details_button" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- def button_to_function(name, *args, &block)
- html_options = args.extract_options!.symbolize_keys
-
- function = block_given? ? update_page(&block) : args[0] || ''
- onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
-
- tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
- end
-
# Returns the JavaScript needed for a remote function.
# Takes the same arguments as link_to_remote.
#
@@ -181,11 +148,9 @@ module ActionView
class JavaScriptGenerator #:nodoc:
def initialize(context, &block) #:nodoc:
@context, @lines = context, []
- @context.update_details(:formats => [:js, :html]) do
- include_helpers_from_context
- @context.with_output_buffer(@lines) do
- @context.instance_exec(self, &block)
- end
+ include_helpers_from_context
+ @context.with_output_buffer(@lines) do
+ @context.instance_exec(self, &block)
end
end
@@ -615,7 +580,7 @@ module ActionView
# page.hide 'spinner'
# end
def update_page(&block)
- JavaScriptGenerator.new(@template, &block).to_s.html_safe
+ JavaScriptGenerator.new(view_context, &block).to_s.html_safe
end
# Works like update_page but wraps the generated JavaScript in a <script>
@@ -689,6 +654,10 @@ module ActionView
@generator << root if root
end
+ def is_a?(klass)
+ klass == JavaScriptProxy
+ end
+
private
def method_missing(method, *arguments, &block)
if method.to_s =~ /(.*)=$/
@@ -882,5 +851,3 @@ module ActionView
end
end
end
-
-require 'action_view/helpers/javascript_helper'
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index e1ce65f90a..27be1690dd 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -29,7 +29,7 @@ module ActionView
end
def safe_concat(string)
- output_buffer.safe_concat(string)
+ output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
end
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
@@ -576,7 +576,7 @@ module ActionView
# each email is yielded and the result is used as the link text.
def auto_link_email_addresses(text, html_options = {})
body = text.dup
- text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
+ text.gsub(/([\w\.!#\$%\-+]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
text = $1
if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 8a89ee58a0..457944dbb6 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -13,7 +13,7 @@ module ActionView
def translate(key, options = {})
options[:raise] = true
translation = I18n.translate(scope_key_by_partial(key), options)
- translation.is_a?(Array) ? translation.map { |entry| entry.html_safe } : translation.html_safe
+ (translation.respond_to?(:join) ? translation.join : translation).html_safe
rescue I18n::MissingTranslationData => e
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
@@ -29,9 +29,10 @@ module ActionView
private
def scope_key_by_partial(key)
- if (key.respond_to?(:join) ? key.join : key.to_s).first == "."
+ strkey = key.respond_to?(:join) ? key.join : key.to_s
+ if strkey.first == "."
if @_virtual_path
- @_virtual_path.gsub(%r{/_?}, ".") + key.to_s
+ @_virtual_path.gsub(%r{/_?}, ".") + strkey
else
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 131e950b18..b23d5fcb68 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -1,6 +1,7 @@
require 'action_view/helpers/javascript_helper'
require 'active_support/core_ext/array/access'
require 'active_support/core_ext/hash/keys'
+require 'action_dispatch'
module ActionView
module Helpers #:nodoc:
@@ -9,6 +10,9 @@ module ActionView
# This allows you to use the same format for links in views
# and controllers.
module UrlHelper
+ extend ActiveSupport::Concern
+
+ include ActionDispatch::Routing::UrlFor
include JavaScriptHelper
# Need to map default url options to controller one.
@@ -16,6 +20,10 @@ module ActionView
controller.send(:default_url_options, *args)
end
+ def url_options
+ controller.url_options
+ end
+
# Returns the URL for the set of +options+ provided. This takes the
# same options as +url_for+ in Action Controller (see the
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
@@ -206,7 +214,7 @@ module ActionView
if block_given?
options = args.first || {}
html_options = args.second
- safe_concat(link_to(capture(&block), options, html_options))
+ link_to(capture(&block), options, html_options)
else
name = args[0]
options = args[1] || {}
@@ -578,8 +586,6 @@ module ActionView
add_confirm_to_attributes!(html_options, confirm) if confirm
add_method_to_attributes!(html_options, method) if method
- html_options["data-url"] = options[:url] if options.is_a?(Hash) && options[:url]
-
html_options
end
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index a3548051c1..a3e2230f6f 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -9,6 +9,11 @@
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:
@@ -16,34 +21,43 @@
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "$"
- # These three are to override number.format and are optional
+ # 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 three are to override number.format and are optional
+ # These five are to override number.format and are optional
# separator:
delimiter: ""
# precision:
+ # significant: false
+ # strip_insignificant_zeros: false
# Used in number_to_precision()
precision:
format:
- # These three are to override number.format and are optional
+ # 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()
+ # Used in number_to_human_size() and number_to_human()
human:
format:
- # These three are to override number.format and are optional
+ # These five are to override number.format and are optional
# separator:
delimiter: ""
- precision: 1
+ 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)
@@ -56,6 +70,31 @@
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:
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 8eb17bf8f1..9b59aac0eb 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
module ActionView
@@ -15,23 +15,26 @@ module ActionView
def self.register_detail(name, options = {}, &block)
self.registered_details << name
+ Accessors.send :define_method, :"_#{name}_defaults", &block
+ Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{name}
+ @details[:#{name}]
+ end
- Setters.send :define_method, :"_#{name}_defaults", &block
- Setters.module_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{name}=(value)
- value = Array(value.presence || _#{name}_defaults)
- #{"value << nil unless value.include?(nil)" unless options[:allow_nil] == false}
+ value = Array.wrap(value.presence || _#{name}_defaults)
- unless value == @details[:#{name}]
- @details_key, @details = nil, @details.merge(:#{name} => value)
- @details.freeze
+ if value != @details[:#{name}]
+ @details_key = nil
+ @details = @details.dup if @details.frozen?
+ @details[:#{name}] = value.freeze
end
end
METHOD
end
- # Holds raw setters for the registered details.
- module Setters #:nodoc:
+ # Holds accessors for the registered details.
+ module Accessors #:nodoc:
end
register_detail(:formats) { Mime::SET.symbols }
@@ -45,7 +48,7 @@ module ActionView
@details_keys = Hash.new
def self.get(details)
- @details_keys[details] ||= new
+ @details_keys[details.freeze] ||= new
end
def initialize
@@ -54,9 +57,10 @@ module ActionView
end
def initialize(view_paths, details = {})
- @details, @details_key = {}, nil
+ @details, @details_key = { :handlers => default_handlers }, nil
+ @frozen_formats = false
self.view_paths = view_paths
- self.details = details
+ self.update_details(details, true)
end
module ViewPaths
@@ -69,16 +73,16 @@ module ActionView
end
def find(name, prefix = nil, partial = false)
- @view_paths.find(name, prefix, partial, details, details_key)
+ @view_paths.find(*args_for_lookup(name, prefix, partial))
end
alias :find_template :find
def find_all(name, prefix = nil, partial = false)
- @view_paths.find_all(name, prefix, partial, details, details_key)
+ @view_paths.find_all(*args_for_lookup(name, prefix, partial))
end
def exists?(name, prefix = nil, partial = false)
- @view_paths.exists?(name, prefix, partial, details, details_key)
+ @view_paths.exists?(*args_for_lookup(name, prefix, partial))
end
alias :template_exists? :exists?
@@ -94,51 +98,80 @@ module ActionView
ensure
added_resolvers.times { view_paths.pop }
end
- end
- module Details
- attr_reader :details
+ protected
+
+ def args_for_lookup(name, prefix, partial) #:nodoc:
+ name, prefix = normalize_name(name, prefix)
+ [name, prefix, partial || false, @details, details_key]
+ end
+
+ # Support legacy foo.erb names even though we now ignore .erb
+ # as well as incorrectly putting part of the path in the template
+ # name instead of the prefix.
+ def normalize_name(name, prefix) #:nodoc:
+ name = name.to_s.gsub(handlers_regexp, '')
+ parts = name.split('/')
+ return parts.pop, [prefix, *parts].compact.join("/")
+ end
- def details=(given_details)
- registered_details.each { |key| send(:"#{key}=", given_details[key]) }
+ def default_handlers #:nodoc:
+ @default_handlers ||= Template::Handlers.extensions
end
- def details_key
+ def handlers_regexp #:nodoc:
+ @handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/
+ end
+ end
+
+ module Details
+ # Calculate the details key. Remove the handlers from calculation to improve performance
+ # since the user cannot modify it explicitly.
+ def details_key #:nodoc:
@details_key ||= DetailsKey.get(@details)
end
- # Shortcut to read formats from details.
- def formats
- @details[:formats].compact
+ # Freeze the current formats in the lookup context. By freezing them, you are guaranteeing
+ # that next template lookups are not going to modify the formats. The controller can also
+ # use this, to ensure that formats won't be further modified (as it does in respond_to blocks).
+ def freeze_formats(formats, unless_frozen=false) #:nodoc:
+ return if unless_frozen && @frozen_formats
+ self.formats = formats
+ @frozen_formats = true
end
# Overload formats= to reject [:"*/*"] values.
- def formats=(value, freeze=true)
- value = nil if value == [:"*/*"]
+ def formats=(value)
+ value = nil if value == [:"*/*"]
+ value << :html if value == [:js]
super(value)
end
- # Shortcut to read locale.
+ # Overload locale to return a symbol instead of array
def locale
- I18n.locale
+ @details[:locale].first
end
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
# to i18n_config, it means that it's has a copy of the original I18n configuration and it's
# acting as proxy, which we need to skip.
def locale=(value)
- value = value.first if value.is_a?(Array)
- config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
- config.locale = value if value
+ if value
+ config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
+ config.locale = value
+ end
super(I18n.locale)
end
# Update the details keys by merging the given hash into the current
# details hash. If a block is given, the details are modified just during
# the execution of the block and reverted to the previous value after.
- def update_details(new_details)
- old_details = @details
- self.details = old_details.merge(new_details)
+ def update_details(new_details, force=false)
+ old_details = @details.dup
+
+ registered_details.each do |key|
+ send(:"#{key}=", new_details[key]) if force || new_details.key?(key)
+ end
if block_given?
begin
@@ -150,7 +183,7 @@ module ActionView
end
end
- include Setters
+ include Accessors
include Details
include ViewPaths
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 2e5d115630..9cf007cd2b 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -3,10 +3,10 @@ require "rails"
module ActionView
class Railtie < Rails::Railtie
- railtie_name :action_view
+ config.action_view = ActiveSupport::OrderedOptions.new
require "action_view/railties/log_subscriber"
- log_subscriber ActionView::Railties::LogSubscriber.new
+ log_subscriber :action_view, ActionView::Railties::LogSubscriber.new
initializer "action_view.cache_asset_timestamps" do |app|
unless app.config.cache_classes
@@ -15,5 +15,13 @@ module ActionView
end
end
end
+
+ initializer "action_view.set_configs" do |app|
+ ActionView.base_hook do
+ app.config.action_view.each do |k,v|
+ send "#{k}=", v
+ end
+ end
+ end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
index 11ff05ce5b..578f39d817 100644
--- a/actionpack/lib/action_view/render/layouts.rb
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -43,10 +43,16 @@ module ActionView
# This is the method which actually finds the layout using details in the lookup
# context object. If no layout is found, it checkes if at least a layout with
# the given name exists across all details before raising the error.
- def find_layout(layout) #:nodoc:
+ #
+ # If self.formats contains several formats, just the first one is considered in
+ # the layout lookup.
+ def find_layout(layout)
begin
- layout =~ /^\// ?
- with_fallbacks { find_template(layout) } : find_template(layout)
+ if formats.size == 1
+ _find_layout(layout)
+ else
+ update_details(:formats => self.formats.first){ _find_layout(layout) }
+ end
rescue ActionView::MissingTemplate => e
update_details(:formats => nil) do
raise unless template_exists?(layout)
@@ -54,6 +60,11 @@ module ActionView
end
end
+ def _find_layout(layout) #:nodoc:
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout) } : find_template(layout)
+ end
+
# Contains the logic that actually renders the layout.
def _render_layout(layout, locals, &block) #:nodoc:
layout.render(self, locals){ |*name| _layout_for(*name, &block) }
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 310efe40e2..492326964a 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -16,13 +16,12 @@ module ActionView
case options
when Hash
if block_given?
- content = _render_partial(options.merge(:partial => options[:layout]), &block)
- safe_concat(content)
+ _render_partial(options.merge(:partial => options[:layout]), &block)
elsif options.key?(:partial)
_render_partial(options)
else
template = _determine_template(options)
- self.formats = template.formats
+ lookup_context.freeze_formats(template.formats, true)
_render_template(template, options[:layout], options)
end
when :update
@@ -54,16 +53,12 @@ module ActionView
layout = find_layout(layout) if layout
ActiveSupport::Notifications.instrument("action_view.render_template",
- :identifier => template.identifier, :layout => layout.try(:identifier)) do
+ :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
content = template.render(self, locals) { |*name| _layout_for(*name) }
@_content_for[:layout] = content
- if layout
- @_layout = layout.identifier
- content = _render_layout(layout, locals)
- end
-
+ content = _render_layout(layout, locals) if layout
content
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index b4fdb49d3b..8abc1633ff 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,8 +1,7 @@
# encoding: utf-8
# This is so that templates compiled in this file are UTF-8
-
-require 'set'
-require "action_view/template/resolver"
+require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/object/blank'
module ActionView
class Template
@@ -24,19 +23,20 @@ module ActionView
@identifier = identifier
@handler = handler
- @partial = details[:partial]
@virtual_path = details[:virtual_path]
@method_names = {}
- format = details[:format]
- format ||= handler.default_format.to_sym if handler.respond_to?(:default_format)
- format ||= :html
- @formats = [format.to_sym]
+ format = details[:format] || :html
+ @formats = Array.wrap(format).map(&:to_sym)
end
def render(view, locals, &block)
- method_name = compile(locals, view)
- view.send(method_name, locals, &block)
+ # Notice that 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.
+ ActiveSupport::Notifications.instrument("action_view.render_template!", :virtual_path => @virtual_path) do
+ method_name = compile(locals, view)
+ view.send(method_name, locals, &block)
+ end
rescue Exception => e
if e.is_a?(Template::Error)
e.sub_template_of(self)
@@ -58,10 +58,6 @@ module ActionView
@counter_name ||= "#{variable_name}_counter".to_sym
end
- def partial?
- @partial
- end
-
def inspect
if defined?(Rails.root)
identifier.sub("#{Rails.root}/", '')
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index 648f708d3d..5222ffa89c 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -1,6 +1,26 @@
require "active_support/core_ext/enumerable"
module ActionView
+ class ActionViewError < StandardError #:nodoc:
+ end
+
+ class MissingTemplate < ActionViewError #:nodoc:
+ attr_reader :path
+
+ def initialize(paths, path, details, partial)
+ @path = path
+ display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
+ template_type = if partial
+ "partial"
+ elsif path =~ /layouts/i
+ 'layout'
+ else
+ 'template'
+ end
+
+ super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
+ end
+ end
class Template
# The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
# bunch of intimate details and uses it to report a very precise exception message.
@@ -73,11 +93,11 @@ module ActionView
end
def to_s
- "\n#{self.class} (#{message}) #{source_location}:\n" +
+ "\n#{self.class} (#{message}) #{source_location}:\n" +
"#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
end
- # don't do anything nontrivial here. Any raised exception from here becomes fatal
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
# (and can't be rescued).
def backtrace
@backtrace
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index ac5902cc0e..705c2bf82e 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -8,6 +8,13 @@ module ActionView
super(value.to_s)
end
alias :append= :<<
+
+ def append_if_string=(value)
+ if value.is_a?(String) && !value.is_a?(NonConcattingString)
+ ActiveSupport::Deprecation.warn("<% %> style block helpers are deprecated. Please use <%= %>", caller)
+ self << value
+ end
+ end
end
module Template::Handlers
@@ -21,14 +28,24 @@ module ActionView
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
end
+ BLOCK_EXPR = /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+
def add_expr_literal(src, code)
- if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+ if code =~ BLOCK_EXPR
src << '@output_buffer.append= ' << code
else
src << '@output_buffer.append= (' << code << ');'
end
end
+ def add_stmt(src, code)
+ if code =~ BLOCK_EXPR
+ src << '@output_buffer.append_if_string= ' << code
+ else
+ super
+ end
+ end
+
def add_expr_escaped(src, code)
src << '@output_buffer.append= ' << escaped_expr(code) << ';'
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index a43597e728..8e8afaa43f 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -1,6 +1,5 @@
require "pathname"
require "active_support/core_ext/class"
-require "active_support/core_ext/array/wrap"
require "action_view/template"
module ActionView
@@ -14,15 +13,8 @@ module ActionView
@cached.clear
end
- def find(*args)
- find_all(*args).first
- end
-
# Normalizes the arguments and passes it on to find_template.
def find_all(name, prefix=nil, partial=false, details={}, key=nil)
- name, prefix = normalize_name(name, prefix)
- details = details.merge(:handlers => default_handlers)
-
cached(key, prefix, name, partial) do
find_templates(name, prefix, partial, details)
end
@@ -34,10 +26,6 @@ module ActionView
@caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
end
- def default_handlers
- Template::Handlers.extensions + [nil]
- end
-
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
@@ -45,17 +33,6 @@ module ActionView
raise NotImplementedError
end
- # Support legacy foo.erb names even though we now ignore .erb
- # as well as incorrectly putting part of the path in the template
- # name instead of the prefix.
- def normalize_name(name, prefix)
- handlers = Template::Handlers.extensions.join('|')
- name = name.to_s.gsub(/\.(?:#{handlers})$/, '')
-
- parts = name.split('/')
- return parts.pop, [prefix, *parts].compact.join("/")
- end
-
def cached(key, prefix, name, partial)
return yield unless key && caching?
scope = @cached[key][prefix][name]
@@ -79,7 +56,7 @@ module ActionView
def find_templates(name, prefix, partial, details)
path = build_path(name, prefix, partial, details)
- query(partial, path, EXTENSION_ORDER.map { |ext| details[ext] })
+ query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
end
def build_path(name, prefix, partial, details)
@@ -89,26 +66,32 @@ module ActionView
path
end
- def query(partial, path, exts)
+ def query(path, exts, formats)
query = File.join(@path, path)
exts.each do |ext|
- query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
+ query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
end
Dir[query].reject { |p| File.directory?(p) }.map do |p|
- handler, format = extract_handler_and_format(p)
+ handler, format = extract_handler_and_format(p, formats)
Template.new(File.read(p), File.expand_path(p), handler,
- :partial => partial, :virtual_path => path, :format => format)
+ :virtual_path => path, :format => format)
end
end
- def extract_handler_and_format(path)
+ # Extract handler and formats from path. If a format cannot be a found neither
+ # from the path, or the handler, we should return the array of formats given
+ # to the resolver.
+ def extract_handler_and_format(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
- handler = Template.handler_class_for_extension(pieces.pop)
- format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
+ format ||= handler.default_format if handler.respond_to?(:default_format)
+ format ||= default_formats
+
[handler, format]
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 1578ac9479..b0ababe344 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -2,29 +2,10 @@ require 'action_controller/test_case'
require 'action_view'
module ActionView
- class Base
- alias_method :initialize_without_template_tracking, :initialize
- def initialize(*args)
- @_rendered = { :template => nil, :partials => Hash.new(0) }
- initialize_without_template_tracking(*args)
- end
-
- attr_internal :rendered
- end
-
- class Template
- alias_method :render_without_tracking, :render
- def render(view, locals, &blk)
- rendered = view.rendered
- rendered[:partials][self] += 1 if partial?
- rendered[:template] ||= []
- rendered[:template] << self
- render_without_tracking(view, locals, &blk)
- end
- end
-
class TestCase < ActiveSupport::TestCase
class TestController < ActionController::Base
+ include ActionDispatch::TestProcess
+
attr_accessor :request, :response, :params
def self.controller_path
@@ -42,6 +23,7 @@ module ActionView
end
include ActionDispatch::Assertions, ActionDispatch::TestProcess
+ include ActionController::TemplateAssertions
include ActionView::Context
include ActionController::PolymorphicRoutes
@@ -64,7 +46,7 @@ module ActionView
end
def config
- @controller.config
+ @controller.config if @controller.respond_to?(:config)
end
def render(options = {}, local_assigns = {}, &block)
@@ -124,7 +106,8 @@ module ActionView
def _view
view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller)
- view.class.send :include, _helpers
+ view.singleton_class.send :include, _helpers
+ view.singleton_class.send :include, @controller._router.url_helpers
view.output_buffer = self.output_buffer
view
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 67aa412d3d..5b2ff3e871 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -44,6 +44,19 @@ ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
FIXTURES = Pathname.new(FIXTURE_LOAD_PATH)
+module RackTestUtils
+ def body_to_string(body)
+ if body.respond_to?(:each)
+ str = ""
+ body.each {|s| str << s }
+ str
+ else
+ body
+ end
+ end
+ extend self
+end
+
module SetupOnce
extend ActiveSupport::Concern
@@ -239,47 +252,6 @@ module ActionController
setup do
@router = SharedTestRoutes
end
-
- def assert_template(options = {}, message = nil)
- validate_request!
-
- hax = @controller.view_context.instance_variable_get(:@_rendered)
-
- case options
- when NilClass, String
- rendered = (hax[:template] || []).map { |t| t.identifier }
- msg = build_message(message,
- "expecting <?> but rendering with <?>",
- options, rendered.join(', '))
- assert_block(msg) do
- if options.nil?
- hax[:template].blank?
- else
- rendered.any? { |t| t.match(options) }
- end
- end
- when Hash
- if expected_partial = options[:partial]
- partials = hax[:partials]
- if expected_count = options[:count]
- found = partials.detect { |p, _| p.identifier.match(expected_partial) }
- actual_count = found.nil? ? 0 : found[1]
- msg = build_message(message,
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
- expected_partial, expected_count, actual_count)
- assert(actual_count == expected_count.to_i, msg)
- else
- msg = build_message(message,
- "expecting partial <?> but action rendered <?>",
- options[:partial], partials.keys)
- assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg)
- end
- else
- assert hax[:partials].empty?,
- "Expected no partials to be rendered"
- end
- end
- end
end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 26e0d6d844..1741b58f72 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -181,6 +181,8 @@ module Admin
end
end
+# require "action_dispatch/test_process"
+
# a test case to exercise the new capabilities TestRequest & TestResponse
class ActionPackAssertionsControllerTest < ActionController::TestCase
# -- assertion-based testing ------------------------------------------------
@@ -303,14 +305,14 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
# make sure that the template objects exist
def test_template_objects_alive
process :assign_this
- assert !@controller.template.instance_variable_get(:"@hi")
- assert @controller.template.instance_variable_get(:"@howdy")
+ assert !@controller.instance_variable_get(:"@hi")
+ assert @controller.instance_variable_get(:"@howdy")
end
# make sure we don't have template objects when we shouldn't
def test_template_object_missing
process :nothing
- assert_nil @controller.template.assigns['howdy']
+ assert_nil @controller.instance_variable_get(:@howdy)
end
# check the empty flashing
@@ -365,11 +367,10 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
# check if we were rendered by a file-based template?
def test_rendered_action
process :nothing
- assert_nil @controller.template.rendered[:template]
+ assert_template nil
process :hello_world
- assert @controller.template.rendered[:template]
- assert 'hello_world', @controller.template.rendered[:template].to_s
+ assert_template 'hello_world'
end
# check the redirection location
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index f047e7da30..49f79681f6 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -56,6 +56,16 @@ protected
end
end
+class AnotherMethodMissingController < ActionController::Base
+ cattr_accessor :_exception
+ rescue_from Exception, :with => :_exception=
+
+ protected
+ def method_missing(*attrs, &block)
+ super
+ end
+end
+
class DefaultUrlOptionsController < ActionController::Base
def from_view
render :inline => "<%= #{params[:route]} %>"
@@ -124,11 +134,11 @@ class ControllerInstanceTests < Test::Unit::TestCase
def test_action_methods
@empty_controllers.each do |c|
- assert_equal Set.new, c.class.__send__(:action_methods), "#{c.controller_path} should be empty!"
+ assert_equal Set.new, c.class.action_methods, "#{c.controller_path} should be empty!"
end
@non_empty_controllers.each do |c|
- assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!"
+ assert_equal Set.new(%w(public_action)), c.class.action_methods, "#{c.controller_path} should not be empty!"
end
end
@@ -173,6 +183,12 @@ class PerformActionTest < ActionController::TestCase
assert_equal 'method_missing', @response.body
end
+ def test_method_missing_should_recieve_symbol
+ use_controller AnotherMethodMissingController
+ get :some_action
+ assert_kind_of NameError, @controller._exception
+ end
+
def test_get_on_hidden_should_fail
use_controller NonEmptyController
assert_raise(ActionController::UnknownAction) { get :hidden_action }
@@ -191,7 +207,7 @@ class UrlOptionsTest < ActionController::TestCase
def test_url_options_override
with_routing do |set|
- set.draw do |map|
+ set.draw do
match 'from_view', :to => 'url_options#from_view', :as => :from_view
match ':controller/:action'
end
@@ -202,7 +218,18 @@ class UrlOptionsTest < ActionController::TestCase
assert_equal 'http://www.override.com/from_view?locale=en', @controller.send(:from_view_url)
assert_equal 'http://www.override.com/default_url_options/new?locale=en', @controller.url_for(:controller => 'default_url_options')
end
- end
+ end
+
+ def test_url_helpers_does_not_become_actions
+ with_routing do |set|
+ set.draw do
+ match "account/overview"
+ end
+
+ @controller.class.send(:include, set.url_helpers)
+ assert !@controller.class.action_methods.include?("account_overview_path")
+ end
+ end
end
class DefaultUrlOptionsTest < ActionController::TestCase
@@ -216,7 +243,7 @@ class DefaultUrlOptionsTest < ActionController::TestCase
def test_default_url_options_override
with_routing do |set|
- set.draw do |map|
+ set.draw do
match 'from_view', :to => 'default_url_options#from_view', :as => :from_view
match ':controller/:action'
end
@@ -231,7 +258,7 @@ class DefaultUrlOptionsTest < ActionController::TestCase
def test_default_url_options_are_used_in_non_positional_parameters
with_routing do |set|
- set.draw do |map|
+ set.draw do
scope("/:locale") do
resources :descriptions
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index a792752ef4..f0ad652d50 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -285,6 +285,8 @@ class ActionCacheTest < ActionController::TestCase
assert_not_equal cached_time, @response.body
end
+ include RackTestUtils
+
def test_action_cache_with_layout
get :with_layout
cached_time = content_to_cache
@@ -294,8 +296,8 @@ class ActionCacheTest < ActionController::TestCase
get :with_layout
assert_not_equal cached_time, @response.body
-
- assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
+ body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout'))
+ assert_equal @response.body, body
end
def test_action_cache_with_layout_and_layout_cache_false
@@ -308,7 +310,8 @@ class ActionCacheTest < ActionController::TestCase
get :layout_false
assert_not_equal cached_time, @response.body
- assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false')
+ body = body_to_string(read_fragment('hostname.com/action_caching_test/layout_false'))
+ assert_equal cached_time, body
end
def test_action_cache_conditional_options
@@ -616,8 +619,10 @@ class FragmentCachingTest < ActionController::TestCase
@store.write('views/expensive', 'fragment content')
fragment_computed = false
+ view_context = @controller.view_context
+
buffer = 'generated till now -> '.html_safe
- @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
+ buffer << view_context.send(:fragment_for, 'expensive') { fragment_computed = true }
assert fragment_computed
assert_equal 'generated till now -> ', buffer
@@ -627,8 +632,10 @@ class FragmentCachingTest < ActionController::TestCase
@store.write('views/expensive', 'fragment content')
fragment_computed = false
+ view_context = @controller.view_context
+
buffer = 'generated till now -> '.html_safe
- @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
+ buffer << view_context.send(:fragment_for, 'expensive') { fragment_computed = true }
assert !fragment_computed
assert_equal 'generated till now -> fragment content', buffer
@@ -704,8 +711,8 @@ CACHED
def test_fragment_caching_in_partials
get :html_fragment_cached_with_partial
assert_response :success
- assert_match /Fragment caching in a partial/, @response.body
- assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
+ assert_match /Old fragment caching in a partial/, @response.body
+ assert_match "Old fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
end
def test_render_inline_before_fragment_caching
@@ -719,14 +726,14 @@ CACHED
def test_fragment_caching_in_rjs_partials
xhr :get, :js_fragment_cached_with_partial
assert_response :success
- assert_match /Fragment caching in a partial/, @response.body
- assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
+ assert_match /Old fragment caching in a partial/, @response.body
+ assert_match "Old fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
def test_html_formatted_fragment_caching
get :formatted_fragment_cached, :format => "html"
assert_response :success
- expected_body = "<body>\n<p>ERB</p>\n</body>"
+ expected_body = "<body>\n<p>ERB</p>\n</body>\n"
assert_equal expected_body, @response.body
@@ -742,15 +749,4 @@ CACHED
assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
end
-
- def test_js_formatted_fragment_caching
- get :formatted_fragment_cached, :format => "js"
- assert_response :success
- expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
- %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
- assert_equal expected_body, @response.body
-
- assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
- @store.read('views/test.host/functional_caching/formatted_fragment_cached')
- end
end
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 908967a110..36498d13a9 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -64,6 +64,12 @@ class CookieTest < ActionController::TestCase
cookies.permanent.signed[:remember_me] = 100
head :ok
end
+
+ def delete_and_set_cookie
+ cookies.delete :user_name
+ cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) }
+ head :ok
+ end
end
tests TestController
@@ -152,6 +158,11 @@ class CookieTest < ActionController::TestCase
assert_equal 100, @controller.send(:cookies).signed[:remember_me]
end
+ def test_delete_and_set_cookie
+ get :delete_and_set_cookie
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
+ assert_equal({"user_name" => "david"}, @response.cookies)
+ end
private
def assert_cookie_header(expected)
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 004e1369d3..ea740f7233 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -651,9 +651,9 @@ class FilterTest < ActionController::TestCase
assert_equal %w( ensure_login find_user ), assigns["ran_filter"]
test_process(ConditionalSkippingController, "login")
- assert_nil @controller.template.controller.instance_variable_get("@ran_after_filter")
+ assert_nil @controller.instance_variable_get("@ran_after_filter")
test_process(ConditionalSkippingController, "change_password")
- assert_equal %w( clean_up ), @controller.template.controller.instance_variable_get("@ran_after_filter")
+ assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_filter")
end
def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 2180466ca7..c9782856bd 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -173,14 +173,12 @@ class IntegrationTestTest < Test::Unit::TestCase
end
def test_opens_new_session
- @test.class.expects(:fixture_table_names).times(2).returns(['foo'])
-
session1 = @test.open_session { |sess| }
session2 = @test.open_session # implicit session
- assert_kind_of ::ActionController::Integration::Session, session1
- assert_kind_of ::ActionController::Integration::Session, session2
- assert_not_equal session1, session2
+ assert session1.respond_to?(:assert_template), "open_session makes assert_template available"
+ assert session2.respond_to?(:assert_template), "open_session makes assert_template available"
+ assert !session1.equal?(session2)
end
# RSpec mixes Matchers (which has a #method_missing) into
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index f635253156..4d687c1ec6 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -120,19 +120,19 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new
get :hello
- assert @controller.template.layout.include?('layouts/layout_test')
+ assert_template :layout => "layouts/layout_test"
end
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
- assert @controller.template.layout.include?('layouts/item')
+ assert_template :layout => "layouts/item"
end
def test_layout_only_exception_when_included
@controller = OnlyLayoutController.new
get :hello
- assert @controller.template.layout.include?('layouts/item')
+ assert_template :layout => "layouts/item"
end
def test_layout_only_exception_when_excepted
@@ -144,7 +144,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_except_exception_when_included
@controller = ExceptLayoutController.new
get :hello
- assert @controller.template.layout.include?('layouts/item')
+ assert_template :layout => "layouts/item"
end
def test_layout_except_exception_when_excepted
@@ -156,19 +156,19 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_set_when_using_render
@controller = SetsLayoutInRenderController.new
get :hello
- assert @controller.template.layout.include?('layouts/third_party_template_library')
+ assert_template :layout => "layouts/third_party_template_library"
end
def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
- assert_nil @controller.template.layout
+ assert_template :layout => nil
end
def test_layout_is_picked_from_the_controller_instances_view_path
@controller = PrependsViewPathController.new
get :hello
- assert @controller.template.layout =~ /layouts\/alt\.\w+/
+ assert_template :layout => /layouts\/alt\.\w+/
end
def test_absolute_pathed_layout
@@ -219,7 +219,7 @@ unless RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/
@controller = LayoutSymlinkedTest.new
get :hello
assert_response 200
- assert @controller.template.layout.include?("layouts/symlinked/symlinked_layout")
+ assert_template :layout => "layouts/symlinked/symlinked_layout"
end
end
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 5c1eaf453c..53cd3f0801 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -765,7 +765,7 @@ class RespondWithControllerTest < ActionController::TestCase
Customer.any_instance.stubs(:errors).returns(errors)
post :using_resource_with_action
- assert_equal "foo - #{[:html].to_s}", @controller.response_body
+ assert_equal "foo - #{[:html].to_s}", @controller.response.body
end
def test_respond_as_responder_entry_point
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
new file mode 100644
index 0000000000..df4b39a103
--- /dev/null
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -0,0 +1,27 @@
+require "abstract_unit"
+
+module BareMetalTest
+ class BareController < ActionController::Metal
+ def index
+ self.response_body = "Hello world"
+ end
+ end
+
+ class BareTest < ActiveSupport::TestCase
+ test "response body is a Rack-compatible response" do
+ status, headers, body = BareController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal 200, status
+ string = ""
+
+ body.each do |part|
+ assert part.is_a?(String), "Each part of the body must be a String"
+ string << part
+ end
+
+ assert headers.is_a?(Hash), "Headers must be a Hash"
+ assert headers["Content-Type"], "Content-Type must exist"
+
+ assert_equal "Hello world", string
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/new_base/metal_test.rb b/actionpack/test/controller/new_base/metal_test.rb
index ab61fd98ee..45a6619eb4 100644
--- a/actionpack/test/controller/new_base/metal_test.rb
+++ b/actionpack/test/controller/new_base/metal_test.rb
@@ -18,6 +18,8 @@ module MetalTest
end
class TestMiddleware < ActiveSupport::TestCase
+ include RackTestUtils
+
def setup
@app = Rack::Builder.new do
use MetalTest::MetalMiddleware
@@ -29,14 +31,14 @@ module MetalTest
env = Rack::MockRequest.env_for("/authed")
response = @app.call(env)
- assert_equal "Hello World", response[2]
+ assert_equal "Hello World", body_to_string(response[2])
end
test "it can return a response using the normal AC::Metal techniques" do
env = Rack::MockRequest.env_for("/")
response = @app.call(env)
- assert_equal "Not authed!", response[2]
+ assert_equal "Not authed!", body_to_string(response[2])
assert_equal 401, response[0]
end
end
diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb
index ada0215b1a..65942ebc15 100644
--- a/actionpack/test/controller/new_base/middleware_test.rb
+++ b/actionpack/test/controller/new_base/middleware_test.rb
@@ -44,7 +44,7 @@ module MiddlewareTest
test "middleware that is 'use'd is called as part of the Rack application" do
result = @app.call(env_for("/"))
- assert_equal "Hello World", result[2]
+ assert_equal "Hello World", RackTestUtils.body_to_string(result[2])
assert_equal "Success", result[1]["Middleware-Test"]
end
diff --git a/actionpack/test/controller/new_base/render_action_test.rb b/actionpack/test/controller/new_base/render_action_test.rb
index 0804d7b09d..d92e9c4bf2 100644
--- a/actionpack/test/controller/new_base/render_action_test.rb
+++ b/actionpack/test/controller/new_base/render_action_test.rb
@@ -117,7 +117,7 @@ module RenderActionWithApplicationLayout
# # ==== Render actions with layouts ====
class BasicController < ::ApplicationController
# Set the view path to an application view structure with layouts
- self.view_paths = self.view_paths = [ActionView::FixtureResolver.new(
+ self.view_paths = [ActionView::FixtureResolver.new(
"render_action_with_application_layout/basic/hello_world.html.erb" => "Hello World!",
"render_action_with_application_layout/basic/hello.html.builder" => "xml.p 'Hello'",
"layouts/application.html.erb" => "Hi <%= yield %> OK, Bye",
@@ -202,7 +202,7 @@ end
module RenderActionWithControllerLayout
class BasicController < ActionController::Base
- self.view_paths = self.view_paths = [ActionView::FixtureResolver.new(
+ self.view_paths = [ActionView::FixtureResolver.new(
"render_action_with_controller_layout/basic/hello_world.html.erb" => "Hello World!",
"layouts/render_action_with_controller_layout/basic.html.erb" => "With Controller Layout! <%= yield %> Bye"
)]
diff --git a/actionpack/test/controller/new_base/render_rjs_test.rb b/actionpack/test/controller/new_base/render_rjs_test.rb
index f4516ade63..b602b9f8e9 100644
--- a/actionpack/test/controller/new_base/render_rjs_test.rb
+++ b/actionpack/test/controller/new_base/render_rjs_test.rb
@@ -5,8 +5,10 @@ module RenderRjs
self.view_paths = [ActionView::FixtureResolver.new(
"render_rjs/basic/index.js.rjs" => "page[:customer].replace_html render(:partial => 'customer')",
"render_rjs/basic/index_html.js.rjs" => "page[:customer].replace_html :partial => 'customer'",
+ "render_rjs/basic/index_no_js.js.erb" => "<%= render(:partial => 'developer') %>",
"render_rjs/basic/_customer.js.erb" => "JS Partial",
"render_rjs/basic/_customer.html.erb" => "HTML Partial",
+ "render_rjs/basic/_developer.html.erb" => "HTML Partial",
"render_rjs/basic/index_locale.js.rjs" => "page[:customer].replace_html :partial => 'customer'",
"render_rjs/basic/_customer.da.html.erb" => "Danish HTML Partial",
"render_rjs/basic/_customer.da.js.erb" => "Danish JS Partial"
@@ -16,6 +18,12 @@ module RenderRjs
render
end
+ def index_respond_to
+ respond_to do |format|
+ format.js { render :action => "index_no_js" }
+ end
+ end
+
def index_locale
self.locale = :da
end
@@ -37,6 +45,16 @@ module RenderRjs
assert_response("$(\"customer\").update(\"JS Partial\");")
end
+ test "rendering a partial in an RJS template should pick the HTML one if no JS is available" do
+ get :index_no_js, "format" => "js"
+ assert_response("HTML Partial")
+ end
+
+ test "rendering a partial in an RJS template should pick the HTML one if no JS is available on respond_to" do
+ get :index_respond_to, "format" => "js"
+ assert_response("HTML Partial")
+ end
+
test "replacing an element with a partial in an RJS template should pick the HTML template over the JS one" do
get :index_html, "format" => "js"
assert_response("$(\"customer\").update(\"HTML Partial\");")
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 20fcb87da6..2f3997518f 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -18,6 +18,13 @@ class TestController < ActionController::Base
layout :determine_layout
+ def name
+ nil
+ end
+
+ private :name
+ helper_method :name
+
def hello_world
end
@@ -418,7 +425,6 @@ class TestController < ActionController::Base
def rendering_with_conflicting_local_vars
@name = "David"
- def @template.name() nil end
render :action => "potential_conflicts"
end
@@ -507,10 +513,6 @@ class TestController < ActionController::Base
end
end
- def partial_only_with_layout
- render :partial => "partial_only", :layout => true
- end
-
def render_to_string_with_partial
@partial_only = render_to_string :partial => "partial_only"
@partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") }
@@ -526,11 +528,11 @@ class TestController < ActionController::Base
end
def partial_with_form_builder
- render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, @template, {}, Proc.new {})
+ render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}, Proc.new {})
end
def partial_with_form_builder_subclass
- render :partial => LabellingFormBuilder.new(:post, nil, @template, {}, Proc.new {})
+ render :partial => LabellingFormBuilder.new(:post, nil, view_context, {}, Proc.new {})
end
def partial_collection
@@ -634,8 +636,7 @@ class TestController < ActionController::Base
"rendering_nothing_on_layout", "render_text_hello_world",
"render_text_hello_world_with_layout",
"hello_world_with_layout_false",
- "partial_only", "partial_only_with_layout",
- "accessing_params_in_template",
+ "partial_only", "accessing_params_in_template",
"accessing_params_in_template_with_layout",
"render_with_explicit_template",
"render_with_explicit_string_template",
@@ -1198,11 +1199,6 @@ class RenderTest < ActionController::TestCase
assert_equal 'partial html', @response.body
end
- def test_partial_only_with_layout
- get :partial_only_with_layout
- assert_equal "<html>only partial</html>", @response.body
- end
-
def test_render_to_string_partial
get :render_to_string_with_partial
assert_equal "only partial", assigns(:partial_only)
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index f5fcf9b0df..c4e71a8689 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -18,14 +18,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
default_url_options :host => "rubyonrails.org"
controller :sessions do
- get 'login' => :new, :as => :login
+ get 'login' => :new
post 'login' => :create
-
- delete 'logout' => :destroy, :as => :logout
+ delete 'logout' => :destroy
end
resource :session do
get :create
+ post :reset
resource :info
end
@@ -34,6 +34,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'account/login', :to => redirect("/login")
match 'account/overview'
+ match '/account/nested/overview'
+ match 'sign_in' => "sessions#new"
match 'account/modulo/:name', :to => redirect("/%{name}s")
match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" }
@@ -120,7 +122,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
# misc
match 'articles/:year/:month/:day/:title', :to => "articles#show", :as => :article
+ # default params
+ match 'inline_pages/(:id)', :to => 'pages#show', :id => 'home'
+ match 'default_pages/(:id)', :to => 'pages#show', :defaults => { :id => 'home' }
+ defaults :id => 'home' do
+ match 'scoped_pages/(:id)', :to => 'pages#show'
+ end
+
namespace :account do
+ match 'shorthand'
match 'description', :to => "account#description", :as => "description"
resource :subscription, :credit, :credit_card
@@ -193,6 +203,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/login', url_for(:controller => 'sessions', :action => 'new', :only_path => true)
assert_equal 'http://rubyonrails.org/login', Routes.url_for(:controller => 'sessions', :action => 'create')
+ assert_equal 'http://rubyonrails.org/login', Routes.url_helpers.login_url
end
end
@@ -237,6 +248,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get '/session/edit'
assert_equal 'sessions#edit', @response.body
assert_equal '/session/edit', edit_session_path
+
+ post '/session/reset'
+ assert_equal 'sessions#reset', @response.body
+ assert_equal '/session/reset', reset_session_path
end
end
@@ -654,6 +669,30 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_convention_match_inside_namespace
+ with_test_routes do
+ assert_equal '/account/shorthand', account_shorthand_path
+ get '/account/shorthand'
+ assert_equal 'account#shorthand', @response.body
+ end
+ end
+
+ def test_convention_match_nested_and_with_leading_slash
+ with_test_routes do
+ assert_equal '/account/nested/overview', account_nested_overview_path
+ get '/account/nested/overview'
+ assert_equal 'account/nested#overview', @response.body
+ end
+ end
+
+ def test_convention_with_explicit_end
+ with_test_routes do
+ get '/sign_in'
+ assert_equal 'sessions#new', @response.body
+ assert_equal '/sign_in', sign_in_path
+ end
+ end
+
def test_redirect_with_complete_url
with_test_routes do
get '/account/google'
@@ -742,6 +781,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_default_params
+ with_test_routes do
+ get '/inline_pages'
+ assert_equal 'home', @request.params[:id]
+
+ get '/default_pages'
+ assert_equal 'home', @request.params[:id]
+
+ get '/scoped_pages'
+ assert_equal 'home', @request.params[:id]
+ end
+ end
+
private
def with_test_routes
yield
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
index 18b5b7ee00..326b336a1a 100644
--- a/actionpack/test/dispatch/url_generation_test.rb
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -34,5 +34,10 @@ module TestUrlGeneration
get "/foo", {}, 'SCRIPT_NAME' => "/new"
assert_equal "/new/foo", response.body
end
+
+ test "handling http protocol with https set" do
+ https!
+ assert_equal "http://www.example.com/foo", foo_url(:protocol => "http")
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/fixtures/functional_caching/_partial.erb b/actionpack/test/fixtures/functional_caching/_partial.erb
index d0e4f72b95..ec0da7cf50 100644
--- a/actionpack/test/fixtures/functional_caching/_partial.erb
+++ b/actionpack/test/fixtures/functional_caching/_partial.erb
@@ -1,3 +1,3 @@
<% cache do %>
-Fragment caching in a partial
-<% end %> \ No newline at end of file
+Old fragment caching in a partial
+<% end %>
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
index d7f43ad95e..9b88fa1f5a 100644
--- a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
@@ -1,3 +1,3 @@
<body>
-<% cache do %><p>ERB</p><% end %>
-</body> \ No newline at end of file
+<%= cache do %><p>ERB</p><% end %>
+</body>
diff --git a/actionpack/test/fixtures/functional_caching/fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/fragment_cached.html.erb
index 268a298a42..c479adb897 100644
--- a/actionpack/test/fixtures/functional_caching/fragment_cached.html.erb
+++ b/actionpack/test/fixtures/functional_caching/fragment_cached.html.erb
@@ -1,2 +1,2 @@
Hello
-<% cache do %>This bit's fragment cached<% end %>
+<%= cache do %>This bit's fragment cached<% end %>
diff --git a/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb
index 87309b8ccb..41647f1404 100644
--- a/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb
+++ b/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb
@@ -1,2 +1,2 @@
<%= render :inline => 'Some inline content' %>
-<% cache do %>Some cached content<% end %>
+<%= cache do %>Some cached content<% end %>
diff --git a/actionpack/test/fixtures/layouts/block_with_layout.erb b/actionpack/test/fixtures/layouts/block_with_layout.erb
index f25b41271d..73ac833e52 100644
--- a/actionpack/test/fixtures/layouts/block_with_layout.erb
+++ b/actionpack/test/fixtures/layouts/block_with_layout.erb
@@ -1,3 +1,3 @@
-<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
+<%= render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% "Return value should be discarded" %><% end %>
<%= yield %>
-<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
+<%= render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>
diff --git a/actionpack/test/fixtures/test/array_translation.erb b/actionpack/test/fixtures/test/array_translation.erb
new file mode 100644
index 0000000000..12c0763313
--- /dev/null
+++ b/actionpack/test/fixtures/test/array_translation.erb
@@ -0,0 +1 @@
+<%= t(['foo', 'bar']) %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/deprecated_nested_layout.erb b/actionpack/test/fixtures/test/deprecated_nested_layout.erb
new file mode 100644
index 0000000000..7b6dcbb6c7
--- /dev/null
+++ b/actionpack/test/fixtures/test/deprecated_nested_layout.erb
@@ -0,0 +1,3 @@
+<% content_for :title, "title" -%>
+<% content_for :column do -%>column<% end -%>
+<% render :layout => 'layouts/column' do -%>content<% end -%> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/nested_layout.erb b/actionpack/test/fixtures/test/nested_layout.erb
index 7b6dcbb6c7..6078f74b4c 100644
--- a/actionpack/test/fixtures/test/nested_layout.erb
+++ b/actionpack/test/fixtures/test/nested_layout.erb
@@ -1,3 +1,3 @@
<% content_for :title, "title" -%>
<% content_for :column do -%>column<% end -%>
-<% render :layout => 'layouts/column' do -%>content<% end -%> \ No newline at end of file
+<%= render :layout => 'layouts/column' do -%>content<% end -%> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/scoped_array_translation.erb b/actionpack/test/fixtures/test/scoped_array_translation.erb
new file mode 100644
index 0000000000..0a0c79f717
--- /dev/null
+++ b/actionpack/test/fixtures/test/scoped_array_translation.erb
@@ -0,0 +1 @@
+<%= t(['.foo', '.bar']) %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/using_layout_around_block.html.erb b/actionpack/test/fixtures/test/using_layout_around_block.html.erb
index a93fa02c9c..3d6661df9a 100644
--- a/actionpack/test/fixtures/test/using_layout_around_block.html.erb
+++ b/actionpack/test/fixtures/test/using_layout_around_block.html.erb
@@ -1 +1 @@
-<% render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %> \ No newline at end of file
+<%= render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %> \ No newline at end of file
diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb
index 02248d84ad..b49ccd39ca 100644
--- a/actionpack/test/lib/fixture_template.rb
+++ b/actionpack/test/lib/fixture_template.rb
@@ -9,17 +9,17 @@ module ActionView #:nodoc:
private
- def query(partial, path, exts)
+ def query(path, exts, formats)
query = Regexp.escape(path)
exts.each do |ext|
- query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << ')'
+ query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
end
templates = []
@hash.select { |k,v| k =~ /^#{query}$/ }.each do |path, source|
- handler, format = extract_handler_and_format(path)
+ handler, format = extract_handler_and_format(path, formats)
templates << Template.new(source, path, handler,
- :partial => partial, :virtual_path => path, :format => format)
+ :virtual_path => path, :format => format)
end
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb
index 371aa53c68..7a665b00bc 100644
--- a/actionpack/test/template/active_model_helper_test.rb
+++ b/actionpack/test/template/active_model_helper_test.rb
@@ -147,6 +147,20 @@ class ActiveModelHelperTest < ActionView::TestCase
)
end
+ def test_field_error_proc
+ old_proc = ActionView::Base.field_error_proc
+ ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
+ %(<div class=\"fieldWithErrors\">#{html_tag} <span class="error">#{[instance.error_message].join(', ')}</span></div>).html_safe
+ end
+
+ assert_dom_equal(
+ %(<div class="fieldWithErrors"><input id="post_author_name" name="post[author_name]" size="30" type="text" value="" /> <span class="error">can't be empty</span></div>),
+ text_field("post", "author_name")
+ )
+ ensure
+ ActionView::Base.field_error_proc = old_proc if old_proc
+ end
+
def test_form_with_string
assert_dom_equal(
%(<form action="create" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
@@ -252,6 +266,10 @@ class ActiveModelHelperTest < ActionView::TestCase
assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
end
+ def test_error_message_on_handles_empty_errors
+ assert_equal "", error_message_on(@post, :tag)
+ end
+
def test_error_messages_for_many_objects
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li><li>User email can't be empty</li></ul></div>), error_messages_for("post", "user")
diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb
index defe85107e..69cf684083 100644
--- a/actionpack/test/template/body_parts_test.rb
+++ b/actionpack/test/template/body_parts_test.rb
@@ -8,9 +8,8 @@ class BodyPartsTest < ActionController::TestCase
def index
RENDERINGS.each do |rendering|
- @template.punctuate_body! rendering
+ view_context.punctuate_body! rendering
end
- @performed_render = true
end
end
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index f887c9ab5b..bf541c17d3 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -3,9 +3,33 @@ require 'abstract_unit'
class CaptureHelperTest < ActionView::TestCase
def setup
super
+ @av = ActionView::Base.new
@_content_for = Hash.new {|h,k| h[k] = "" }
end
+ def test_capture_captures_the_temporary_output_buffer_in_its_block
+ assert_nil @av.output_buffer
+ string = @av.capture do
+ @av.output_buffer << 'foo'
+ @av.output_buffer << 'bar'
+ end
+ assert_nil @av.output_buffer
+ assert_equal 'foobar', string
+ assert_kind_of ActionView::NonConcattingString, string
+ end
+
+ def test_capture_captures_the_value_returned_by_the_block_if_the_temporary_buffer_is_blank
+ string = @av.capture('foo', 'bar') do |a, b|
+ a + b
+ end
+ assert_equal 'foobar', string
+ assert_kind_of ActionView::NonConcattingString, string
+ end
+
+ def test_capture_returns_nil_if_the_returned_value_is_not_a_string
+ assert_nil @av.capture { 1 }
+ end
+
def test_content_for
assert ! content_for?(:title)
content_for :title, 'title'
@@ -13,9 +37,85 @@ class CaptureHelperTest < ActionView::TestCase
assert ! content_for?(:something_else)
end
+ def test_with_output_buffer_swaps_the_output_buffer_given_no_argument
+ assert_nil @av.output_buffer
+ buffer = @av.with_output_buffer do
+ @av.output_buffer << '.'
+ end
+ assert_equal '.', buffer
+ assert_nil @av.output_buffer
+ end
+
+ def test_with_output_buffer_swaps_the_output_buffer_with_an_argument
+ assert_nil @av.output_buffer
+ buffer = ActionView::OutputBuffer.new('.')
+ @av.with_output_buffer(buffer) do
+ @av.output_buffer << '.'
+ end
+ assert_equal '..', buffer
+ assert_nil @av.output_buffer
+ end
+
+ def test_with_output_buffer_restores_the_output_buffer
+ buffer = ActionView::OutputBuffer.new
+ @av.output_buffer = buffer
+ @av.with_output_buffer do
+ @av.output_buffer << '.'
+ end
+ assert buffer.equal?(@av.output_buffer)
+ end
+
+ unless RUBY_VERSION < '1.9'
+ def test_with_output_buffer_sets_proper_encoding
+ @av.output_buffer = ActionView::OutputBuffer.new
+
+ # Ensure we set the output buffer to an encoding different than the default one.
+ alt_encoding = alt_encoding(@av.output_buffer)
+ @av.output_buffer.force_encoding(alt_encoding)
+
+ @av.with_output_buffer do
+ assert alt_encoding, @av.output_buffer.encoding
+ end
+ end
+ end
+
def test_with_output_buffer_does_not_assume_there_is_an_output_buffer
- av = ActionView::Base.new
- assert_nil av.output_buffer
- assert_equal "", av.with_output_buffer {}
+ assert_nil @av.output_buffer
+ assert_equal "", @av.with_output_buffer {}
+ end
+
+ def test_flush_output_buffer_concats_output_buffer_to_response
+ view = view_with_controller
+ assert_equal [], view.response.body_parts
+
+ view.output_buffer << 'OMG'
+ view.flush_output_buffer
+ assert_equal ['OMG'], view.response.body_parts
+ assert_equal '', view.output_buffer
+
+ view.output_buffer << 'foobar'
+ view.flush_output_buffer
+ assert_equal ['OMG', 'foobar'], view.response.body_parts
+ assert_equal '', view.output_buffer
+ end
+
+ unless RUBY_VERSION < '1.9'
+ def test_flush_output_buffer_preserves_the_encoding_of_the_output_buffer
+ view = view_with_controller
+ alt_encoding = alt_encoding(view.output_buffer)
+ view.output_buffer.force_encoding(alt_encoding)
+ flush_output_buffer
+ assert_equal alt_encoding, view.output_buffer.encoding
+ end
+ end
+
+ def alt_encoding(output_buffer)
+ output_buffer.encoding == Encoding::US_ASCII ? Encoding::UTF_8 : Encoding::US_ASCII
+ end
+
+ def view_with_controller
+ returning(TestController.new.view_context) do |view|
+ view.output_buffer = ActionView::OutputBuffer.new
+ end
end
end
diff --git a/actionpack/test/template/erb/form_for_test.rb b/actionpack/test/template/erb/form_for_test.rb
new file mode 100644
index 0000000000..482dbb0287
--- /dev/null
+++ b/actionpack/test/template/erb/form_for_test.rb
@@ -0,0 +1,11 @@
+require "abstract_unit"
+require "template/erb/helper"
+
+module ERBTest
+ class TagHelperTest < BlockTestCase
+ test "form_for works" do
+ output = render_content "form_for(:staticpage, :url => {:controller => 'blah', :action => 'update'})", ""
+ assert_equal "<form action=\"/blah/update\" method=\"post\"></form>", output
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/erb/helper.rb b/actionpack/test/template/erb/helper.rb
new file mode 100644
index 0000000000..7147178849
--- /dev/null
+++ b/actionpack/test/template/erb/helper.rb
@@ -0,0 +1,30 @@
+module ERBTest
+ class ViewContext
+ mock_controller = Class.new do
+ include SharedTestRoutes.url_helpers
+ end
+
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::FormHelper
+
+ attr_accessor :output_buffer
+
+ def protect_against_forgery?() false end
+
+ define_method(:controller) do
+ mock_controller.new
+ end
+ end
+
+ class BlockTestCase < ActiveSupport::TestCase
+ def render_content(start, inside)
+ template = block_helper(start, inside)
+ ActionView::Template::Handlers::Erubis.new(template).evaluate(ViewContext.new)
+ end
+
+ def block_helper(str, rest)
+ "<%= #{str} do %>#{rest}<% end %>"
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/erb/tag_helper_test.rb b/actionpack/test/template/erb/tag_helper_test.rb
index cc96a43901..64a88bde1d 100644
--- a/actionpack/test/template/erb/tag_helper_test.rb
+++ b/actionpack/test/template/erb/tag_helper_test.rb
@@ -1,66 +1,44 @@
require "abstract_unit"
+require "template/erb/helper"
module ERBTest
- class ViewContext
- mock_controller = Class.new do
- include SharedTestRoutes.url_helpers
- end
-
- include ActionView::Helpers::TagHelper
- include ActionView::Helpers::JavaScriptHelper
- include ActionView::Helpers::FormHelper
-
- attr_accessor :output_buffer
-
- def protect_against_forgery?() false end
-
- define_method(:controller) do
- mock_controller.new
- end
- end
-
- class DeprecatedViewContext < ViewContext
- include ActionView::Helpers::DeprecatedBlockHelpers
- end
-
module SharedTagHelpers
extend ActiveSupport::Testing::Declarative
- def render_content(start, inside)
- template = block_helper(start, inside)
- ActionView::Template::Handlers::Erubis.new(template).evaluate(context.new)
+ def maybe_deprecated
+ if @deprecated
+ assert_deprecated { yield }
+ else
+ yield
+ end
end
test "percent equals works for content_tag and does not require parenthesis on method call" do
- assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world")
+ maybe_deprecated { assert_equal "<div>Hello world</div>", render_content("content_tag :div", "Hello world") }
end
test "percent equals works for javascript_tag" do
expected_output = "<script type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
- assert_equal expected_output, render_content("javascript_tag", "alert('Hello')")
+ maybe_deprecated { assert_equal expected_output, render_content("javascript_tag", "alert('Hello')") }
end
test "percent equals works for javascript_tag with options" do
expected_output = "<script id=\"the_js_tag\" type=\"text/javascript\">\n//<![CDATA[\nalert('Hello')\n//]]>\n</script>"
- assert_equal expected_output, render_content("javascript_tag(:id => 'the_js_tag')", "alert('Hello')")
+ maybe_deprecated { assert_equal expected_output, render_content("javascript_tag(:id => 'the_js_tag')", "alert('Hello')") }
end
test "percent equals works with form tags" do
expected_output = "<form action=\"foo\" method=\"post\">hello</form>"
- assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>")
+ maybe_deprecated { assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>") }
end
test "percent equals works with fieldset tags" do
expected_output = "<fieldset><legend>foo</legend>hello</fieldset>"
- assert_equal expected_output, render_content("field_set_tag('foo')", "<%= 'hello' %>")
+ maybe_deprecated { assert_equal expected_output, render_content("field_set_tag('foo')", "<%= 'hello' %>") }
end
end
- class TagHelperTest < ActiveSupport::TestCase
- def context
- ViewContext
- end
-
+ class TagHelperTest < BlockTestCase
def block_helper(str, rest)
"<%= #{str} do %>#{rest}<% end %>"
end
@@ -68,15 +46,15 @@ module ERBTest
include SharedTagHelpers
end
- class DeprecatedTagHelperTest < ActiveSupport::TestCase
- def context
- DeprecatedViewContext
- end
-
+ class DeprecatedTagHelperTest < BlockTestCase
def block_helper(str, rest)
"<% __in_erb_template=true %><% #{str} do %>#{rest}<% end %>"
end
+ def setup
+ @deprecated = true
+ end
+
include SharedTagHelpers
end
end \ No newline at end of file
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index f49b763881..c5c2a6b952 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -17,7 +17,7 @@ class JavaScriptHelperTest < ActionView::TestCase
ActiveSupport.escape_html_entities_in_json = true
@template = self
end
-
+
def teardown
ActiveSupport.escape_html_entities_in_json = false
end
@@ -60,6 +60,35 @@ class JavaScriptHelperTest < ActionView::TestCase
button_to_function("Greeting")
end
+ def test_link_to_function
+ assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')")
+ end
+
+ def test_link_to_function_with_existing_onclick
+ assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
+ end
+
+ def test_link_to_function_with_rjs_block
+ html = link_to_function( "Greet me!" ) do |page|
+ page.replace_html 'header', "<h1>Greetings</h1>"
+ end
+ assert_dom_equal %(<a href="#" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
+ end
+
+ def test_link_to_function_with_rjs_block_and_options
+ html = link_to_function( "Greet me!", :class => "updater" ) do |page|
+ page.replace_html 'header', "<h1>Greetings</h1>"
+ end
+ assert_dom_equal %(<a href="#" class="updater" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
+ end
+
+ def test_link_to_function_with_href
+ assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+ end
+
def test_javascript_tag
self.output_buffer = 'foo'
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index 697ebc694a..df1aa2edb2 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -22,34 +22,35 @@ class LookupContextTest < ActiveSupport::TestCase
end
test "normalizes details on initialization" do
- formats = Mime::SET + [nil]
- locale = [I18n.locale, nil]
- assert_equal Hash[:formats => formats, :locale => locale], @lookup_context.details
- end
-
- test "allows me to set details" do
- @lookup_context.details = { :formats => [:html], :locale => :pt }
- assert_equal Hash[:formats => [:html, nil], :locale => [:pt, nil]], @lookup_context.details
+ assert_equal Mime::SET, @lookup_context.formats
+ assert_equal :en, @lookup_context.locale
end
- test "does not allow details to be modified in place" do
- assert @lookup_context.details.frozen?
+ test "allows me to update details" do
+ @lookup_context.update_details(:formats => [:html], :locale => :pt)
+ assert_equal [:html], @lookup_context.formats
+ assert_equal :pt, @lookup_context.locale
end
test "allows me to update an specific detail" do
@lookup_context.update_details(:locale => :pt)
assert_equal :pt, I18n.locale
- formats = Mime::SET + [nil]
- locale = [I18n.locale, nil]
- assert_equal Hash[:formats => formats, :locale => locale], @lookup_context.details
+ assert_equal :pt, @lookup_context.locale
+ end
+
+ test "allows me to freeze and retrieve frozen formats" do
+ @lookup_context.formats.freeze
+ assert @lookup_context.formats.frozen?
end
test "allows me to change some details to execute an specific block of code" do
- formats = Mime::SET + [nil]
+ formats = Mime::SET
@lookup_context.update_details(:locale => :pt) do
- assert_equal Hash[:formats => formats, :locale => [:pt, nil]], @lookup_context.details
+ assert_equal formats, @lookup_context.formats
+ assert_equal :pt, @lookup_context.locale
end
- assert_equal Hash[:formats => formats, :locale => [:en, nil]], @lookup_context.details
+ assert_equal formats, @lookup_context.formats
+ assert_equal :en, @lookup_context.locale
end
test "provides getters and setters for formats" do
@@ -62,6 +63,11 @@ class LookupContextTest < ActiveSupport::TestCase
assert_equal Mime::SET, @lookup_context.formats
end
+ test "adds :html fallback to :js formats" do
+ @lookup_context.formats = [:js]
+ assert_equal [:js, :html], @lookup_context.formats
+ end
+
test "provides getters and setters for locale" do
@lookup_context.locale = :pt
assert_equal :pt, @lookup_context.locale
@@ -94,6 +100,13 @@ class LookupContextTest < ActiveSupport::TestCase
assert_equal "Hey verden", template.source
end
+ test "found templates respects given formats if one cannot be found from template or handler" do
+ ActionView::Template::Handlers::ERB.expects(:default_format).returns(nil)
+ @lookup_context.formats = [:text]
+ template = @lookup_context.find("hello_world", "test")
+ assert_equal [:text], template.formats
+ end
+
test "adds fallbacks to view paths when required" do
assert_equal 1, @lookup_context.view_paths.size
diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb
index bf5b81292f..f730a0d7f5 100644
--- a/actionpack/test/template/number_helper_i18n_test.rb
+++ b/actionpack/test/template/number_helper_i18n_test.rb
@@ -1,69 +1,95 @@
require 'abstract_unit'
-class NumberHelperI18nTests < Test::Unit::TestCase
- include ActionView::Helpers::NumberHelper
-
- attr_reader :request
+class NumberHelperTest < ActionView::TestCase
+ tests ActionView::Helpers::NumberHelper
def setup
- @number_defaults = { :precision => 3, :delimiter => ',', :separator => '.' }
- @currency_defaults = { :unit => '$', :format => '%u%n', :precision => 2 }
- @human_defaults = { :precision => 1 }
- @human_storage_units_format_default = "%n %u"
- @human_storage_units_units_byte_other = "Bytes"
- @human_storage_units_units_kb_other = "KB"
- @percentage_defaults = { :delimiter => '' }
- @precision_defaults = { :delimiter => '' }
+ I18n.backend.store_translations 'ts',
+ :number => {
+ :format => { :precision => 3, :delimiter => ',', :separator => '.', :significant => false, :strip_insignificant_zeros => false },
+ :currency => { :format => { :unit => '&$', :format => '%u - %n', :precision => 2 } },
+ :human => {
+ :format => {
+ :precision => 2,
+ :significant => true,
+ :strip_insignificant_zeros => true
+ },
+ :storage_units => {
+ :format => "%n %u",
+ :units => {
+ :byte => "b",
+ :kb => "k"
+ }
+ },
+ :decimal_units => {
+ :format => "%n %u",
+ :units => {
+ :deci => {:one => "Tenth", :other => "Tenths"},
+ :unit => "u",
+ :ten => {:one => "Ten", :other => "Tens"},
+ :thousand => "t",
+ :million => "m" ,
+ :billion =>"b" ,
+ :trillion =>"t" ,
+ :quadrillion =>"q"
+ }
+ }
+ },
+ :percentage => { :format => {:delimiter => '', :precision => 2, :strip_insignificant_zeros => true} },
+ :precision => { :format => {:delimiter => '', :significant => true} }
+ },
+ :custom_units_for_number_to_human => {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"}
+ end
- I18n.backend.store_translations 'en', :number => { :format => @number_defaults,
- :currency => { :format => @currency_defaults }, :human => @human_defaults }
+ def test_number_to_currency
+ assert_equal("&$ - 10.00", number_to_currency(10, :locale => 'ts'))
end
- def test_number_to_currency_translates_currency_formats
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- I18n.expects(:translate).with(:'number.currency.format', :locale => 'en',
- :raise => true).returns(@currency_defaults)
- number_to_currency(1, :locale => 'en')
+ def test_number_with_precision
+ #Delimiter was set to ""
+ assert_equal("10000", number_with_precision(10000, :locale => 'ts'))
+
+ #Precision inherited and significant was set
+ assert_equal("1.00", number_with_precision(1.0, :locale => 'ts'))
+
end
- def test_number_with_precision_translates_number_formats
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- I18n.expects(:translate).with(:'number.precision.format', :locale => 'en',
- :raise => true).returns(@precision_defaults)
- number_with_precision(1, :locale => 'en')
+ def test_number_with_delimiter
+ #Delimiter "," and separator "."
+ assert_equal("1,000,000.234", number_with_delimiter(1000000.234, :locale => 'ts'))
end
- def test_number_with_delimiter_translates_number_formats
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- number_with_delimiter(1, :locale => 'en')
+ def test_number_to_percentage
+ # to see if strip_insignificant_zeros is true
+ assert_equal("1%", number_to_percentage(1, :locale => 'ts'))
+ # precision is 2, significant should be inherited
+ assert_equal("1.24%", number_to_percentage(1.2434, :locale => 'ts'))
+ # no delimiter
+ assert_equal("12434%", number_to_percentage(12434, :locale => 'ts'))
end
- def test_number_to_percentage_translates_number_formats
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- I18n.expects(:translate).with(:'number.percentage.format', :locale => 'en',
- :raise => true).returns(@percentage_defaults)
- number_to_percentage(1, :locale => 'en')
+ def test_number_to_human_size
+ #b for bytes and k for kbytes
+ assert_equal("2 k", number_to_human_size(2048, :locale => 'ts'))
+ assert_equal("42 b", number_to_human_size(42, :locale => 'ts'))
end
- def test_number_to_human_size_translates_human_formats
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- I18n.expects(:translate).with(:'number.human.format', :locale => 'en',
- :raise => true).returns(@human_defaults)
- I18n.expects(:translate).with(:'number.human.storage_units.format', :locale => 'en',
- :raise => true).returns(@human_storage_units_format_default)
- I18n.expects(:translate).with(:'number.human.storage_units.units.kb', :locale => 'en', :count => 2,
- :raise => true).returns(@human_storage_units_units_kb_other)
- # 2KB
- number_to_human_size(2048, :locale => 'en')
+ def test_number_to_human_with_default_translation_scope
+ #Using t for thousand
+ assert_equal "2 t", number_to_human(2000, :locale => 'ts')
+ #Significant was set to true with precision 2, using b for billion
+ assert_equal "1.2 b", number_to_human(1234567890, :locale => 'ts')
+ #Using pluralization (Ten/Tens and Tenth/Tenths)
+ assert_equal "1 Tenth", number_to_human(0.1, :locale => 'ts')
+ assert_equal "1.3 Tenth", number_to_human(0.134, :locale => 'ts')
+ assert_equal "2 Tenths", number_to_human(0.2, :locale => 'ts')
+ assert_equal "1 Ten", number_to_human(10, :locale => 'ts')
+ assert_equal "1.2 Ten", number_to_human(12, :locale => 'ts')
+ assert_equal "2 Tens", number_to_human(20, :locale => 'ts')
+ end
- I18n.expects(:translate).with(:'number.format', :locale => 'en', :raise => true).returns(@number_defaults)
- I18n.expects(:translate).with(:'number.human.format', :locale => 'en',
- :raise => true).returns(@human_defaults)
- I18n.expects(:translate).with(:'number.human.storage_units.format', :locale => 'en',
- :raise => true).returns(@human_storage_units_format_default)
- I18n.expects(:translate).with(:'number.human.storage_units.units.byte', :locale => 'en', :count => 42,
- :raise => true).returns(@human_storage_units_units_byte_other)
- # 42 Bytes
- number_to_human_size(42, :locale => 'en')
+ def test_number_to_human_with_custom_translation_scope
+ #Significant was set to true with precision 2, with custom translated units
+ assert_equal "4.3 cm", number_to_human(0.0432, :locale => 'ts', :units => :custom_units_for_number_to_human)
end
end
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 0a2b82bd89..50c57a5588 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -19,6 +19,15 @@ class NumberHelperTest < ActionView::TestCase
gigabytes(number) * 1024
end
+ def silence_deprecation_warnings
+ @old_deprecatios_silenced = ActiveSupport::Deprecation.silenced
+ ActiveSupport::Deprecation.silenced = true
+ end
+
+ def restore_deprecation_warnings
+ ActiveSupport::Deprecation.silenced = @old_deprecatios_silenced
+ end
+
def test_number_to_phone
assert_equal("555-1234", number_to_phone(5551234))
assert_equal("800-555-1212", number_to_phone(8005551212))
@@ -31,8 +40,6 @@ 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("x", number_to_phone("x"))
- assert_nil number_to_phone(nil)
end
def test_number_to_currency
@@ -43,9 +50,6 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("&pound;1234567890,50", number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}))
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("$x.", number_to_currency("x")) # fails due to API consolidation
- assert_equal("$x", number_to_currency("x"))
- assert_nil number_to_currency(nil)
end
def test_number_to_percentage
@@ -54,9 +58,8 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2}))
assert_equal("100.000%", number_to_percentage("100"))
assert_equal("1000.000%", number_to_percentage("1000"))
- assert_equal("x%", number_to_percentage("x"))
+ 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_nil number_to_percentage(nil)
end
def test_number_with_delimiter
@@ -70,8 +73,6 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("123,456,789.78901", number_with_delimiter(123456789.78901))
assert_equal("0.78901", number_with_delimiter(0.78901))
assert_equal("123,456.78", number_with_delimiter("123456.78"))
- assert_equal("x", number_with_delimiter("x"))
- assert_nil number_with_delimiter(nil)
end
def test_number_with_delimiter_with_options_hash
@@ -81,6 +82,16 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',')
end
+ def test_number_with_delimiter_old_api
+ silence_deprecation_warnings
+ assert_equal '12 345 678', number_with_delimiter(12345678, " ")
+ assert_equal '12-345-678.05', number_with_delimiter(12345678.05, '-')
+ assert_equal '12.345.678,05', number_with_delimiter(12345678.05, '.', ',')
+ assert_equal '12,345,678.05', number_with_delimiter(12345678.05, ',', '.')
+ assert_equal '12 345 678-05', number_with_delimiter(12345678.05, ',', '.', :delimiter => ' ', :separator => '-')
+ restore_deprecation_warnings
+ end
+
def test_number_with_precision
assert_equal("111.235", number_with_precision(111.2346))
assert_equal("31.83", number_with_precision(31.825, :precision => 2))
@@ -91,10 +102,6 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("3268", number_with_precision((32.6751 * 100.00), :precision => 0))
assert_equal("112", number_with_precision(111.50, :precision => 0))
assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0))
-
- # Return non-numeric params unchanged.
- assert_equal("x", number_with_precision("x"))
- assert_nil number_with_precision(nil)
end
def test_number_with_precision_with_custom_delimiter_and_separator
@@ -102,48 +109,272 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '1.231,83', number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.')
end
+ def test_number_with_precision_with_significant_digits
+ assert_equal "124000", number_with_precision(123987, :precision => 3, :significant => true)
+ assert_equal "120000000", number_with_precision(123987876, :precision => 2, :significant => true )
+ assert_equal "40000", number_with_precision("43523", :precision => 1, :significant => true )
+ assert_equal "9775", number_with_precision(9775, :precision => 4, :significant => true )
+ assert_equal "5.4", number_with_precision(5.3923, :precision => 2, :significant => true )
+ assert_equal "5", number_with_precision(5.3923, :precision => 1, :significant => true )
+ assert_equal "1", number_with_precision(1.232, :precision => 1, :significant => true )
+ assert_equal "7", number_with_precision(7, :precision => 1, :significant => true )
+ assert_equal "1", number_with_precision(1, :precision => 1, :significant => true )
+ assert_equal "53", number_with_precision(52.7923, :precision => 2, :significant => true )
+ assert_equal "9775.00", number_with_precision(9775, :precision => 6, :significant => true )
+ assert_equal "5.392900", number_with_precision(5.3929, :precision => 7, :significant => true )
+ end
+
+ def test_number_with_precision_with_strip_insignificant_zeros
+ assert_equal "9775.43", number_with_precision(9775.43, :precision => 4, :strip_insignificant_zeros => true )
+ assert_equal "9775.2", number_with_precision(9775.2, :precision => 6, :significant => true, :strip_insignificant_zeros => true )
+ end
+
+ def test_number_with_precision_with_significant_true_and_zero_precision
+ # Zero precision with significant is a mistake (would always return zero),
+ # so we treat it as if significant was false (increases backwards compatibily for number_to_human_size)
+ assert_equal "124", number_with_precision(123.987, :precision => 0, :significant => true)
+ assert_equal "12", number_with_precision(12, :precision => 0, :significant => true )
+ assert_equal "12", number_with_precision("12.3", :precision => 0, :significant => true )
+ end
+
+ def test_number_with_precision_old_api
+ silence_deprecation_warnings
+ assert_equal("31.8250", number_with_precision(31.825, 4))
+ assert_equal("111.235", number_with_precision(111.2346, 3))
+ assert_equal("111.00", number_with_precision(111, 2))
+ assert_equal("111.000", number_with_precision(111, 2, :precision =>3))
+ restore_deprecation_warnings
+ end
+
def test_number_to_human_size
assert_equal '0 Bytes', number_to_human_size(0)
assert_equal '1 Byte', number_to_human_size(1)
assert_equal '3 Bytes', number_to_human_size(3.14159265)
assert_equal '123 Bytes', number_to_human_size(123.0)
assert_equal '123 Bytes', number_to_human_size(123)
- assert_equal '1.2 KB', number_to_human_size(1234)
+ assert_equal '1.21 KB', number_to_human_size(1234)
assert_equal '12.1 KB', number_to_human_size(12345)
- assert_equal '1.2 MB', number_to_human_size(1234567)
- assert_equal '1.1 GB', number_to_human_size(1234567890)
- assert_equal '1.1 TB', number_to_human_size(1234567890123)
- assert_equal '1025 TB', number_to_human_size(terabytes(1025))
+ assert_equal '1.18 MB', number_to_human_size(1234567)
+ assert_equal '1.15 GB', number_to_human_size(1234567890)
+ assert_equal '1.12 TB', number_to_human_size(1234567890123)
+ assert_equal '1030 TB', number_to_human_size(terabytes(1026))
assert_equal '444 KB', number_to_human_size(kilobytes(444))
- assert_equal '1023 MB', number_to_human_size(megabytes(1023))
+ assert_equal '1020 MB', number_to_human_size(megabytes(1023))
assert_equal '3 TB', number_to_human_size(terabytes(3))
- assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2)
+ assert_equal '1.2 MB', number_to_human_size(1234567, :precision => 2)
assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
- assert_equal("123 Bytes", number_to_human_size("123"))
- assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
+ assert_equal '123 Bytes', number_to_human_size('123')
+ assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4)
assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4)
assert_equal '1 Byte', number_to_human_size(1.1)
assert_equal '10 Bytes', number_to_human_size(10)
- #assert_nil number_to_human_size('x') # fails due to API consolidation
- assert_nil number_to_human_size(nil)
end
def test_number_to_human_size_with_options_hash
- assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2)
+ assert_equal '1.2 MB', number_to_human_size(1234567, :precision => 2)
assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
- assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
+ assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 2)
assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4)
assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4)
- assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 0)
- assert_equal '500 MB', number_to_human_size(524288000, :precision=>0)
- assert_equal '40 KB', number_to_human_size(41010, :precision => 0)
- assert_equal '40 KB', number_to_human_size(41100, :precision => 0)
+ assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 1)
+ assert_equal '500 MB', number_to_human_size(524288000, :precision=>3)
+ assert_equal '40 KB', number_to_human_size(41010, :precision => 1)
+ assert_equal '40 KB', number_to_human_size(41100, :precision => 2)
+ 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
end
def test_number_to_human_size_with_custom_delimiter_and_separator
- assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :separator => ',')
+ assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :separator => ',')
assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4, :separator => ',')
- assert_equal '1.000,1 TB', number_to_human_size(terabytes(1000.1), :delimiter => '.', :separator => ',')
+ assert_equal '1.000,1 TB', number_to_human_size(terabytes(1000.1), :precision => 5, :delimiter => '.', :separator => ',')
+ end
+
+ def test_number_to_human_size_old_api
+ silence_deprecation_warnings
+ assert_equal '1.3143 KB', number_to_human_size(kilobytes(1.3143), 4, :significant => false)
+ assert_equal '10.45 KB', number_to_human_size(kilobytes(10.453), 4)
+ assert_equal '10 KB', number_to_human_size(kilobytes(10.453), 4, :precision => 2)
+ restore_deprecation_warnings
+ end
+
+ def test_number_to_human
+ assert_equal '123', number_to_human(123)
+ assert_equal '1.23 Thousand', number_to_human(1234)
+ assert_equal '12.3 Thousand', number_to_human(12345)
+ assert_equal '1.23 Million', number_to_human(1234567)
+ assert_equal '1.23 Billion', number_to_human(1234567890)
+ assert_equal '1.23 Trillion', number_to_human(1234567890123)
+ assert_equal '1.23 Quadrillion', number_to_human(1234567890123456)
+ assert_equal '1230 Quadrillion', number_to_human(1234567890123456789)
+ assert_equal '490 Thousand', number_to_human(489939, :precision => 2)
+ assert_equal '489.9 Thousand', number_to_human(489939, :precision => 4)
+ assert_equal '489 Thousand', number_to_human(489000, :precision => 4)
+ assert_equal '489.0 Thousand', number_to_human(489000, :precision => 4, :strip_insignificant_zeros => false)
+ assert_equal '1.2346 Million', number_to_human(1234567, :precision => 4, :significant => false)
+ assert_equal '1,2 Million', number_to_human(1234567, :precision => 1, :significant => false, :separator => ',')
+ assert_equal '1 Million', number_to_human(1234567, :precision => 0, :significant => true, :separator => ',') #significant forced to false
+ end
+
+ def test_number_to_human_with_custom_units
+ #Only integers
+ volume = {:unit => "ml", :thousand => "lt", :million => "m3"}
+ assert_equal '123 lt', number_to_human(123456, :units => volume)
+ assert_equal '12 ml', number_to_human(12, :units => volume)
+ assert_equal '1.23 m3', number_to_human(1234567, :units => volume)
+
+ #Including fractionals
+ distance = {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"}
+ assert_equal '1.23 mm', number_to_human(0.00123, :units => distance)
+ assert_equal '1.23 cm', number_to_human(0.0123, :units => distance)
+ assert_equal '1.23 dm', number_to_human(0.123, :units => distance)
+ assert_equal '1.23 m', number_to_human(1.23, :units => distance)
+ assert_equal '1.23 dam', number_to_human(12.3, :units => distance)
+ assert_equal '1.23 hm', number_to_human(123, :units => distance)
+ assert_equal '1.23 km', number_to_human(1230, :units => distance)
+ assert_equal '1.23 km', number_to_human(1230, :units => distance)
+ assert_equal '1.23 km', number_to_human(1230, :units => distance)
+ assert_equal '12.3 km', number_to_human(12300, :units => distance)
+
+ #The quantifiers don't need to be a continuous sequence
+ gangster = {:hundred => "hundred bucks", :million => "thousand quids"}
+ assert_equal '1 hundred bucks', number_to_human(100, :units => gangster)
+ assert_equal '25 hundred bucks', number_to_human(2500, :units => gangster)
+ assert_equal '25 thousand quids', number_to_human(25000000, :units => gangster)
+ assert_equal '12300 thousand quids', number_to_human(12345000000, :units => gangster)
+
+ #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 '})
end
+
+ def test_number_to_human_with_custom_format
+ assert_equal '123 times Thousand', number_to_human(123456, :format => "%n times %u")
+ volume = {:unit => "ml", :thousand => "lt", :million => "m3"}
+ assert_equal '123.lt', number_to_human(123456, :units => volume, :format => "%n.%u")
+ end
+
+ def test_number_helpers_should_return_nil_when_given_nil
+ assert_nil number_to_phone(nil)
+ assert_nil number_to_currency(nil)
+ assert_nil number_to_percentage(nil)
+ assert_nil number_with_delimiter(nil)
+ assert_nil number_with_precision(nil)
+ assert_nil number_to_human_size(nil)
+ assert_nil number_to_human(nil)
+ end
+
+ def test_number_helpers_should_return_non_numeric_param_unchanged
+ assert_equal("+1-x x 123", number_to_phone("x", :country_code => 1, :extension => 123))
+ assert_equal("x", number_to_phone("x"))
+ assert_equal("$x.", number_to_currency("x."))
+ assert_equal("$x", number_to_currency("x"))
+ assert_equal("x%", number_to_percentage("x"))
+ assert_equal("x", number_with_delimiter("x"))
+ assert_equal("x.", number_with_precision("x."))
+ assert_equal("x", number_with_precision("x"))
+ assert_equal "x", number_to_human_size('x')
+ assert_equal "x", number_to_human('x')
+ end
+
+ def test_number_helpers_outputs_are_html_safe
+ assert number_to_human(1).html_safe?
+ assert !number_to_human("<script></script>").html_safe?
+ assert number_to_human("asdf".html_safe).html_safe?
+
+ assert number_to_human_size(1).html_safe?
+ assert number_to_human_size(1000000).html_safe?
+ assert !number_to_human_size("<script></script>").html_safe?
+ assert number_to_human_size("asdf".html_safe).html_safe?
+
+ assert number_with_precision(1, :strip_insignificant_zeros => false).html_safe?
+ assert number_with_precision(1, :strip_insignificant_zeros => true).html_safe?
+ assert !number_with_precision("<script></script>").html_safe?
+ assert number_with_precision("asdf".html_safe).html_safe?
+
+ assert number_to_currency(1).html_safe?
+ assert !number_to_currency("<script></script>").html_safe?
+ assert number_to_currency("asdf".html_safe).html_safe?
+
+ assert number_to_percentage(1).html_safe?
+ assert !number_to_percentage("<script></script>").html_safe?
+ assert number_to_percentage("asdf".html_safe).html_safe?
+
+ assert number_to_phone(1).html_safe?
+ assert !number_to_phone("<script></script>").html_safe?
+ assert number_to_phone("asdf".html_safe).html_safe?
+
+ assert number_with_delimiter(1).html_safe?
+ assert !number_with_delimiter("<script></script>").html_safe?
+ assert number_with_delimiter("asdf".html_safe).html_safe?
+ end
+
+ def test_number_helpers_should_raise_error_if_invalid_when_specified
+ assert_raise InvalidNumberError do
+ number_to_human("x", :raise => true)
+ end
+ begin
+ number_to_human("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_to_human_size("x", :raise => true)
+ end
+ begin
+ number_to_human_size("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_with_precision("x", :raise => true)
+ end
+ begin
+ number_with_precision("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_to_currency("x", :raise => true)
+ end
+ begin
+ number_with_precision("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_to_percentage("x", :raise => true)
+ end
+ begin
+ number_to_percentage("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_with_delimiter("x", :raise => true)
+ end
+ begin
+ number_with_delimiter("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ assert_raise InvalidNumberError do
+ number_to_phone("x", :raise => true)
+ end
+ begin
+ number_to_phone("x", :raise => true)
+ rescue InvalidNumberError => e
+ assert_equal "x", e.number
+ end
+
+ end
+
end
diff --git a/actionpack/test/template/output_buffer_test.rb b/actionpack/test/template/output_buffer_test.rb
index 36bbaf9099..bd49a11af1 100644
--- a/actionpack/test/template/output_buffer_test.rb
+++ b/actionpack/test/template/output_buffer_test.rb
@@ -10,6 +10,7 @@ class OutputBufferTest < ActionController::TestCase
tests TestController
def setup
+ @vc = @controller.view_context
get :index
assert_equal ['foo'], body_parts
end
@@ -19,21 +20,21 @@ class OutputBufferTest < ActionController::TestCase
end
test 'flushing ignores nil output buffer' do
- @controller.template.flush_output_buffer
+ @controller.view_context.flush_output_buffer
assert_nil output_buffer
assert_equal ['foo'], body_parts
end
test 'flushing ignores empty output buffer' do
- @controller.template.output_buffer = ''
- @controller.template.flush_output_buffer
+ @vc.output_buffer = ''
+ @vc.flush_output_buffer
assert_equal '', output_buffer
assert_equal ['foo'], body_parts
end
test 'flushing appends the output buffer to the body parts' do
- @controller.template.output_buffer = 'bar'
- @controller.template.flush_output_buffer
+ @vc.output_buffer = 'bar'
+ @vc.flush_output_buffer
assert_equal '', output_buffer
assert_equal ['foo', 'bar'], body_parts
end
@@ -41,8 +42,8 @@ class OutputBufferTest < ActionController::TestCase
if '1.9'.respond_to?(:force_encoding)
test 'flushing preserves output buffer encoding' do
original_buffer = ' '.force_encoding(Encoding::EUC_JP)
- @controller.template.output_buffer = original_buffer
- @controller.template.flush_output_buffer
+ @vc.output_buffer = original_buffer
+ @vc.flush_output_buffer
assert_equal ['foo', original_buffer], body_parts
assert_not_equal original_buffer, output_buffer
assert_equal Encoding::EUC_JP, output_buffer.encoding
@@ -51,10 +52,10 @@ class OutputBufferTest < ActionController::TestCase
protected
def output_buffer
- @controller.template.output_buffer
+ @vc.output_buffer
end
def body_parts
- @controller.template.response.body_parts
+ @controller.response.body_parts
end
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index cea8ab1bce..e54ebfbf8d 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -9,7 +9,7 @@ module RenderTestCases
def setup_view(paths)
@assigns = { :secret => 'in the sauce' }
@view = ActionView::Base.new(paths, @assigns)
- @controller_view = ActionView::Base.for_controller(TestController.new)
+ @controller_view = TestController.new.view_context
# Reload and register danish language for testing
I18n.reload!
@@ -228,6 +228,14 @@ module RenderTestCases
@view.render(:file => "test/hello_world.erb", :layout => "layouts/yield")
end
+ # TODO: Move to deprecated_tests.rb
+ def test_render_with_nested_layout_deprecated
+ assert_deprecated do
+ assert_equal %(<title>title</title>\n\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
+ @view.render(:file => "test/deprecated_nested_layout.erb", :layout => "layouts/yield")
+ end
+ end
+
def test_render_with_nested_layout
assert_equal %(<title>title</title>\n\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
@view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 699fb2f5bc..6782bf06d4 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -20,7 +20,14 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_translation_of_an_array
I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"])
- assert_equal ["foo", "bar"], translate(["foo", "bar"])
+ assert_equal "foobar", translate(["foo", "bar"])
+ end
+
+ def test_translation_of_an_array_with_html
+ expected = '<a href="#">foo</a><a href="#">bar</a>'
+ I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(['<a href="#">foo</a>', '<a href="#">bar</a>'])
+ @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ assert_equal expected, @view.render(:file => "test/array_translation")
end
def test_delegates_localize_to_i18n
@@ -34,4 +41,10 @@ class TranslationHelperTest < ActiveSupport::TestCase
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "helper", @view.render(:file => "test/translation")
end
+
+ def test_scoping_by_partial_of_an_array
+ I18n.expects(:translate).with("test.scoped_array_translation.foo.bar", :raise => true).returns(["foo", "bar"])
+ @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ assert_equal "foobar", @view.render(:file => "test/scoped_array_translation")
+ end
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 165cb655da..87b2e59255 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -238,10 +238,7 @@ class UrlHelperTest < ActionView::TestCase
end
def test_link_tag_using_block_in_erb
- __in_erb_template = ''
-
- link_to("http://example.com") { concat("Example site") }
-
+ output_buffer = link_to("http://example.com") { concat("Example site") }
assert_equal '<a href="http://example.com">Example site</a>', output_buffer
end