aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG8
-rw-r--r--actionpack/RUNNING_UNIT_TESTS13
-rw-r--r--actionpack/lib/abstract_controller/base.rb1
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb5
-rw-r--r--actionpack/lib/action_controller/metal.rb5
-rw-r--r--actionpack/lib/action_controller/metal/head.rb6
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb1
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb20
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb4
-rw-r--r--actionpack/lib/action_controller/railtie.rb8
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb13
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb58
-rw-r--r--actionpack/lib/action_view.rb22
-rw-r--r--actionpack/lib/action_view/base.rb42
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb12
-rw-r--r--actionpack/lib/action_view/lookup_context.rb31
-rw-r--r--actionpack/lib/action_view/partials.rb (renamed from actionpack/lib/action_view/render/partials.rb)161
-rw-r--r--actionpack/lib/action_view/path_set.rb (renamed from actionpack/lib/action_view/paths.rb)4
-rw-r--r--actionpack/lib/action_view/render/rendering.rb67
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb36
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb166
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb98
-rw-r--r--actionpack/lib/action_view/rendering.rb (renamed from actionpack/lib/action_view/render/layouts.rb)63
-rw-r--r--actionpack/lib/action_view/template.rb145
-rw-r--r--actionpack/lib/action_view/template/error.rb5
-rw-r--r--actionpack/lib/action_view/template/handler.rb10
-rw-r--r--actionpack/lib/action_view/template/handlers.rb10
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb8
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb25
-rw-r--r--actionpack/lib/action_view/template/handlers/rjs.rb12
-rw-r--r--actionpack/lib/action_view/template/resolver.rb115
-rw-r--r--actionpack/lib/action_view/template/text.rb4
-rw-r--r--actionpack/lib/action_view/testing/resolvers.rb13
-rw-r--r--actionpack/test/controller/content_type_test.rb24
-rw-r--r--actionpack/test/controller/layout_test.rb18
-rw-r--r--actionpack/test/controller/mime_responds_test.rb19
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb15
-rw-r--r--actionpack/test/controller/new_base/render_once_test.rb86
-rw-r--r--actionpack/test/controller/new_base/render_partial_test.rb17
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb26
-rw-r--r--actionpack/test/dispatch/request_test.rb12
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb20
-rw-r--r--actionpack/test/fixtures/layout_tests/alt/hello.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/alt/hello.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb (renamed from actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml)0
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/item.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/item.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/layout_test.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/goodbye.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/goodbye.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/hello.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/hello.rhtml1
-rw-r--r--actionpack/test/fixtures/old_content_type/render_default_for_builder.builder (renamed from actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml)0
-rw-r--r--actionpack/test/fixtures/old_content_type/render_default_for_erb.erb (renamed from actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml)0
-rw-r--r--actionpack/test/fixtures/test/_object_inspector.erb1
-rw-r--r--actionpack/test/template/form_helper_test.rb45
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb10
-rw-r--r--actionpack/test/template/log_subscriber_test.rb6
-rw-r--r--actionpack/test/template/lookup_context_test.rb66
-rw-r--r--actionpack/test/template/render_test.rb17
-rw-r--r--actionpack/test/template/template_test.rb110
-rw-r--r--actionpack/test/template/translation_helper_test.rb4
-rw-r--r--actionpack/test/template/url_helper_test.rb23
73 files changed, 1180 insertions, 578 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 6352b97a6b..3f8188b1f7 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,13 @@
*Rails 3.1.0 (unreleased)*
+* Added render :once. You can pass either a string or an array of strings and Rails will ensure they each of them are rendered just once. [José Valim]
+
+* Deprecate old template handler API. The new API simply requires a template handler to respond to call. [José Valim]
+
+* :rhtml and :rxml were finally removed as template handlers. [José Valim]
+
+* Moved etag responsibility from ActionDispatch::Response to the middleware stack. [José Valim]
+
* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. [José Valim]
* file_field automatically adds :multipart => true to the enclosing form. [Santiago Pastorino]
diff --git a/actionpack/RUNNING_UNIT_TESTS b/actionpack/RUNNING_UNIT_TESTS
index d6a1ccf871..1e3ba7abe7 100644
--- a/actionpack/RUNNING_UNIT_TESTS
+++ b/actionpack/RUNNING_UNIT_TESTS
@@ -8,15 +8,18 @@ Rake can be found at http://rake.rubyforge.org
== Running by hand
-If you only want to run a single test suite, or don't want to bother with Rake,
-you can do so with something like:
+To run a single test suite
- ruby -Itest test/controller/base_tests.rb
+ rake test TEST=path/to/test.rb
-== Dependency on ActiveRecord and database setup
+which can be further narrowed down to one test:
+
+ rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
+
+== Dependency on Active Record and database setup
Test cases in the test/controller/active_record/ directory depend on having
-activerecord and sqlite installed. If ActiveRecord is not in
+activerecord and sqlite installed. If Active Record is not in
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
these tests are skipped.
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index f9f6eb945e..f83eaded88 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -13,6 +13,7 @@ module AbstractController
class Base
attr_internal :response_body
attr_internal :action_name
+ attr_internal :formats
include ActiveSupport::Configurable
extend ActiveSupport::DescendantsTracker
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 5d9b35d297..1c63fb2d14 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -12,7 +12,6 @@ module AbstractController
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
# it will trigger the lookup_context and consequently expire the cache.
- # TODO Add some deprecation warnings to remove I18n.locale from controllers
class I18nProxy < ::I18n::Config #:nodoc:
attr_reader :i18n_config, :lookup_context
@@ -50,7 +49,7 @@ module AbstractController
if controller.respond_to?(:_helpers)
include controller._helpers
- if controller.respond_to?(:_routes)
+ if controller.respond_to?(:_routes) && controller._routes
include controller._routes.url_helpers
include controller._routes.mounted_helpers
end
@@ -156,7 +155,7 @@ module AbstractController
options[:partial] = action_name
end
- if (options.keys & [:partial, :file, :template]).empty?
+ if (options.keys & [:partial, :file, :template, :once]).empty?
options[:prefix] ||= _prefix
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index ace1aabe03..329798e84f 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -119,6 +119,11 @@ module ActionController
headers["Location"] = url
end
+ # basic url_for that can be overridden for more robust functionality
+ def url_for(string)
+ string
+ end
+
def status
@_status
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 2b4a3b9392..8abcad55a2 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -2,8 +2,6 @@ module ActionController
module Head
extend ActiveSupport::Concern
- include ActionController::UrlFor
-
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
@@ -27,8 +25,8 @@ module ActionController
self.status = status
self.location = url_for(location) if location
- self.content_type = Mime[formats.first]
+ self.content_type = Mime[formats.first] if formats
self.response_body = " "
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 86bb810947..e524e546ad 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -2,7 +2,6 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
- include ActionController::RackDelegation
include AbstractController::Rendering
# Before processing, set the request formats in current controller formats.
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 851925e1b7..38d32211cc 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -161,6 +161,8 @@ module ActionController #:nodoc:
display resource.errors, :status => :unprocessable_entity
elsif post?
display resource, :status => :created, :location => api_location
+ elsif has_empty_resource_definition?
+ display empty_resource, :status => :ok
else
head :ok
end
@@ -221,5 +223,23 @@ module ActionController #:nodoc:
def default_action
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
end
+
+ # Check whether resource needs a specific definition of empty resource to be valid
+ #
+ def has_empty_resource_definition?
+ respond_to?("empty_#{format}_resource")
+ end
+
+ # Delegate to proper empty resource method
+ #
+ def empty_resource
+ send("empty_#{format}_resource")
+ end
+
+ # Return a valid empty JSON resource
+ #
+ def empty_json_resource
+ "{}"
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 2013da3adb..312dc8eb3e 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -101,10 +101,6 @@ module ActionController #:nodoc:
# send_data image.data, :type => image.content_type, :disposition => 'inline'
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
- #
- # <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
- # data to the browser, then use <tt>render :text => proc { ... }</tt>
- # instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
send_file_headers! options.dup
render options.slice(:status, :content_type).merge(:text => data)
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 0ade42ba2d..c5a661f2b0 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -21,10 +21,10 @@ module ActionController
paths = app.config.paths
options = app.config.action_controller
- options.assets_dir ||= paths.public.to_a.first
- options.javascripts_dir ||= paths.public.javascripts.to_a.first
- options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
- options.page_cache_directory ||= paths.public.to_a.first
+ options.assets_dir ||= paths["public"].first
+ options.javascripts_dir ||= paths["public/javascripts"].first
+ options.stylesheets_dir ||= paths["public/stylesheets"].first
+ options.page_cache_directory ||= paths["public"].first
# make sure readers methods get compiled
options.asset_path ||= nil
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
index fa71d55946..7a59d4f2f3 100644
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -5,12 +5,14 @@ module ActionController
Module.new do
define_method(:inherited) do |klass|
super(klass)
+
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
- klass.helpers_path = namespace._railtie.config.paths.app.helpers.to_a
+ paths = namespace._railtie.paths["app/helpers"].existent
else
- klass.helpers_path = app.config.helpers_paths
+ paths = app.config.helpers_paths
end
+ klass.helpers_path = paths
klass.helper :all if klass.superclass == ActionController::Base
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index bbcdefb190..55a3166f6d 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -214,13 +214,13 @@ module ActionDispatch
# Override Rack's GET method to support indifferent access
def GET
- @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
+ @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {})
end
alias :query_parameters :GET
# Override Rack's POST method to support indifferent access
def POST
- @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
+ @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {})
end
alias :request_parameters :POST
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index ef0c9c51f5..71e736ce9f 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -62,6 +62,7 @@ module ActionDispatch
private
def render_exception(env, exception)
log_error(exception)
+ exception = original_exception(exception)
request = Request.new(env)
if @consider_all_requests_local || request.local?
@@ -154,5 +155,17 @@ module ActionDispatch
def logger
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
+
+ def original_exception(exception)
+ if registered_original_exception?(exception)
+ exception.original_exception
+ else
+ exception
+ end
+ end
+
+ def registered_original_exception?(exception)
+ exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index bf10f81127..3c7dcea003 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -735,15 +735,15 @@ module ActionDispatch
resource_scope(SingletonResource.new(resources.pop, options)) do
yield if block_given?
- collection_scope do
+ collection do
post :create
end if parent_resource.actions.include?(:create)
- new_scope do
+ new do
get :new
end if parent_resource.actions.include?(:new)
- member_scope do
+ member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -780,16 +780,16 @@ module ActionDispatch
resource_scope(Resource.new(resources.pop, options)) do
yield if block_given?
- collection_scope do
+ collection do
get :index if parent_resource.actions.include?(:index)
post :create if parent_resource.actions.include?(:create)
end
- new_scope do
+ new do
get :new
end if parent_resource.actions.include?(:new)
- member_scope do
+ member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -813,12 +813,14 @@ module ActionDispatch
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
# route helpers.
def collection
- unless @scope[:scope_level] == :resources
- raise ArgumentError, "can't use collection outside resources scope"
+ unless resource_scope?
+ raise ArgumentError, "can't use collection outside resource(s) scope"
end
- collection_scope do
- yield
+ with_scope_level(:collection) do
+ scope(parent_resource.collection_scope) do
+ yield
+ end
end
end
@@ -838,8 +840,10 @@ module ActionDispatch
raise ArgumentError, "can't use member outside resource(s) scope"
end
- member_scope do
- yield
+ with_scope_level(:member) do
+ scope(parent_resource.member_scope) do
+ yield
+ end
end
end
@@ -848,8 +852,10 @@ module ActionDispatch
raise ArgumentError, "can't use new outside resource(s) scope"
end
- new_scope do
- yield
+ with_scope_level(:new) do
+ scope(parent_resource.new_scope(action_path(:new))) do
+ yield
+ end
end
end
@@ -1034,30 +1040,6 @@ module ActionDispatch
end
end
- def new_scope
- with_scope_level(:new) do
- scope(parent_resource.new_scope(action_path(:new))) do
- yield
- end
- end
- end
-
- def collection_scope
- with_scope_level(:collection) do
- scope(parent_resource.collection_scope) do
- yield
- end
- end
- end
-
- def member_scope
- with_scope_level(:member) do
- scope(parent_resource.member_scope) do
- yield
- end
- end
- end
-
def nested_options
{}.tap do |options|
options[:as] = parent_resource.member_name
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 5f9dc70766..dada64a86f 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,9 +21,6 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
require 'active_support/ruby/shim'
require 'active_support/core_ext/class/attribute_accessors'
@@ -33,24 +30,27 @@ module ActionView
extend ActiveSupport::Autoload
eager_autoload do
+ autoload :Base
autoload :Context
- autoload :Template
autoload :Helpers
- autoload :Base
autoload :LookupContext
- autoload :PathSet, "action_view/paths"
- autoload :TestCase, "action_view/test_case"
+ autoload :Partials
+ autoload :PathSet
+ autoload :Rendering
+ autoload :Template
+ autoload :TestCase
- autoload_under "render" do
- autoload :Layouts
- autoload :Partials
- autoload :Rendering
+ autoload_under "renderer" do
+ autoload :AbstractRenderer
+ autoload :PartialRenderer
+ autoload :TemplateRenderer
end
autoload_at "action_view/template/resolver" do
autoload :Resolver
autoload :PathResolver
autoload :FileSystemResolver
+ autoload :FallbackFileSystemResolver
end
autoload_at "action_view/template/error" do
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 0bef3e3a08..1beae37af3 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -155,10 +155,7 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods documentation for more details.
class Base
- module Subclasses
- end
-
- include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ include Helpers, Rendering, Partials, ::ERB::Util, Context
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@@ -177,13 +174,12 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- attr_accessor :base_path, :assigns, :template_extension, :lookup_context
- attr_internal :captures, :request, :controller, :template, :config
+ attr_accessor :_template
+ attr_internal :request, :controller, :config, :assigns, :lookup_context
- delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
- :view_paths, :view_paths=, :with_fallbacks, :update_details, :with_layout_format, :to => :lookup_context
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
- delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
+ delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
@@ -198,32 +194,40 @@ module ActionView #:nodoc:
end
def assign(new_assigns) # :nodoc:
- self.assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
+ @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
assign(assigns_for_first_render)
- self.helpers = self.class.helpers || Module.new
-
- if @_controller = controller
- @_request = controller.request if controller.respond_to?(:request)
- end
-
- @_config = controller && controller.respond_to?(:config) ? controller.config.inheritable_copy : {}
+ self.helpers = Module.new unless self.class.helpers
+ @_config = {}
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
@output_buffer = nil
- @lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
+ if @_controller = controller
+ @_request = controller.request if controller.respond_to?(:request)
+ @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
+ end
+
+ @_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
lookup_context : ActionView::LookupContext.new(lookup_context)
- @lookup_context.formats = formats if formats
+ @_lookup_context.formats = formats if formats
+ end
+
+ def store_content_for(key, value)
+ @_content_for[key] = value
end
def controller_path
@controller_path ||= controller && controller.controller_path
end
+ def controller_prefix
+ @controller_prefix ||= controller && controller._prefix
+ end
+
ActiveSupport.run_load_hooks(:action_view, self)
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index c1dfbe5dc3..d0f91f6b71 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -152,7 +152,7 @@ module ActionView
#
# # Normally you'd calculate RELEASE_NUMBER at startup.
# RELEASE_NUMBER = 12345
- # config.action_controller.asset_path_template = proc { |asset_path|
+ # config.action_controller.asset_path = proc { |asset_path|
# "/release-#{RELEASE_NUMBER}#{asset_path}"
# }
#
@@ -292,9 +292,6 @@ module ActionView
#
# * = The application.js file is only referenced if it exists
#
- # Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
- # (e.g., you're going to be using a certain .js file in every action), then take a look at the register_javascript_include_default method.
- #
# You can also include all javascripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
# javascript_include_tag :all # =>
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 3cd8b02bc4..b34a74788e 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1020,14 +1020,9 @@ module ActionView
def value_before_type_cast(object, method_name)
unless object.nil?
- if object.respond_to?(method_name)
- object.send(method_name)
- # FIXME: this is AR dependent
- elsif object.respond_to?(method_name + "_before_type_cast")
- object.send(method_name + "_before_type_cast")
- else
- raise NoMethodError, "Model #{object.class} does not respond to #{method_name}"
- end
+ object.respond_to?(method_name + "_before_type_cast") ?
+ object.send(method_name + "_before_type_cast") :
+ object.send(method_name)
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 298db46177..ae83b6bf39 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -390,7 +390,7 @@ module ActionView
end
if confirm = options.delete("confirm")
- add_confirm_to_attributes!(options, confirm)
+ options["data-confirm"] = confirm
end
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
@@ -423,7 +423,7 @@ module ActionView
options.stringify_keys!
if confirm = options.delete("confirm")
- add_confirm_to_attributes!(options, confirm)
+ options["data-confirm"] = confirm
end
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 300207dfac..41cd8d5171 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -546,7 +546,7 @@ module ActionView
end
def with_formats(*args)
- @context ? @context.update_details(:formats => args) { yield } : yield
+ @context ? @context.lookup_context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 13767a09f9..8574ca6595 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -45,8 +45,8 @@ module ActionView
private
def scope_key_by_partial(key)
if key.to_s.first == "."
- if @_virtual_path
- @_virtual_path.gsub(%r{/_?}, ".") + key.to_s
+ if (path = @_template && @_template.virtual_path)
+ path.gsub(%r{/_?}, ".") + key.to_s
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 da42d94318..c007cac47f 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -587,10 +587,12 @@ module ActionView
html_options = html_options.stringify_keys
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
+ disable_with = html_options.delete("disable_with")
confirm = html_options.delete('confirm')
method = html_options.delete('method')
- add_confirm_to_attributes!(html_options, confirm) if confirm
+ html_options["data-disable-with"] = disable_with if disable_with
+ html_options["data-confirm"] = confirm if confirm
add_method_to_attributes!(html_options, method) if method
html_options
@@ -601,13 +603,9 @@ module ActionView
options.is_a?(Hash) && options.key?('remote') && options.delete('remote')
end
- def add_confirm_to_attributes!(html_options, confirm)
- html_options["data-confirm"] = confirm if confirm
- end
-
def add_method_to_attributes!(html_options, method)
- html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get"
- html_options["data-method"] = method if method
+ html_options["rel"] = "nofollow" if method.to_s.downcase != "get"
+ html_options["data-method"] = method
end
def options_for_javascript(options)
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 3ea8b86af1..27f94a73a6 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -10,7 +10,7 @@ module ActionView
# this key is generated just once during the request, it speeds up all cache accesses.
class LookupContext #:nodoc:
mattr_accessor :fallbacks
- @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
+ @@fallbacks = FallbackFileSystemResolver.instances
mattr_accessor :registered_details
self.registered_details = []
@@ -61,6 +61,7 @@ module ActionView
def initialize(view_paths, details = {})
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
+ @cache = true
self.view_paths = view_paths
self.registered_detail_setters.each do |key, setter|
@@ -77,17 +78,17 @@ module ActionView
@view_paths = ActionView::Base.process_view_paths(paths)
end
- def find(name, prefix = nil, partial = false)
- @view_paths.find(*args_for_lookup(name, prefix, partial))
+ def find(name, prefix = nil, partial = false, keys = [])
+ @view_paths.find(*args_for_lookup(name, prefix, partial, keys))
end
alias :find_template :find
- def find_all(name, prefix = nil, partial = false)
- @view_paths.find_all(*args_for_lookup(name, prefix, partial))
+ def find_all(name, prefix = nil, partial = false, keys = [])
+ @view_paths.find_all(*args_for_lookup(name, prefix, partial, keys))
end
- def exists?(name, prefix = nil, partial = false)
- @view_paths.exists?(*args_for_lookup(name, prefix, partial))
+ def exists?(name, prefix = nil, partial = false, keys = [])
+ @view_paths.exists?(*args_for_lookup(name, prefix, partial, keys))
end
alias :template_exists? :exists?
@@ -106,9 +107,9 @@ module ActionView
protected
- def args_for_lookup(name, prefix, partial) #:nodoc:
+ def args_for_lookup(name, prefix, partial, keys) #:nodoc:
name, prefix = normalize_name(name, prefix)
- [name, prefix, partial || false, @details, details_key]
+ [name, prefix, partial || false, @details, keys, details_key]
end
# Support legacy foo.erb names even though we now ignore .erb
@@ -130,10 +131,20 @@ module ActionView
end
module Details
+ attr_accessor :cache
+
# 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)
+ @details_key ||= DetailsKey.get(@details) if @cache
+ end
+
+ # Temporary skip passing the details_key forward.
+ def disable_cache
+ old_value, @cache = @cache, false
+ yield
+ ensure
+ @cache = old_value
end
# Freeze the current formats in the lookup context. By freezing them, you are guaranteeing
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/partials.rb
index cc9b444837..844c3e9572 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -210,165 +210,12 @@ module ActionView
# <%- end -%>
# <% end %>
module Partials
- extend ActiveSupport::Concern
-
- class PartialRenderer
- PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
-
- def initialize(view_context, options, block)
- @view = view_context
- @partial_names = PARTIAL_NAMES[@view.controller.class.name]
-
- setup(options, block)
- end
-
- def setup(options, block)
- partial = options[:partial]
-
- @options = options
- @locals = options[:locals] || {}
- @block = block
-
- if String === partial
- @object = options[:object]
- @path = partial
- @collection = collection
- else
- @object = partial
-
- if @collection = collection
- paths = @collection_paths = @collection.map { |o| partial_path(o) }
- @path = paths.uniq.size == 1 ? paths.first : nil
- else
- @path = partial_path
- end
- end
- end
-
- def render
- identifier = ((@template = find_template) ? @template.identifier : @path)
-
- if @collection
- ActiveSupport::Notifications.instrument("render_collection.action_view",
- :identifier => identifier || "collection", :count => @collection.size) do
- render_collection
- end
- else
- content = ActiveSupport::Notifications.instrument("render_partial.action_view",
- :identifier => identifier) do
- render_partial
- end
-
- if !@block && (layout = @options[:layout])
- content = @view._render_layout(find_template(layout), @locals){ content }
- end
-
- content
- end
- end
-
- def render_collection
- return nil if @collection.blank?
-
- if @options.key?(:spacer_template)
- spacer = find_template(@options[:spacer_template]).render(@view, @locals)
- end
-
- result = @template ? collection_with_template : collection_without_template
- result.join(spacer).html_safe
- end
-
- def collection_with_template(template = @template)
- segments, locals, template = [], @locals, @template
-
- if @options[:as]
- as = @options[:as]
- counter = "#{as}_counter".to_sym
- else
- as = template.variable_name
- counter = template.counter_name
- end
-
- locals[counter] = -1
-
- @collection.each do |object|
- locals[counter] += 1
- locals[as] = object
- segments << template.render(@view, locals)
- end
-
- segments
- end
-
- def collection_without_template(collection_paths = @collection_paths)
- segments, locals = [], @locals
- index, template = -1, nil
-
- if @options[:as]
- as = @options[:as]
- counter = "#{as}_counter"
- end
-
- @collection.each_with_index do |object, i|
- template = find_template(collection_paths[i])
- locals[as || template.variable_name] = object
- locals[counter || template.counter_name] = (index += 1)
-
- segments << template.render(@view, locals)
- end
-
- @template = template
- segments
- end
-
- def render_partial(object = @object)
- locals, view, template = @locals, @view, @template
-
- object ||= locals[template.variable_name]
- locals[@options[:as] || template.variable_name] = object
-
- template.render(view, locals) do |*name|
- view._layout_for(*name, &@block)
- end
- end
-
- private
-
- def collection
- if @object.respond_to?(:to_ary)
- @object
- elsif @options.key?(:collection)
- @options[:collection] || []
- end
- end
-
- def find_template(path=@path)
- return path unless path.is_a?(String)
- prefix = @view.controller_path unless path.include?(?/)
- @view.find_template(path, prefix, true)
- end
-
- def partial_path(object = @object)
- @partial_names[object.class.name] ||= begin
- object = object.to_model if object.respond_to?(:to_model)
-
- object.class.model_name.partial_path.dup.tap do |partial|
- path = @view.controller_path
- partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
- end
- end
- end
- end
-
def _render_partial(options, &block) #:nodoc:
- if defined?(@renderer)
- @renderer.setup(options, block)
- else
- @renderer = PartialRenderer.new(self, options, block)
- end
-
- @renderer.render
+ _partial_renderer.setup(options, block).render
end
+ def _partial_renderer #:nodoc:
+ @_partial_renderer ||= PartialRenderer.new(self)
+ end
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/path_set.rb
index 9857d688d2..dc26d75ff3 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/path_set.rb
@@ -10,8 +10,8 @@ module ActionView #:nodoc:
METHOD
end
- def find(path, prefix = nil, partial = false, details = {}, key = nil)
- template = find_all(path, prefix, partial, details, key).first
+ def find(path, prefix = nil, partial = false, details = {}, keys = [], key = nil)
+ template = find_all(path, prefix, partial, details, keys, key).first
raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
template
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
deleted file mode 100644
index 5320245173..0000000000
--- a/actionpack/lib/action_view/render/rendering.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'active_support/core_ext/object/try'
-
-module ActionView
- # = Action View Rendering
- module Rendering
- # Returns the result of a render that's dictated by the options hash. The primary options are:
- #
- # * <tt>:partial</tt> - See ActionView::Partials.
- # * <tt>:update</tt> - Calls update_page with the block given.
- # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
- # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
- # * <tt>:text</tt> - Renders the text passed in out.
- #
- # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
- # as the locals hash.
- def render(options = {}, locals = {}, &block)
- case options
- when Hash
- if block_given?
- _render_partial(options.merge(:partial => options[:layout]), &block)
- elsif options.key?(:partial)
- _render_partial(options)
- else
- template = _determine_template(options)
- lookup_context.freeze_formats(template.formats, true)
- _render_template(template, options[:layout], options)
- end
- when :update
- update_page(&block)
- else
- _render_partial(:partial => options, :locals => locals)
- end
- end
-
- # Determine the template to be rendered using the given options.
- def _determine_template(options) #:nodoc:
- if options.key?(:inline)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, {})
- elsif options.key?(:text)
- Template::Text.new(options[:text], formats.try(:first))
- elsif options.key?(:file)
- with_fallbacks { find_template(options[:file], options[:prefix]) }
- elsif options.key?(:template)
- options[:template].respond_to?(:render) ?
- options[:template] : find_template(options[:template], options[:prefix])
- end
- end
-
- # Renders the given template. An string representing the layout can be
- # supplied as well.
- def _render_template(template, layout = nil, options = {}) #:nodoc:
- locals = options[:locals] || {}
- layout = find_layout(layout) if layout
-
- ActiveSupport::Notifications.instrument("render_template.action_view",
- :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
-
- content = template.render(self, locals) { |*name| _layout_for(*name) }
- @_content_for[:layout] = content if layout
-
- content = _render_layout(layout, locals) if layout
- content
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
new file mode 100644
index 0000000000..77cfa51dff
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -0,0 +1,36 @@
+module ActionView
+ class AbstractRenderer #:nodoc:
+ attr_reader :vew, :lookup_context
+
+ delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
+ :with_layout_format, :formats, :to => :lookup_context
+
+ def initialize(view)
+ @view = view
+ @lookup_context = view.lookup_context
+ end
+
+ def render
+ raise NotImplementedError
+ end
+
+ # Checks if the given path contains a format and if so, change
+ # the lookup context to take this new format into account.
+ def wrap_formats(value)
+ return yield unless value.is_a?(String)
+ @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
+
+ if value.sub!(@@formats_regexp, "")
+ update_details(:formats => [$1.to_sym]){ yield }
+ else
+ yield
+ end
+ end
+
+ protected
+
+ def instrument(name, options={})
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
new file mode 100644
index 0000000000..c580397cad
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -0,0 +1,166 @@
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class PartialRenderer < AbstractRenderer #:nodoc:
+ PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
+
+ def initialize(view)
+ super
+ @partial_names = PARTIAL_NAMES[@view.controller.class.name]
+ end
+
+ def setup(options, block)
+ partial = options[:partial]
+
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ if String === partial
+ @object = options[:object]
+ @path = partial
+ @collection = collection
+ else
+ @object = partial
+
+ if @collection = collection_from_object || collection
+ paths = @collection_data = @collection.map { |o| partial_path(o) }
+ @path = paths.uniq.size == 1 ? paths.first : nil
+ else
+ @path = partial_path
+ end
+ end
+
+ if @path
+ @variable, @variable_counter = retrieve_variable(@path)
+ else
+ paths.map! { |path| retrieve_variable(path).unshift(path) }
+ end
+
+ self
+ end
+
+ def render
+ wrap_formats(@path) do
+ identifier = ((@template = find_partial) ? @template.identifier : @path)
+
+ if @collection
+ instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
+ render_collection
+ end
+ else
+ instrument(:partial, :identifier => identifier) do
+ render_partial
+ end
+ end
+ end
+ end
+
+ def render_collection
+ return nil if @collection.blank?
+
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
+ end
+
+ result = @template ? collection_with_template : collection_without_template
+ result.join(spacer).html_safe
+ end
+
+ def render_partial
+ locals, view, block = @locals, @view, @block
+ object, as = @object, @variable
+
+ if !block && (layout = @options[:layout])
+ layout = find_template(layout)
+ end
+
+ object ||= locals[as]
+ locals[as] = object
+
+ content = @template.render(view, locals) do |*name|
+ view._layout_for(*name, &block)
+ end
+
+ content = layout.render(view, locals){ content } if layout
+ content
+ end
+
+ private
+
+ def collection
+ if @options.key?(:collection)
+ @options[:collection] || []
+ end
+ end
+
+ def collection_from_object
+ if @object.respond_to?(:to_ary)
+ @object
+ end
+ end
+
+ def find_partial
+ if path = @path
+ locals = @locals.keys
+ locals << @variable
+ locals << @variable_counter if @collection
+ find_template(path, locals)
+ end
+ end
+
+ def find_template(path=@path, locals=@locals.keys)
+ prefix = @view.controller_prefix unless path.include?(?/)
+ @lookup_context.find_template(path, prefix, true, locals)
+ end
+
+ def collection_with_template
+ segments, locals, template = [], @locals, @template
+ as, counter = @variable, @variable_counter
+
+ locals[counter] = -1
+
+ @collection.each do |object|
+ locals[counter] += 1
+ locals[as] = object
+ segments << template.render(@view, locals)
+ end
+
+ segments
+ end
+
+ def collection_without_template
+ segments, locals, collection_data = [], @locals, @collection_data
+ index, template, cache = -1, nil, {}
+ keys = @locals.keys
+
+ @collection.each_with_index do |object, i|
+ path, *data = collection_data[i]
+ template = (cache[path] ||= find_template(path, keys + data))
+ locals[data[0]] = object
+ locals[data[1]] = (index += 1)
+ segments << template.render(@view, locals)
+ end
+
+ @template = template
+ segments
+ end
+
+ def partial_path(object = @object)
+ @partial_names[object.class.name] ||= begin
+ object = object.to_model if object.respond_to?(:to_model)
+
+ object.class.model_name.partial_path.dup.tap do |partial|
+ path = @view.controller_prefix
+ partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
+ end
+ end
+ end
+
+ def retrieve_variable(path)
+ variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable_counter = :"#{variable}_counter" if @collection
+ [variable, variable_counter]
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
new file mode 100644
index 0000000000..36beb5c8d0
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -0,0 +1,98 @@
+require 'set'
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/array/wrap'
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class TemplateRenderer < AbstractRenderer #:nodoc:
+ attr_reader :rendered
+
+ def initialize(view)
+ super
+ @rendered = Set.new
+ end
+
+ def render(options)
+ wrap_formats(options[:template] || options[:file]) do
+ template = determine_template(options)
+ render_template(template, options[:layout], options[:locals])
+ end
+ end
+
+ def render_once(options)
+ paths, locals = options[:once], options[:locals] || {}
+ layout, keys = options[:layout], locals.keys
+ prefix = options.fetch(:prefix, @view.controller_prefix)
+
+ raise "render :once expects a String or an Array to be given" unless paths
+
+ render_with_layout(layout, locals) do
+ contents = []
+ Array.wrap(paths).each do |path|
+ template = find_template(path, prefix, false, keys)
+ contents << render_template(template, nil, locals) if @rendered.add?(template)
+ end
+ contents.join("\n")
+ end
+ end
+
+ # Determine the template to be rendered using the given options.
+ def determine_template(options) #:nodoc:
+ keys = options[:locals].try(:keys) || []
+
+ if options.key?(:text)
+ Template::Text.new(options[:text], formats.try(:first))
+ elsif options.key?(:file)
+ with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
+ elsif options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template.new(options[:inline], "inline template", handler, :locals => keys)
+ elsif options.key?(:template)
+ options[:template].respond_to?(:render) ?
+ options[:template] : find_template(options[:template], options[:prefix], false, keys)
+ end
+ end
+
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+ lookup_context.freeze_formats(template.formats, true)
+ view, locals = @view, locals || {}
+
+ render_with_layout(layout_name, locals) do |layout|
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+ template.render(view, locals) { |*name| view._layout_for(*name) }
+ end
+ end
+ end
+
+ def render_with_layout(path, locals) #:nodoc:
+ layout = path && find_layout(path, locals.keys)
+ content = yield(layout)
+
+ if layout
+ view = @view
+ view.store_content_for(:layout, content)
+ layout.render(view, locals){ |*name| view._layout_for(*name) }
+ else
+ content
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checks if at least a layout with
+ # the given name exists across all details before raising the error.
+ def find_layout(layout, keys)
+ begin
+ with_layout_format do
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
+ end
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless template_exists?(layout)
+ 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/rendering.rb
index 8882acca31..baa5d2c3fd 100644
--- a/actionpack/lib/action_view/render/layouts.rb
+++ b/actionpack/lib/action_view/rendering.rb
@@ -1,6 +1,38 @@
+require 'active_support/core_ext/object/try'
+
module ActionView
- # = Action View Layouts
- module Layouts
+ # = Action View Rendering
+ module Rendering
+ # Returns the result of a render that's dictated by the options hash. The primary options are:
+ #
+ # * <tt>:partial</tt> - See ActionView::Partials.
+ # * <tt>:update</tt> - Calls update_page with the block given.
+ # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
+ # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
+ # * <tt>:text</tt> - Renders the text passed in out.
+ # * <tt>:once</tt> - Accepts a string or an array of strings and Rails will ensure they each of them are rendered just once.
+ #
+ # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
+ # as the locals hash.
+ def render(options = {}, locals = {}, &block)
+ case options
+ when Hash
+ if block_given?
+ _render_partial(options.merge(:partial => options[:layout]), &block)
+ elsif options.key?(:partial)
+ _render_partial(options)
+ elsif options.key?(:once)
+ _render_once(options)
+ else
+ _render_template(options)
+ end
+ when :update
+ update_page(&block)
+ else
+ _render_partial(:partial => options, :locals => locals)
+ end
+ end
+
# Returns the contents that are yielded to a layout, given a name or a block.
#
# You can think of a layout as a method that is called with a block. If the user calls
@@ -47,7 +79,7 @@ module ActionView
# Hello David
# </html>
#
- def _layout_for(*args, &block) #:nodoc:
+ def _layout_for(*args, &block)
name = args.first
if name.is_a?(Symbol)
@@ -59,25 +91,16 @@ module ActionView
end
end
- # This is the method which actually finds the layout using details in the lookup
- # context object. If no layout is found, it checks if at least a layout with
- # the given name exists across all details before raising the error.
- def find_layout(layout)
- begin
- with_layout_format do
- layout =~ /^\// ?
- with_fallbacks { find_template(layout) } : find_template(layout)
- end
- rescue ActionView::MissingTemplate => e
- update_details(:formats => nil) do
- raise unless template_exists?(layout)
- end
- end
+ def _render_once(options) #:nodoc:
+ _template_renderer.render_once(options)
+ end
+
+ def _render_template(options) #:nodoc:
+ _template_renderer.render(options)
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) }
+ def _template_renderer #:nodoc:
+ @_template_renderer ||= TemplateRenderer.new(self)
end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 164d177dcc..7dd8acf37b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -98,8 +98,9 @@ module ActionView
extend Template::Handlers
- attr_reader :source, :identifier, :handler, :virtual_path, :formats,
- :original_encoding
+ attr_accessor :locals, :formats, :virtual_path
+
+ attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
# This finalizer is needed (and exactly with a proc inside another proc)
# otherwise templates leak in development.
@@ -112,49 +113,75 @@ module ActionView
end
def initialize(source, identifier, handler, details)
- @source = source
- @identifier = identifier
- @handler = handler
- @original_encoding = nil
- @method_names = {}
-
- format = details[:format] || :html
- @formats = Array.wrap(format).map(&:to_sym)
- @virtual_path = details[:virtual_path].try(:sub, ".#{format}", "")
+ format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
+
+ @source = source
+ @identifier = identifier
+ @handler = handler
+ @compiled = false
+ @original_encoding = nil
+ @locals = details[:locals] || []
+ @virtual_path = details[:virtual_path]
+ @updated_at = details[:updated_at] || Time.now
+ @formats = Array.wrap(format).map(&:to_sym)
end
+ # Render a template. If the template was not compiled yet, it is done
+ # exactly before rendering.
+ #
+ # This method is instrumented as "!render_template.action_view". 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.
def render(view, 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.
+ old_template, view._template = view._template, self
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
- if view.is_a?(ActionView::CompiledTemplates)
- mod = ActionView::CompiledTemplates
- else
- mod = view.singleton_class
- end
-
- method_name = compile(locals, view, mod)
+ compile!(view)
view.send(method_name, locals, &block)
end
rescue Exception => e
- if e.is_a?(Template::Error)
- e.sub_template_of(self)
- raise e
- else
- raise Template::Error.new(self, view.respond_to?(:assigns) ? view.assigns : {}, e)
- end
+ handle_render_error(view, e)
+ ensure
+ view._template = old_template
end
def mime_type
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
end
- def variable_name
- @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ # Receives a view object and return a template similar to self by using @virtual_path.
+ #
+ # This method is useful if you have a template object but it does not contain its source
+ # anymore since it was already compiled. In such cases, all you need to do is to call
+ # refresh passing in the view object.
+ #
+ # Notice this method raises an error if the template to be refreshed does not have a
+ # virtual path set (true just for inline templates).
+ def refresh(view)
+ raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
+ lookup = view.lookup_context
+ pieces = @virtual_path.split("/")
+ name = pieces.pop
+ partial = !!name.sub!(/^_/, "")
+ lookup.disable_cache do
+ lookup.find_template(name, pieces.join, partial, @locals)
+ end
end
- def counter_name
- @counter_name ||= "#{variable_name}_counter".to_sym
+ # Expires this template by setting his updated_at date to Jan 1st, 1970.
+ def expire!
+ @updated_at = Time.utc(1970)
+ end
+
+ # Receives a view context and renders a template exactly like self by using
+ # the @virtual_path. It raises an error if no @virtual_path was given.
+ def rerender(view)
+ raise "A template needs to have a virtual path in order to be rerendered" unless @virtual_path
+ name = @virtual_path.dup
+ if name.sub!(/(^|\/)_([^\/]*)$/, '\1\2')
+ view.render :partial => name
+ else
+ view.render :template => @virtual_path
+ end
end
def inspect
@@ -166,7 +193,27 @@ module ActionView
end
end
- private
+ protected
+
+ # Compile a template. This method ensures a template is compiled
+ # just once and removes the source after it is compiled.
+ def compile!(view) #:nodoc:
+ return if @compiled
+
+ if view.is_a?(ActionView::CompiledTemplates)
+ mod = ActionView::CompiledTemplates
+ else
+ mod = view.singleton_class
+ end
+
+ compile(view, mod)
+
+ # Just discard the source if we have a virtual path. This
+ # means we can get the template back.
+ @source = nil if @virtual_path
+ @compiled = true
+ end
+
# Among other things, this method is responsible for properly setting
# the encoding of the source. Until this point, we assume that the
# source is BINARY data. If no additional information is supplied,
@@ -187,11 +234,8 @@ module ActionView
# encode the source into Encoding.default_internal. In general,
# this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
- def compile(locals, view, mod)
- method_name = build_method_name(locals)
- return method_name if view.respond_to?(method_name)
-
- locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
+ def compile(view, mod) #:nodoc:
+ method_name = self.method_name
if source.encoding_aware?
# Look for # encoding: *. If we find one, we'll encode the
@@ -231,9 +275,9 @@ module ActionView
# encoding of the code
source = <<-end_src
def #{method_name}(local_assigns)
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
+ _old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
- @_virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
+ @output_buffer = _old_output_buffer
end
end_src
@@ -256,8 +300,6 @@ module ActionView
begin
mod.module_eval(source, identifier, 0)
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
-
- method_name
rescue Exception => e # errors from template code
if logger = (view && view.logger)
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
@@ -269,12 +311,27 @@ module ActionView
end
end
- def build_method_name(locals)
- @method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
+ def handle_render_error(view, e) #:nodoc:
+ if e.is_a?(Template::Error)
+ e.sub_template_of(self)
+ raise e
+ else
+ assigns = view.respond_to?(:assigns) ? view.assigns : {}
+ template = @virtual_path ? refresh(view) : self
+ raise Template::Error.new(template, assigns, e)
+ end
+ end
+
+ def locals_code #:nodoc:
+ @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+ end
+
+ def method_name #:nodoc:
+ @method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
end
- def identifier_method_name
- @identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_')
+ def identifier_method_name #:nodoc:
+ inspect.gsub(/[^a-z_]/, '_')
end
end
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index 423e1e0bf5..ff256738a9 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -43,8 +43,9 @@ module ActionView
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.
+ # The Template::Error exception is raised when the compilation or rendering of the template
+ # fails. This exception then gathers a bunch of intimate details and uses it to report a
+ # precise exception message.
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3
diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb
index c6a1bc6235..636f3ebbad 100644
--- a/actionpack/lib/action_view/template/handler.rb
+++ b/actionpack/lib/action_view/template/handler.rb
@@ -1,4 +1,4 @@
-require "action_dispatch/http/mime_type"
+require 'action_dispatch/http/mime_type'
require 'active_support/core_ext/class/attribute'
# Legacy TemplateHandler stub
@@ -7,6 +7,8 @@ module ActionView
module Handlers #:nodoc:
module Compilable
def self.included(base)
+ ActiveSupport::Deprecation.warn "Including Compilable in your template handler is deprecated. " <<
+ "Since Rails 3, all the API your template handler needs to implement is to respond to #call."
base.extend(ClassMethods)
end
@@ -26,6 +28,12 @@ module ActionView
class_attribute :default_format
self.default_format = Mime::HTML
+ def self.inherited(base)
+ ActiveSupport::Deprecation.warn "Inheriting from ActionView::Template::Handler is deprecated. " <<
+ "Since Rails 3, all the API your template handler needs to implement is to respond to #call."
+ super
+ end
+
def self.call(template)
raise "Need to implement #{self.class.name}#call(template)"
end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index ed397699b0..60347e2a95 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -7,13 +7,9 @@ module ActionView #:nodoc:
autoload :Builder, 'action_view/template/handlers/builder'
def self.extended(base)
- base.register_default_template_handler :erb, ERB
- base.register_template_handler :rjs, RJS
- base.register_template_handler :builder, Builder
-
- # TODO: Depreciate old template extensions
- base.register_template_handler :rhtml, ERB
- base.register_template_handler :rxml, Builder
+ base.register_default_template_handler :erb, ERB.new
+ base.register_template_handler :rjs, RJS.new
+ base.register_template_handler :builder, Builder.new
end
@@template_handlers = {}
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index a93cfca8aa..2c52cfd90e 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -1,11 +1,11 @@
module ActionView
module Template::Handlers
- class Builder < Template::Handler
- include Compilable
-
+ class Builder
+ # Default format used by Builder.
+ class_attribute :default_format
self.default_format = Mime::XML
- def compile(template)
+ def call(template)
require 'builder'
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
"self.output_buffer = xml.target!;" +
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 24e1e44c1d..b827610456 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/string/output_safety'
-require "action_view/template"
+require 'action_view/template'
+require 'action_view/template/handler'
require 'erubis'
module ActionView
@@ -47,28 +48,31 @@ module ActionView
end
end
- class ERB < Handler
- include Compilable
-
- ##
- # :singleton-method:
+ class ERB
# Specify trim mode for the ERB compiler. Defaults to '-'.
# See ERb documentation for suitable values.
- cattr_accessor :erb_trim_mode
+ class_attribute :erb_trim_mode
self.erb_trim_mode = '-'
+ # Default format used by ERB.
+ class_attribute :default_format
self.default_format = Mime::HTML
- cattr_accessor :erb_implementation
+ # Default implemenation used.
+ class_attribute :erb_implementation
self.erb_implementation = Erubis
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
- def self.handles_encoding?
+ def self.call(template)
+ new.call(template)
+ end
+
+ def handles_encoding?
true
end
- def compile(template)
+ def call(template)
if template.source.encoding_aware?
# First, convert to BINARY, so in case the encoding is
# wrong, we can still find an encoding tag
@@ -94,6 +98,7 @@ module ActionView
end
private
+
def valid_encoding(string, encoding)
# If a magic encoding comment was found, tag the
# String with this encoding. This is for a case
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
index 128be5077c..9d71059134 100644
--- a/actionpack/lib/action_view/template/handlers/rjs.rb
+++ b/actionpack/lib/action_view/template/handlers/rjs.rb
@@ -1,17 +1,13 @@
module ActionView
module Template::Handlers
- class RJS < Template::Handler
- include Compilable
-
+ class RJS
+ # Default format used by RJS.
+ class_attribute :default_format
self.default_format = Mime::JS
- def compile(template)
+ def call(template)
"update_page do |page|;#{template.source}\nend"
end
-
- def default_format
- Mime::JS
- end
end
end
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index a261e08dbc..7707dbcf98 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -6,9 +6,8 @@ module ActionView
# = Action View Resolver
class Resolver
def initialize
- @path = nil
- @cached = Hash.new { |h1,k1| h1[k1] =
- Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
+ @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
+ h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
end
def clear_cache
@@ -16,8 +15,8 @@ module ActionView
end
# Normalizes the arguments and passes it on to find_template.
- def find_all(name, prefix=nil, partial=false, details={}, key=nil)
- cached(key, prefix, name, partial) do
+ def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil)
+ cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
end
end
@@ -35,34 +34,73 @@ module ActionView
raise NotImplementedError
end
- def cached(key, prefix, name, partial)
- return yield unless key && caching?
- @cached[key][prefix][name][partial] ||= yield
+ # Helpers that builds a path. Useful for building virtual paths.
+ def build_path(name, prefix, partial)
+ path = ""
+ path << "#{prefix}/" unless prefix.empty?
+ path << (partial ? "_#{name}" : name)
+ path
+ end
+
+ # Hnadles templates caching. If a key is given and caching is on
+ # always check the cache before hitting the resolver. Otherwise,
+ # it always hits the resolver but check if the resolver is fresher
+ # before returning it.
+ def cached(key, path_info, details, locals) #:nodoc:
+ name, prefix, partial = path_info
+ locals = sort_locals(locals)
+
+ if key && caching?
+ @cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
+ else
+ fresh = decorate(yield, path_info, details, locals)
+ return fresh unless key
+
+ scope = @cached[key][name][prefix][partial]
+ cache = scope[locals]
+ mtime = cache && cache.map(&:updated_at).max
+
+ if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
+ scope[locals] = fresh
+ else
+ cache
+ end
+ end
+ end
+
+ # Ensures all the resolver information is set in the template.
+ def decorate(templates, path_info, details, locals) #:nodoc:
+ cached = nil
+ templates.each do |t|
+ t.locals = locals
+ t.formats = details[:formats] || [:html] if t.formats.empty?
+ t.virtual_path ||= (cached ||= build_path(*path_info))
+ end
+ end
+
+ if :symbol.respond_to?("<=>")
+ def sort_locals(locals) #:nodoc:
+ locals.sort.freeze
+ end
+ else
+ def sort_locals(locals) #:nodoc:
+ locals = locals.map{ |l| l.to_s }
+ locals.sort!
+ locals.freeze
+ end
end
end
class PathResolver < Resolver
EXTENSION_ORDER = [:locale, :formats, :handlers]
- def to_s
- @path.to_s
- end
- alias :to_path :to_s
-
- private
+ private
def find_templates(name, prefix, partial, details)
- path = build_path(name, prefix, partial, details)
+ path = build_path(name, prefix, partial)
query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
end
- def build_path(name, prefix, partial, details)
- path = ""
- path << "#{prefix}/" unless prefix.empty?
- path << (partial ? "_#{name}" : name)
- path
- end
-
def query(path, exts, formats)
query = File.join(@path, path)
@@ -76,26 +114,28 @@ module ActionView
contents = File.open(p, "rb") {|io| io.read }
Template.new(contents, File.expand_path(p), handler,
- :virtual_path => path, :format => format)
+ :virtual_path => path, :format => format, :updated_at => mtime(p))
end
end
+ # Returns the file mtime from the filesystem.
+ def mtime(p)
+ File.stat(p).mtime
+ end
+
# 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
- format ||= handler.default_format if handler.respond_to?(:default_format)
- format ||= default_formats
-
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last]
[handler, format]
end
end
+ # A resolver that loads files from the filesystem.
class FileSystemResolver < PathResolver
def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
@@ -103,9 +143,26 @@ module ActionView
@path = File.expand_path(path)
end
+ def to_s
+ @path.to_s
+ end
+ alias :to_path :to_s
+
def eql?(resolver)
self.class.equal?(resolver.class) && to_path == resolver.to_path
end
alias :== :eql?
end
+
+ # The same as FileSystemResolver but does not allow templates to store
+ # a virtual path since it is invalid for such resolvers.
+ class FallbackFileSystemResolver < FileSystemResolver
+ def self.instances
+ [new(""), new("/")]
+ end
+
+ def decorate(*)
+ super.each { |t| t.virtual_path = nil }
+ end
+ end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 51be831dfb..4261c3b5e2 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -25,10 +25,6 @@ module ActionView #:nodoc:
def formats
[@mime_type.to_sym]
end
-
- def partial?
- false
- end
end
end
end
diff --git a/actionpack/lib/action_view/testing/resolvers.rb b/actionpack/lib/action_view/testing/resolvers.rb
index b2b62528a9..55583096e0 100644
--- a/actionpack/lib/action_view/testing/resolvers.rb
+++ b/actionpack/lib/action_view/testing/resolvers.rb
@@ -13,26 +13,29 @@ module ActionView #:nodoc:
@hash = hash
end
- private
+ private
def query(path, exts, formats)
- query = Regexp.escape(path)
+ query = ""
exts.each do |ext|
query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
end
+ query = /^(#{Regexp.escape(path)})#{query}$/
templates = []
- @hash.select { |k,v| k =~ /^#{query}$/ }.each do |_path, source|
+ @hash.each do |_path, array|
+ source, updated_at = array
+ next unless _path =~ query
handler, format = extract_handler_and_format(_path, formats)
templates << Template.new(source, _path, handler,
- :virtual_path => _path, :format => format)
+ :virtual_path => $1, :format => format, :updated_at => updated_at)
end
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
end
end
- class NullResolver < ActionView::PathResolver
+ class NullResolver < PathResolver
def query(path, exts, formats)
handler, format = extract_handler_and_format(path, formats)
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)]
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index 967107853b..9500c25a32 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -29,18 +29,18 @@ class OldContentTypeController < ActionController::Base
render :text => "hello world!"
end
- def render_default_for_rhtml
+ def render_default_for_erb
end
- def render_default_for_rxml
+ def render_default_for_builder
end
def render_default_for_rjs
end
- def render_change_for_rxml
+ def render_change_for_builder
response.content_type = Mime::HTML
- render :action => "render_default_for_rxml"
+ render :action => "render_default_for_builder"
end
def render_default_content_types_for_respond_to
@@ -108,23 +108,23 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset, @response.headers.inspect
end
- def test_nil_default_for_rhtml
+ def test_nil_default_for_erb
OldContentTypeController.default_charset = nil
- get :render_default_for_rhtml
+ get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_nil @response.charset, @response.headers.inspect
ensure
OldContentTypeController.default_charset = "utf-8"
end
- def test_default_for_rhtml
- get :render_default_for_rhtml
+ def test_default_for_erb
+ get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end
- def test_default_for_rxml
- get :render_default_for_rxml
+ def test_default_for_builder
+ get :render_default_for_builder
assert_equal Mime::XML, @response.content_type
assert_equal "utf-8", @response.charset
end
@@ -135,8 +135,8 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset
end
- def test_change_for_rxml
- get :render_change_for_rxml
+ def test_change_for_builder
+ get :render_change_for_builder
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 32a0f40614..cafe2b9320 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -46,13 +46,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_application_layout_is_default_when_no_controller_match
@controller = ProductController.new
get :hello
- assert_equal 'layout_test.rhtml hello.rhtml', @response.body
+ assert_equal 'layout_test.erb hello.erb', @response.body
end
def test_controller_name_layout_name_match
@controller = ItemController.new
get :hello
- assert_equal 'item.rhtml hello.rhtml', @response.body
+ assert_equal 'item.erb hello.erb', @response.body
end
def test_third_party_template_library_auto_discovers_layout
@@ -65,13 +65,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_namespaced_controllers_auto_detect_layouts1
@controller = ControllerNameSpace::NestedController.new
get :hello
- assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
+ assert_equal 'controller_name_space/nested.erb hello.erb', @response.body
end
def test_namespaced_controllers_auto_detect_layouts2
@controller = MultipleExtensions.new
get :hello
- assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
+ assert_equal 'multiple_extensions.html.erb hello.erb', @response.body.strip
end
end
@@ -79,7 +79,7 @@ class DefaultLayoutController < LayoutTest
end
class AbsolutePathLayoutController < LayoutTest
- layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.rhtml')
+ layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.erb')
end
class HasOwnLayoutController < LayoutTest
@@ -137,7 +137,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
- assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
+ assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_except_exception_when_included
@@ -149,7 +149,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
- assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
+ assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_set_when_using_render
@@ -173,7 +173,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_absolute_pathed_layout
@controller = AbsolutePathLayoutController.new
get :hello
- assert_equal "layout_test.rhtml hello.rhtml", @response.body.strip
+ assert_equal "layout_test.erb hello.erb", @response.body.strip
end
end
@@ -184,7 +184,7 @@ class RenderWithTemplateOptionController < LayoutTest
end
class SetsNonExistentLayoutFile < LayoutTest
- layout "nofile.rhtml"
+ layout "nofile.erb"
end
class LayoutExceptionRaised < ActionController::TestCase
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index adccfa028f..a898ef76e5 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -709,6 +709,15 @@ class RespondWithControllerTest < ActionController::TestCase
assert_equal " ", @response.body
end
+ def test_using_resource_for_put_with_json_yields_ok_on_success
+ Customer.any_instance.stubs(:to_json).returns('{"name": "David"}')
+ @request.accept = "application/json"
+ put :using_resource
+ assert_equal "application/json", @response.content_type
+ assert_equal 200, @response.status
+ assert_equal "{}", @response.body
+ end
+
def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure
@request.accept = "application/xml"
errors = { :name => :invalid }
@@ -739,6 +748,16 @@ class RespondWithControllerTest < ActionController::TestCase
assert_equal " ", @response.body
end
+ def test_using_resource_for_delete_with_json_yields_ok_on_success
+ Customer.any_instance.stubs(:to_json).returns('{"name": "David"}')
+ Customer.any_instance.stubs(:destroyed?).returns(true)
+ @request.accept = "application/json"
+ delete :using_resource
+ assert_equal "application/json", @response.content_type
+ assert_equal 200, @response.status
+ assert_equal "{}", @response.body
+ end
+
def test_using_resource_for_delete_with_html_redirects_on_failure
with_test_route_set do
errors = { :name => :invalid }
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 8a06e8d2a0..44922cecff 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -24,4 +24,19 @@ module BareMetalTest
assert_equal "Hello world", string
end
end
+
+ class HeadController < ActionController::Metal
+ include ActionController::Head
+
+ def index
+ head :not_found
+ end
+ end
+
+ class HeadTest < ActiveSupport::TestCase
+ test "head works on its own" do
+ status, headers, body = HeadController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal 404, status
+ end
+ end
end
diff --git a/actionpack/test/controller/new_base/render_once_test.rb b/actionpack/test/controller/new_base/render_once_test.rb
new file mode 100644
index 0000000000..3035ed4ff2
--- /dev/null
+++ b/actionpack/test/controller/new_base/render_once_test.rb
@@ -0,0 +1,86 @@
+require 'abstract_unit'
+
+module RenderTemplate
+ class RenderOnceController < ActionController::Base
+ layout false
+
+ RESOLVER = ActionView::FixtureResolver.new(
+ "test/a.html.erb" => "a",
+ "test/b.html.erb" => "<>",
+ "test/c.html.erb" => "c",
+ "test/one.html.erb" => "<%= render :once => 'result' %>",
+ "test/two.html.erb" => "<%= render :once => 'result' %>",
+ "test/three.html.erb" => "<%= render :once => 'result' %>",
+ "test/result.html.erb" => "YES!",
+ "other/result.html.erb" => "NO!",
+ "layouts/test.html.erb" => "l<%= yield %>l"
+ )
+
+ self.view_paths = [RESOLVER]
+
+ def _prefix
+ "test"
+ end
+
+ def multiple
+ render :once => %w(a b c)
+ end
+
+ def once
+ render :once => %w(one two three)
+ end
+
+ def duplicate
+ render :once => %w(a a a)
+ end
+
+ def with_layout
+ render :once => %w(a b c), :layout => "test"
+ end
+
+ def with_prefix
+ render :once => "result", :prefix => "other"
+ end
+
+ def with_nil_prefix
+ render :once => "test/result", :prefix => nil
+ end
+ end
+
+ module Tests
+ def test_mutliple_arguments_get_all_rendered
+ get :multiple
+ assert_response "a\n<>\nc"
+ end
+
+ def test_referenced_templates_get_rendered_once
+ get :once
+ assert_response "YES!\n\n"
+ end
+
+ def test_duplicated_templates_get_rendered_once
+ get :duplicate
+ assert_response "a"
+ end
+
+ def test_layout_wraps_all_rendered_templates
+ get :with_layout
+ assert_response "la\n<>\ncl"
+ end
+
+ def test_with_prefix_option
+ get :with_prefix
+ assert_response "NO!"
+ end
+
+ def test_with_nil_prefix_option
+ get :with_nil_prefix
+ assert_response "YES!"
+ end
+ end
+
+ class TestRenderOnce < Rack::TestCase
+ testing RenderTemplate::RenderOnceController
+ include Tests
+ end
+end
diff --git a/actionpack/test/controller/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb
index 5c7e66dba2..d800ea264d 100644
--- a/actionpack/test/controller/new_base/render_partial_test.rb
+++ b/actionpack/test/controller/new_base/render_partial_test.rb
@@ -5,10 +5,17 @@ module RenderPartial
class BasicController < ActionController::Base
self.view_paths = [ActionView::FixtureResolver.new(
- "render_partial/basic/_basic.html.erb" => "BasicPartial!",
- "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>"
+ "render_partial/basic/_basic.html.erb" => "BasicPartial!",
+ "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>",
+ "render_partial/basic/with_json.html.erb" => "<%= render 'with_json.json' %>",
+ "render_partial/basic/_with_json.json.erb" => "<%= render 'final' %>",
+ "render_partial/basic/_final.json.erb" => "{ final: json }"
)]
+ def html_with_json_inside_json
+ render :action => "with_json"
+ end
+
def changing
@test_unchanged = 'hello'
render :action => "basic"
@@ -22,6 +29,12 @@ module RenderPartial
get :changing
assert_response("goodbyeBasicPartial!goodbye")
end
+
+ test "rendering a template with renders another partial with other format that renders other partial in the same format" do
+ get :html_with_json_inside_json
+ assert_content_type "text/html; charset=utf-8"
+ assert_response "{ final: json }"
+ end
end
end
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index fea98d6bbd..d31193a063 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -8,13 +8,21 @@ module RenderTemplate
"shared.html.erb" => "Elastica",
"locals.html.erb" => "The secret is <%= secret %>",
"xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend",
- "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>"
+ "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>",
+ "test/with_json.html.erb" => "<%= render :template => 'test/with_json.json' %>",
+ "test/with_json.json.erb" => "<%= render :template => 'test/final' %>",
+ "test/final.json.erb" => "{ final: json }",
+ "test/with_error.html.erb" => "<%= idontexist %>"
)]
def index
render :template => "test/basic"
end
+ def html_with_json_inside_json
+ render :template => "test/with_json"
+ end
+
def index_without_key
render "test/basic"
end
@@ -42,6 +50,10 @@ module RenderTemplate
def with_raw
render :template => "with_raw"
end
+
+ def with_error
+ render :template => "test/with_error"
+ end
end
class TestWithoutLayout < Rack::TestCase
@@ -88,6 +100,18 @@ module RenderTemplate
assert_body "Hello <strong>this is raw</strong>"
assert_status 200
end
+
+ test "rendering a template with renders another template with other format that renders other template in the same format" do
+ get :html_with_json_inside_json
+ assert_content_type "text/html; charset=utf-8"
+ assert_response "{ final: json }"
+ end
+
+ test "rendering a template with error properly exceprts the code" do
+ get :with_error
+ assert_status 500
+ assert_match "undefined local variable or method `idontexist'", response.body
+ end
end
class WithLayoutController < ::ApplicationController
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 3efed8bef6..04709e5346 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -385,6 +385,18 @@ class RequestTest < ActiveSupport::TestCase
assert_equal({"bar" => 2}, request.query_parameters)
end
+ test "parameters still accessible after rack parse error" do
+ mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" }
+ request = nil
+ begin
+ request = stub_request(mock_rack_env)
+ request.parameters
+ rescue TypeError => e
+ # rack will raise a TypeError when parsing this query string
+ end
+ assert_equal({}, request.parameters)
+ end
+
test "formats with accept header" do
request = stub_request 'HTTP_ACCEPT' => 'text/html'
request.expects(:parameters).at_least_once.returns({})
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index ce6c397e32..2a478c214f 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
class ShowExceptionsTest < ActionDispatch::IntegrationTest
+
Boomer = lambda do |env|
req = ActionDispatch::Request.new(env)
case req.path
@@ -12,6 +13,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
raise ActionController::NotImplemented
when "/unprocessable_entity"
raise ActionController::InvalidAuthenticityToken
+ when "/not_found_original_exception"
+ raise ActionView::Template::Error.new('template', {}, AbstractController::ActionNotFound.new)
else
raise "puke!"
end
@@ -101,4 +104,21 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
assert_response 500
assert_match("&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body)
end
+
+ test "show registered original exception for wrapped exceptions when consider_all_requests_local is false" do
+ @app = ProductionApp
+ self.remote_addr = '208.77.188.166'
+
+ get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 404
+ assert_match(/404 error/, body)
+ end
+
+ test "show registered original exception for wrapped exceptions when consider_all_requests_local is true" do
+ @app = DevelopmentApp
+
+ get "/not_found_original_exception", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 404
+ assert_match(/AbstractController::ActionNotFound/, body)
+ end
end
diff --git a/actionpack/test/fixtures/layout_tests/alt/hello.erb b/actionpack/test/fixtures/layout_tests/alt/hello.erb
new file mode 100644
index 0000000000..1055c36659
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/alt/hello.erb
@@ -0,0 +1 @@
+alt/hello.erb
diff --git a/actionpack/test/fixtures/layout_tests/alt/hello.rhtml b/actionpack/test/fixtures/layout_tests/alt/hello.rhtml
deleted file mode 100644
index fcda6cf97a..0000000000
--- a/actionpack/test/fixtures/layout_tests/alt/hello.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-alt/hello.rhtml
diff --git a/actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml b/actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb
index e69de29bb2..e69de29bb2 100644
--- a/actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml
+++ b/actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb
diff --git a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb
new file mode 100644
index 0000000000..121bc079a1
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb
@@ -0,0 +1 @@
+controller_name_space/nested.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
deleted file mode 100644
index 5f86a7de4d..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-controller_name_space/nested.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/item.erb b/actionpack/test/fixtures/layout_tests/layouts/item.erb
new file mode 100644
index 0000000000..60f04d77d5
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/item.erb
@@ -0,0 +1 @@
+item.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/item.rhtml b/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
deleted file mode 100644
index 1bc7cbda06..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-item.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb b/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb
new file mode 100644
index 0000000000..b74ac0840d
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb
@@ -0,0 +1 @@
+layout_test.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml b/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
deleted file mode 100644
index c0f2642b40..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-layout_test.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/goodbye.erb b/actionpack/test/fixtures/layout_tests/views/goodbye.erb
new file mode 100644
index 0000000000..4ee911188e
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/views/goodbye.erb
@@ -0,0 +1 @@
+hello.erb \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml b/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
deleted file mode 100644
index bbccf0913e..0000000000
--- a/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-hello.rhtml \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/hello.erb b/actionpack/test/fixtures/layout_tests/views/hello.erb
new file mode 100644
index 0000000000..4ee911188e
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/views/hello.erb
@@ -0,0 +1 @@
+hello.erb \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/hello.rhtml b/actionpack/test/fixtures/layout_tests/views/hello.rhtml
deleted file mode 100644
index bbccf0913e..0000000000
--- a/actionpack/test/fixtures/layout_tests/views/hello.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-hello.rhtml \ No newline at end of file
diff --git a/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml b/actionpack/test/fixtures/old_content_type/render_default_for_builder.builder
index 598d62e2fc..598d62e2fc 100644
--- a/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml
+++ b/actionpack/test/fixtures/old_content_type/render_default_for_builder.builder
diff --git a/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml b/actionpack/test/fixtures/old_content_type/render_default_for_erb.erb
index c7926d48bb..c7926d48bb 100644
--- a/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml
+++ b/actionpack/test/fixtures/old_content_type/render_default_for_erb.erb
diff --git a/actionpack/test/fixtures/test/_object_inspector.erb b/actionpack/test/fixtures/test/_object_inspector.erb
new file mode 100644
index 0000000000..53af593821
--- /dev/null
+++ b/actionpack/test/fixtures/test/_object_inspector.erb
@@ -0,0 +1 @@
+<%= object_inspector.inspect -%> \ No newline at end of file
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 8809e510fb..acb6e7aa64 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -4,18 +4,6 @@ require 'controller/fake_models'
class FormHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormHelper
- class Developer
- def name_before_type_cast
- "David"
- end
-
- def name
- "Santiago"
- end
-
- attr_writer :language
- end
-
def form_for(*)
@output_buffer = super
end
@@ -278,24 +266,6 @@ class FormHelperTest < ActionView::TestCase
text_field("user", "email", :type => "email")
end
- def test_text_field_from_a_user_defined_method
- @developer = Developer.new
- assert_dom_equal(
- '<input id="developer_name" name="developer[name]" size="30" type="text" value="Santiago" />', text_field("developer", "name")
- )
- end
-
- def test_text_field_on_a_model_with_undefined_attr_reader
- @developer = Developer.new
- @developer.language = 'ruby'
- begin
- text_field("developer", "language")
- rescue NoMethodError => error
- message = error.message
- end
- assert_equal "Model #{Developer} does not respond to language", message
- end
-
def test_check_box
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -765,11 +735,11 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_search_field
# Test case for bug which would emit an "object" attribute
# when used with form_for using a search_field form helper
- form_for(Post.new, :url => "/search", :html => { :id => 'search-post' }) do |f|
+ form_for(Post.new, :url => "/search", :html => { :id => 'search-post', :method => :get}) do |f|
concat f.search_field(:title)
end
- expected = whole_form("/search", "search-post", "new_post") do
+ expected = whole_form("/search", "search-post", "new_post", "get") do
"<input name='post[title]' size='30' type='search' id='post_title' />"
end
@@ -1558,17 +1528,20 @@ class FormHelperTest < ActionView::TestCase
def snowman(method = nil)
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
+ if (method && !['get','post'].include?(method.to_s))
+ txt << %{<input name="_method" type="hidden" value="#{method}" />}
+ end
txt << %{</div>}
end
- def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil)
+ def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil)
txt = %{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if multipart
txt << %{ data-remote="true"} if remote
txt << %{ class="#{html_class}"} if html_class
txt << %{ id="#{id}"} if id
- txt << %{ method="post">}
+ method = method.to_s == "get" ? "get" : "post"
+ txt << %{ method="#{method}">}
end
def whole_form(action = "/", id = nil, html_class = nil, options = nil)
@@ -1580,7 +1553,7 @@ class FormHelperTest < ActionView::TestCase
method = options
end
- form_text(action, id, html_class, remote, multipart) + snowman(method) + contents + "</form>"
+ form_text(action, id, html_class, remote, multipart, method) + snowman(method) + contents + "</form>"
end
def test_default_form_builder
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 8c8e87ae9f..f3933a25b9 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -13,19 +13,23 @@ class FormTagHelperTest < ActionView::TestCase
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
+ if (method && !['get','post'].include?(method.to_s))
+ txt << %{<input name="_method" type="hidden" value="#{method}" />}
+ end
txt << %{</div>}
end
def form_text(action = "http://www.example.com", options = {})
- remote, enctype, html_class, id = options.values_at(:remote, :enctype, :html_class, :id)
+ remote, enctype, html_class, id, method = options.values_at(:remote, :enctype, :html_class, :id, :method)
+
+ method = method.to_s == "get" ? "get" : "post"
txt = %{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if enctype
txt << %{ data-remote="true"} if remote
txt << %{ class="#{html_class}"} if html_class
txt << %{ id="#{id}"} if id
- txt << %{ method="post">}
+ txt << %{ method="#{method}">}
end
def whole_form(action = "http://www.example.com", options = {})
diff --git a/actionpack/test/template/log_subscriber_test.rb b/actionpack/test/template/log_subscriber_test.rb
index 6fb8d39818..435936b19f 100644
--- a/actionpack/test/template/log_subscriber_test.rb
+++ b/actionpack/test/template/log_subscriber_test.rb
@@ -57,7 +57,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
end
def test_render_partial_with_implicit_path
- @view.stubs(:controller_path).returns("test")
+ @view.stubs(:controller_prefix).returns("test")
@view.render(Customer.new("david"), :greeting => "hi")
wait
@@ -74,7 +74,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
end
def test_render_collection_with_implicit_path
- @view.stubs(:controller_path).returns("test")
+ @view.stubs(:controller_prefix).returns("test")
@view.render([ Customer.new("david"), Customer.new("mary") ], :greeting => "hi")
wait
@@ -83,7 +83,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
end
def test_render_collection_template_without_path
- @view.stubs(:controller_path).returns("test")
+ @view.stubs(:controller_prefix).returns("test")
@view.render([ GoodCustomer.new("david"), Customer.new("mary") ], :greeting => "hi")
wait
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index cc71cb42d0..6d3b26e131 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -100,8 +100,8 @@ class LookupContextTest < ActiveSupport::TestCase
@lookup_context.with_fallbacks do
assert_equal 3, @lookup_context.view_paths.size
- assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new(""))
- assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new("/"))
+ assert @lookup_context.view_paths.include?(ActionView::FallbackFileSystemResolver.new(""))
+ assert @lookup_context.view_paths.include?(ActionView::FallbackFileSystemResolver.new("/"))
end
end
@@ -163,4 +163,66 @@ class LookupContextTest < ActiveSupport::TestCase
template = @lookup_context.find("foo", "test", true)
assert_equal "Bar", template.source
end
+
+ test "can disable the cache on demand" do
+ @lookup_context.view_paths = ActionView::FixtureResolver.new("test/_foo.erb" => "Foo")
+ old_template = @lookup_context.find("foo", "test", true)
+
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal template, old_template
+
+ assert @lookup_context.cache
+ template = @lookup_context.disable_cache do
+ assert !@lookup_context.cache
+ @lookup_context.find("foo", "test", true)
+ end
+ assert @lookup_context.cache
+
+ assert_not_equal template, old_template
+ end
+end
+
+class LookupContextWithFalseCaching < ActiveSupport::TestCase
+ def setup
+ @resolver = ActionView::FixtureResolver.new("test/_foo.erb" => ["Foo", Time.utc(2000)])
+ @resolver.stubs(:caching?).returns(false)
+ @lookup_context = ActionView::LookupContext.new(@resolver, {})
+ end
+
+ test "templates are always found in the resolver but timestamp is checked before being compiled" do
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Now we are going to change the template, but it won't change the returned template
+ # since the timestamp is the same.
+ @resolver.hash["test/_foo.erb"][0] = "Bar"
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Now update the timestamp.
+ @resolver.hash["test/_foo.erb"][1] = Time.now.utc
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Bar", template.source
+ end
+
+ test "if no template was found in the second lookup, give it higher preference" do
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", "test", true)
+ end
+ end
+
+ test "if no template was cached in the first lookup, do not use the cache in the second" do
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", "test", true)
+ end
+
+ @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)]
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+ end
end \ No newline at end of file
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 205fdcf345..17bb610b6a 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -114,7 +114,7 @@ module RenderTestCases
end
def test_render_sub_template_with_errors
- @view.render(:file => "test/sub_template_raise")
+ @view.render(:template => "test/sub_template_raise")
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
assert_match %r!method.*doesnt_exist!, e.message
@@ -123,10 +123,25 @@ module RenderTestCases
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
+ def test_render_file_with_errors
+ @view.render(:file => File.expand_path("test/_raise", FIXTURE_LOAD_PATH))
+ flunk "Render did not raise Template::Error"
+ rescue ActionView::Template::Error => e
+ assert_match %r!method.*doesnt_exist!, e.message
+ assert_equal "", e.sub_template_message
+ assert_equal "1", e.line_number
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
+ end
+
def test_render_object
assert_equal "Hello: david", @view.render(:partial => "test/customer", :object => Customer.new("david"))
end
+ def test_render_object_with_array
+ assert_equal "[1, 2, 3]", @view.render(:partial => "test/object_inspector", :object => [1, 2, 3])
+ end
+
def test_render_partial_collection
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
end
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index c7c33af670..63f792d328 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -1,9 +1,18 @@
require "abstract_unit"
+require "logger"
class TestERBTemplate < ActiveSupport::TestCase
- ERBHandler = ActionView::Template::Handlers::ERB
+ ERBHandler = ActionView::Template::Handlers::ERB.new
+
+ class LookupContext
+ def disable_cache
+ yield
+ end
+ end
class Context
+ attr_accessor :_template
+
def initialize
@output_buffer = "original"
@_virtual_path = nil
@@ -15,15 +24,18 @@ class TestERBTemplate < ActiveSupport::TestCase
def partial
ActionView::Template.new(
- "<%= @_virtual_path %>",
+ "<%= @_template.virtual_path %>",
"partial",
ERBHandler,
:virtual_path => "partial"
)
end
+ def lookup_context
+ @lookup_context ||= LookupContext.new
+ end
+
def logger
- require "logger"
Logger.new(STDERR)
end
@@ -32,16 +44,16 @@ class TestERBTemplate < ActiveSupport::TestCase
end
end
- def new_template(body = "<%= hello %>", handler = ERBHandler, details = {})
- ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"})
+ def new_template(body = "<%= hello %>", details = {})
+ ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"}.merge!(details))
end
def render(locals = {})
- @template.render(@obj, locals)
+ @template.render(@context, locals)
end
def setup
- @obj = Context.new
+ @context = Context.new
end
def test_basic_template
@@ -49,24 +61,96 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_equal "Hello", render
end
+ def test_template_loses_its_source_after_rendering
+ @template = new_template
+ render
+ assert_nil @template.source
+ end
+
+ def test_template_does_not_lose_its_source_after_rendering_if_it_does_not_have_a_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ render
+ assert_equal "Hello", @template.source
+ end
+
def test_locals
@template = new_template("<%= my_local %>")
+ @template.locals = [:my_local]
assert_equal "I'm a local", render(:my_local => "I'm a local")
end
def test_restores_buffer
@template = new_template
assert_equal "Hello", render
- assert_equal "original", @obj.my_buffer
+ assert_equal "original", @context.my_buffer
end
def test_virtual_path
- @template = new_template("<%= @_virtual_path %>" \
+ @template = new_template("<%= @_template.virtual_path %>" \
"<%= partial.render(self, {}) %>" \
- "<%= @_virtual_path %>")
+ "<%= @_template.virtual_path %>")
assert_equal "hellopartialhello", render
end
+ def test_refresh_with_templates
+ @template = new_template("Hello", :virtual_path => "test/foo")
+ @template.locals = [:key]
+ @context.lookup_context.expects(:find_template).with("foo", "test", false, [:key]).returns("template")
+ assert_equal "template", @template.refresh(@context)
+ end
+
+ def test_refresh_with_partials
+ @template = new_template("Hello", :virtual_path => "test/_foo")
+ @template.locals = [:key]
+ @context.lookup_context.expects(:find_template).with("foo", "test", true, [:key]).returns("partial")
+ assert_equal "partial", @template.refresh(@context)
+ end
+
+ def test_refresh_raises_an_error_without_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ assert_raise RuntimeError do
+ @template.refresh(@context)
+ end
+ end
+
+ def test_template_expire_sets_the_timestamp_to_1970
+ @template = new_template("Hello", :updated_at => Time.utc(2010))
+ assert_equal Time.utc(2010), @template.updated_at
+ @template.expire!
+ assert_equal Time.utc(1970), @template.updated_at
+ end
+
+ def test_template_rerender_renders_a_template_like_self
+ @template = new_template("Hello", :virtual_path => "test/foo_bar")
+ @context.expects(:render).with(:template => "test/foo_bar").returns("template")
+ assert_equal "template", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_root_template_like_self
+ @template = new_template("Hello", :virtual_path => "foo_bar")
+ @context.expects(:render).with(:template => "foo_bar").returns("template")
+ assert_equal "template", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_partial_like_self
+ @template = new_template("Hello", :virtual_path => "test/_foo_bar")
+ @context.expects(:render).with(:partial => "test/foo_bar").returns("partial")
+ assert_equal "partial", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_root_partial_like_self
+ @template = new_template("Hello", :virtual_path => "_foo_bar")
+ @context.expects(:render).with(:partial => "foo_bar").returns("partial")
+ assert_equal "partial", @template.rerender(@context)
+ end
+
+ def test_rerender_raises_an_error_without_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ assert_raise RuntimeError do
+ @template.rerender(@context)
+ end
+ end
+
if "ruby".encoding_aware?
def test_resulting_string_is_utf8
@template = new_template
@@ -101,14 +185,14 @@ class TestERBTemplate < ActiveSupport::TestCase
# inside Rails.
def test_lying_with_magic_comment
assert_raises(ActionView::Template::Error) do
- @template = new_template("# encoding: UTF-8\nhello \xFCmlat")
+ @template = new_template("# encoding: UTF-8\nhello \xFCmlat", :virtual_path => nil)
render
end
end
def test_encoding_can_be_specified_with_magic_comment_in_erb
with_external_encoding Encoding::UTF_8 do
- @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat")
+ @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat", :virtual_path => nil)
result = render
assert_equal Encoding::UTF_8, render.encoding
assert_equal "hello \u{fc}mlat", render
@@ -117,7 +201,7 @@ class TestERBTemplate < ActiveSupport::TestCase
def test_error_when_template_isnt_valid_utf8
assert_raises(ActionView::Template::Error, /\xFC/) do
- @template = new_template("hello \xFCmlat")
+ @template = new_template("hello \xFCmlat", :virtual_path => nil)
render
end
end
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 952719a589..763080550b 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -31,13 +31,13 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_scoping_by_partial
I18n.expects(:translate).with("test.translation.helper", :raise => true).returns("helper")
- @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ @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_translation.foo.bar", :raise => true).returns(["foo", "bar"])
- @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ @view = ::ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "foobar", @view.render(:file => "test/scoped_translation")
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 98276da559..a6fdf806a4 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -9,7 +9,7 @@ class UrlHelperTest < ActiveSupport::TestCase
# or request.
#
# In those cases, we'll set up a simple mock
- attr_accessor :controller, :request
+ attr_accessor :controller, :request, :_template
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
@@ -69,6 +69,13 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_button_to_with_javascript_disable_with
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
+ )
+ end
+
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
@@ -76,6 +83,20 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_button_to_with_remote_and_javascript_disable_with
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
+ )
+ end
+
+ def test_button_to_with_remote_and_javascript_confirm_and_javascript_disable_with
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?", :disable_with => "Greeting...")
+ )
+ end
+
def test_button_to_with_remote_false
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",