diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2009-08-09 18:39:44 +0100 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2009-08-09 18:39:44 +0100 |
commit | 2e50110eac439f3d5d292f1519ae7c79991eb91a (patch) | |
tree | 5ba10ebd6ddf0a7487754798993d3862ee9ec3b3 /actionpack | |
parent | a7f09bc12236d9e7bdc2ee34d5fe3c782d6ad385 (diff) | |
parent | bb1e1776914edf3be7e46b55036c18a64595f919 (diff) | |
download | rails-2e50110eac439f3d5d292f1519ae7c79991eb91a.tar.gz rails-2e50110eac439f3d5d292f1519ae7c79991eb91a.tar.bz2 rails-2e50110eac439f3d5d292f1519ae7c79991eb91a.zip |
Merge commit 'mainstream/master'
Diffstat (limited to 'actionpack')
109 files changed, 1299 insertions, 773 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index e758983d7a..75de1fe2a6 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs] + +* Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov] + * Ruby 1.9: ERB template encoding using a magic comment at the top of the file. [Jeremy Kemper] <%# encoding: utf-8 %> diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb index 9eb92cd8e7..a9015da053 100644 --- a/actionpack/examples/minimal.rb +++ b/actionpack/examples/minimal.rb @@ -3,13 +3,19 @@ ENV['RAILS_ENV'] ||= 'production' ENV['NO_RELOAD'] ||= '1' $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" require 'action_controller' require 'action_controller/new_base' if ENV['NEW'] +require 'action_view' require 'benchmark' class Runner - def initialize(app) - @app = app + def initialize(app, output) + @app, @output = app, output + end + + def puts(*) + super if @output end def call(env) @@ -18,16 +24,22 @@ class Runner end def report(env, response) + return unless ENV["DEBUG"] out = env['rack.errors'] out.puts response[0], response[1].to_yaml, '---' response[2].each { |part| out.puts part } out.puts '---' end - def self.run(app, n, label = nil) - puts '=' * label.size, label, '=' * label.size if label - env = { 'n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout } - t = Benchmark.realtime { new(app).call(env) } + def self.puts(*) + super if @output + end + + def self.run(app, n, label, output = true) + @output = output + puts label, '=' * label.size if label + env = Rack::MockRequest.env_for("/").merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) + t = Benchmark.realtime { new(app, output).call(env) } puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] puts end @@ -36,23 +48,71 @@ end N = (ENV['N'] || 1000).to_i +module ActionController::Rails2Compatibility + instance_methods.each do |name| + remove_method name + end +end + class BasePostController < ActionController::Base + append_view_path "#{File.dirname(__FILE__)}/views" + + def overhead + self.response_body = '' + end + def index render :text => '' end + + def partial + render :partial => "/partial" + end + + def many_partials + render :partial => "/many_partials" + end + + def partial_collection + render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10] + end + + def show_template + render :template => "template" + end end OK = [200, {}, []] MetalPostController = lambda { OK } -if ActionController.const_defined?(:Http) - class HttpPostController < ActionController::Http - def index - self.response_body = '' - end +class HttpPostController < ActionController::Metal + def index + self.response_body = '' end end -Runner.run(MetalPostController, N, 'metal') -Runner.run(HttpPostController.action(:index), N, 'http') if defined? HttpPostController -Runner.run(BasePostController.action(:index), N, 'base') +unless ENV["PROFILE"] + Runner.run(BasePostController.action(:overhead), N, 'overhead', false) + Runner.run(BasePostController.action(:index), N, 'index', false) + Runner.run(BasePostController.action(:partial), N, 'partial', false) + Runner.run(BasePostController.action(:many_partials), N, 'many_partials', false) + Runner.run(BasePostController.action(:partial_collection), N, 'collection', false) + Runner.run(BasePostController.action(:show_template), N, 'template', false) + + (ENV["M"] || 1).to_i.times do + Runner.run(BasePostController.action(:overhead), N, 'overhead') + Runner.run(BasePostController.action(:index), N, 'index') + Runner.run(BasePostController.action(:partial), N, 'partial') + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + Runner.run(BasePostController.action(:partial_collection), N, 'collection') + Runner.run(BasePostController.action(:show_template), N, 'template') + end +else + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + require "ruby-prof" + RubyProf.start + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + result = RubyProf.stop + printer = RubyProf::CallStackPrinter.new(result) + printer.print(File.open("output.html", "w")) +end
\ No newline at end of file diff --git a/actionpack/examples/very_simple.rb b/actionpack/examples/very_simple.rb index 8d01cb39bd..6714185172 100644 --- a/actionpack/examples/very_simple.rb +++ b/actionpack/examples/very_simple.rb @@ -3,17 +3,17 @@ $:.push "rails/actionpack/lib" require "action_controller" -class Kaigi < ActionController::Http +class Kaigi < ActionController::Metal include AbstractController::Callbacks include ActionController::RackConvenience - include ActionController::Renderer + include ActionController::RenderingController include ActionController::Layouts include ActionView::Context before_filter :set_name append_view_path "views" - def _action_view + def view_context self end @@ -23,7 +23,7 @@ class Kaigi < ActionController::Http DEFAULT_LAYOUT = Object.new.tap {|l| def l.render(*) yield end } - def _render_template_from_controller(template, layout = DEFAULT_LAYOUT, options = {}, partial = false) + def render_template(template, layout = DEFAULT_LAYOUT, options = {}, partial = false) ret = template.render(self, {}) layout.render(self, {}) { ret } end @@ -47,4 +47,4 @@ app = Rack::Builder.new do map("/kaigi/alt") { run Kaigi.action(:alt) } end.to_app -Rack::Handler::Mongrel.run app, :Port => 3000
\ No newline at end of file +Rack::Handler::Mongrel.run app, :Port => 3000 diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb new file mode 100644 index 0000000000..bcfe958e2c --- /dev/null +++ b/actionpack/examples/views/_collection.erb @@ -0,0 +1 @@ +<%= collection %>
\ No newline at end of file diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb new file mode 100644 index 0000000000..5ab2f8a432 --- /dev/null +++ b/actionpack/examples/views/_hello.erb @@ -0,0 +1 @@ +Hello
\ No newline at end of file diff --git a/actionpack/examples/views/_many_partials.erb b/actionpack/examples/views/_many_partials.erb new file mode 100644 index 0000000000..7e379d46f5 --- /dev/null +++ b/actionpack/examples/views/_many_partials.erb @@ -0,0 +1,10 @@ +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %>
\ No newline at end of file diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb new file mode 100644 index 0000000000..3ca8e80b52 --- /dev/null +++ b/actionpack/examples/views/_partial.erb @@ -0,0 +1,10 @@ +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb index 3108e9ad70..5ab2f8a432 100644 --- a/actionpack/examples/views/template.html.erb +++ b/actionpack/examples/views/template.html.erb @@ -1 +1 @@ -Hello <%= @name %>
\ No newline at end of file +Hello
\ No newline at end of file diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb new file mode 100644 index 0000000000..cdeb55b915 --- /dev/null +++ b/actionpack/lib/abstract_controller.rb @@ -0,0 +1,16 @@ +require "active_support/core_ext/module/attr_internal" +require "active_support/core_ext/module/delegation" + +module AbstractController + autoload :Base, "abstract_controller/base" + autoload :Benchmarker, "abstract_controller/benchmarker" + autoload :Callbacks, "abstract_controller/callbacks" + autoload :Helpers, "abstract_controller/helpers" + autoload :Layouts, "abstract_controller/layouts" + autoload :Logger, "abstract_controller/logger" + autoload :RenderingController, "abstract_controller/rendering_controller" + # === Exceptions + autoload :ActionNotFound, "abstract_controller/exceptions" + autoload :DoubleRenderError, "abstract_controller/exceptions" + autoload :Error, "abstract_controller/exceptions" +end diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/abstract_controller/base.rb index ca00e66349..b93e6ce634 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -30,7 +30,7 @@ module AbstractController # instance methods on that abstract class. Public instance methods of # a controller would normally be considered action methods, so we # are removing those methods on classes declared as abstract - # (ActionController::Http and ActionController::Base are defined + # (ActionController::Metal and ActionController::Base are defined # as abstract) def internal_methods controller = self diff --git a/actionpack/lib/action_controller/abstract/benchmarker.rb b/actionpack/lib/abstract_controller/benchmarker.rb index 58e9564c2f..58e9564c2f 100644 --- a/actionpack/lib/action_controller/abstract/benchmarker.rb +++ b/actionpack/lib/abstract_controller/benchmarker.rb diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index ea4b59466e..ea4b59466e 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb diff --git a/actionpack/lib/action_controller/abstract/exceptions.rb b/actionpack/lib/abstract_controller/exceptions.rb index b671516de1..b671516de1 100644 --- a/actionpack/lib/action_controller/abstract/exceptions.rb +++ b/actionpack/lib/abstract_controller/exceptions.rb diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index 5efa37fde3..f3072fad74 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -2,10 +2,18 @@ module AbstractController module Helpers extend ActiveSupport::Concern - include Renderer + include RenderingController + + def self.next_serial + @helper_serial ||= 0 + @helper_serial += 1 + end included do extlib_inheritable_accessor(:_helpers) { Module.new } + extlib_inheritable_accessor(:_helper_serial) do + AbstractController::Helpers.next_serial + end end module ClassMethods @@ -58,6 +66,8 @@ module AbstractController # of the helper module. Any methods defined in the block # will be helpers. def helper(*args, &block) + self._helper_serial = AbstractController::Helpers.next_serial + 1 + args.flatten.each do |arg| case arg when Module diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index f021dd8b62..0063d54149 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -2,7 +2,7 @@ module AbstractController module Layouts extend ActiveSupport::Concern - include Renderer + include RenderingController included do extlib_inheritable_accessor(:_layout_conditions) { Hash.new } @@ -76,7 +76,7 @@ module AbstractController when nil self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def _layout(details) - if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts") + if view_paths.exists?("#{_implied_layout_name}", details, "layouts") "#{_implied_layout_name}" else super @@ -88,6 +88,24 @@ module AbstractController end end + def render_to_body(options = {}) + # In the case of a partial with a layout, handle the layout + # here, and make sure the view does not try to handle it + layout = options.delete(:layout) if options.key?(:partial) + + response = super + + # This is a little bit messy. We need to explicitly handle partial + # layouts here since the core lookup logic is in the view, but + # we need to determine the layout based on the controller + if layout + layout = _layout_for_option(layout, options[:_template].details) + response = layout.render(view_context, options[:locals] || {}) { response } + end + + response + end + private # This will be overwritten by _write_layout_method def _layout(details) end @@ -115,7 +133,7 @@ module AbstractController def _find_layout(name, details) # TODO: Make prefix actually part of details in ViewPath#find_by_parts prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" - view_paths.find_by_parts(name, details, prefix) + view_paths.find(name, details, prefix) end # Returns the default layout for this controller and a given set of details. diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/abstract_controller/logger.rb index fd33bd2ddd..fd33bd2ddd 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/abstract_controller/logger.rb diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index 41b7d47458..bb7891fbfd 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -1,7 +1,7 @@ -require "action_controller/abstract/logger" +require "abstract_controller/logger" module AbstractController - module Renderer + module RenderingController extend ActiveSupport::Concern include AbstractController::Logger @@ -19,11 +19,11 @@ module AbstractController # The view class must have the following methods: # View.for_controller[controller] Create a new ActionView instance for a # controller - # View#_render_partial_from_controller[options] + # View#render_partial[options] # - responsible for setting options[:_template] # - Returns String with the rendered partial # options<Hash>:: see _render_partial in ActionView::Base - # View#_render_template_from_controller[template, layout, options, partial] + # View#render_template[template, layout, options, partial] # - Returns String with the rendered template # template<ActionView::Template>:: The template to render # layout<ActionView::Template>:: The layout to render around the template @@ -31,8 +31,8 @@ module AbstractController # partial<Boolean>:: Whether or not the template to render is a partial # # Override this method in a to change the default behavior. - def _action_view - @_action_view ||= ActionView::Base.for_controller(self) + def view_context + @_view_context ||= ActionView::Base.for_controller(self) end # Mostly abstracts the fact that calling render twice is a DoubleRenderError. @@ -54,8 +54,8 @@ module AbstractController # :api: plugin def render_to_body(options = {}) # TODO: Refactor so we can just use the normal template logic for this - if options[:_partial_object] - _action_view._render_partial_from_controller(options) + if options.key?(:partial) + view_context.render_partial(options) else _determine_template(options) _render_template(options) @@ -67,7 +67,7 @@ module AbstractController # # :api: plugin def render_to_string(options = {}) - AbstractController::Renderer.body_to_s(render_to_body(options)) + AbstractController::RenderingController.body_to_s(render_to_body(options)) end # Renders the template from an object. @@ -77,7 +77,7 @@ module AbstractController # _layout<ActionView::Template>:: The layout to wrap the template in (optional) # _partial<TrueClass, FalseClass>:: Whether or not the template to be rendered is a partial def _render_template(options) - _action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial]) + view_context.render_template(options) end # The list of view paths for this controller. See ActionView::ViewPathSet for @@ -111,7 +111,7 @@ module AbstractController def _determine_template(options) name = (options[:_template_name] || action_name).to_s - options[:_template] ||= view_paths.find_by_parts( + options[:_template] ||= view_paths.find( name, { :formats => formats }, options[:_prefix], options[:_partial] ) end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 32572c93c0..37ff618edd 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,62 +1,62 @@ module ActionController - autoload :Base, "action_controller/base/base" - autoload :ConditionalGet, "action_controller/base/conditional_get" - autoload :HideActions, "action_controller/base/hide_actions" - autoload :Http, "action_controller/base/http" - autoload :Layouts, "action_controller/base/layouts" - autoload :RackConvenience, "action_controller/base/rack_convenience" - autoload :Rails2Compatibility, "action_controller/base/compatibility" - autoload :Redirector, "action_controller/base/redirector" - autoload :Renderer, "action_controller/base/renderer" - autoload :RenderOptions, "action_controller/base/render_options" - autoload :Renderers, "action_controller/base/render_options" - autoload :Rescue, "action_controller/base/rescuable" - autoload :Testing, "action_controller/base/testing" - autoload :UrlFor, "action_controller/base/url_for" - autoload :Session, "action_controller/base/session" - autoload :Helpers, "action_controller/base/helpers" + autoload :Base, "action_controller/base" + autoload :ConditionalGet, "action_controller/metal/conditional_get" + autoload :HideActions, "action_controller/metal/hide_actions" + autoload :Metal, "action_controller/metal" + autoload :Layouts, "action_controller/metal/layouts" + autoload :RackConvenience, "action_controller/metal/rack_convenience" + autoload :Rails2Compatibility, "action_controller/metal/compatibility" + autoload :Redirector, "action_controller/metal/redirector" + autoload :RenderingController, "action_controller/metal/rendering_controller" + autoload :RenderOptions, "action_controller/metal/render_options" + autoload :Rescue, "action_controller/metal/rescuable" + autoload :Responder, "action_controller/metal/responder" + autoload :Testing, "action_controller/metal/testing" + autoload :UrlFor, "action_controller/metal/url_for" + autoload :Session, "action_controller/metal/session" + autoload :Helpers, "action_controller/metal/helpers" # Ported modules # require 'action_controller/routing' autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' autoload :Integration, 'action_controller/testing/integration' - autoload :MimeResponds, 'action_controller/base/mime_responds' + autoload :MimeResponds, 'action_controller/metal/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' - autoload :SessionManagement, 'action_controller/base/session_management' + autoload :SessionManagement, 'action_controller/metal/session_management' autoload :TestCase, 'action_controller/testing/test_case' autoload :TestProcess, 'action_controller/testing/process' autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - autoload :Verification, 'action_controller/base/verification' - autoload :Flash, 'action_controller/base/flash' - autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' - autoload :Streaming, 'action_controller/base/streaming' - autoload :HttpAuthentication, 'action_controller/base/http_authentication' - autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' + autoload :Verification, 'action_controller/metal/verification' + autoload :Flash, 'action_controller/metal/flash' + autoload :RequestForgeryProtection, 'action_controller/metal/request_forgery_protection' + autoload :Streaming, 'action_controller/metal/streaming' + autoload :HttpAuthentication, 'action_controller/metal/http_authentication' + autoload :FilterParameterLogging, 'action_controller/metal/filter_parameter_logging' autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/base/cookies' + autoload :Cookies, 'action_controller/metal/cookies' - autoload :ActionControllerError, 'action_controller/base/exceptions' - autoload :SessionRestoreError, 'action_controller/base/exceptions' - autoload :RenderError, 'action_controller/base/exceptions' - autoload :RoutingError, 'action_controller/base/exceptions' - autoload :MethodNotAllowed, 'action_controller/base/exceptions' - autoload :NotImplemented, 'action_controller/base/exceptions' - autoload :UnknownController, 'action_controller/base/exceptions' - autoload :MissingFile, 'action_controller/base/exceptions' - autoload :RenderError, 'action_controller/base/exceptions' - autoload :SessionOverflowError, 'action_controller/base/exceptions' - autoload :UnknownHttpMethod, 'action_controller/base/exceptions' + autoload :ActionControllerError, 'action_controller/metal/exceptions' + autoload :SessionRestoreError, 'action_controller/metal/exceptions' + autoload :RenderError, 'action_controller/metal/exceptions' + autoload :RoutingError, 'action_controller/metal/exceptions' + autoload :MethodNotAllowed, 'action_controller/metal/exceptions' + autoload :NotImplemented, 'action_controller/metal/exceptions' + autoload :UnknownController, 'action_controller/metal/exceptions' + autoload :MissingFile, 'action_controller/metal/exceptions' + autoload :RenderError, 'action_controller/metal/exceptions' + autoload :SessionOverflowError, 'action_controller/metal/exceptions' + autoload :UnknownHttpMethod, 'action_controller/metal/exceptions' autoload :Routing, 'action_controller/routing' end autoload :HTML, 'action_controller/vendor/html-scanner' -autoload :AbstractController, 'action_controller/abstract' +autoload :AbstractController, 'abstract_controller' autoload :Rack, 'action_dispatch' autoload :ActionDispatch, 'action_dispatch' @@ -69,4 +69,4 @@ require 'active_support/core_ext/load_error' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/name_error' -require 'active_support/inflector'
\ No newline at end of file +require 'active_support/inflector' diff --git a/actionpack/lib/action_controller/abstract.rb b/actionpack/lib/action_controller/abstract.rb deleted file mode 100644 index d0eba253b8..0000000000 --- a/actionpack/lib/action_controller/abstract.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "active_support/core_ext/module/attr_internal" -require "active_support/core_ext/module/delegation" - -module AbstractController - autoload :Base, "action_controller/abstract/base" - autoload :Benchmarker, "action_controller/abstract/benchmarker" - autoload :Callbacks, "action_controller/abstract/callbacks" - autoload :Helpers, "action_controller/abstract/helpers" - autoload :Layouts, "action_controller/abstract/layouts" - autoload :Logger, "action_controller/abstract/logger" - autoload :Renderer, "action_controller/abstract/renderer" - # === Exceptions - autoload :ActionNotFound, "action_controller/abstract/exceptions" - autoload :DoubleRenderError, "action_controller/abstract/exceptions" - autoload :Error, "action_controller/abstract/exceptions" -end diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base.rb index e541d24e31..698189bd46 100644 --- a/actionpack/lib/action_controller/base/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,5 +1,5 @@ module ActionController - class Base < Http + class Base < Metal abstract! include AbstractController::Benchmarker @@ -10,8 +10,8 @@ module ActionController include ActionController::HideActions include ActionController::UrlFor include ActionController::Redirector - include ActionController::Renderer - include ActionController::Renderers::All + include ActionController::RenderingController + include ActionController::RenderOptions::All include ActionController::Layouts include ActionController::ConditionalGet include ActionController::RackConvenience @@ -41,7 +41,7 @@ module ActionController module ImplicitRender def send_action(*) ret = super - default_render unless performed? + default_render unless response_body ret end @@ -51,7 +51,7 @@ module ActionController def method_for_action(action_name) super || begin - if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) + if view_paths.exists?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) "default_render" end end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index d8a1662acc..cb0c3a1384 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -129,8 +129,7 @@ module ActionController #:nodoc: end def content_for_layout(controller) - # TODO: Remove this when new base is merged in - template = controller.respond_to?(:template) ? controller.template : controller._action_view + template = controller.view_context template.layout && template.instance_variable_get('@cached_content_for_layout') end end diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 95cba0e411..4ef600bea0 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -36,8 +36,8 @@ module ActionController #:nodoc: def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc: if perform_caching - if cache = read_fragment(name, options) - buffer.concat(cache) + if fragment_exist?(name,options) + buffer.concat(read_fragment(name, options)) else pos = buffer.length block.call diff --git a/actionpack/lib/action_controller/legacy/layout.rb b/actionpack/lib/action_controller/legacy/layout.rb index 3f3d20b95f..43aea0eba2 100644 --- a/actionpack/lib/action_controller/legacy/layout.rb +++ b/actionpack/lib/action_controller/legacy/layout.rb @@ -191,7 +191,7 @@ module ActionController #:nodoc: def memoized_find_layout(layout, formats) #:nodoc: return layout if layout.nil? || layout.respond_to?(:render) prefix = layout.to_s =~ /layouts\// ? nil : "layouts" - view_paths.find_by_parts(layout.to_s, {:formats => formats}, prefix) + view_paths.find(layout.to_s, {:formats => formats}, prefix) end def find_layout(*args) diff --git a/actionpack/lib/action_controller/base/http.rb b/actionpack/lib/action_controller/metal.rb index 3efd1b656f..5333ca497c 100644 --- a/actionpack/lib/action_controller/base/http.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,13 +1,11 @@ -require 'action_controller/abstract' - module ActionController - # ActionController::Http provides a way to get a valid Rack application from a controller. + # ActionController::Metal provides a way to get a valid Rack application from a controller. # # In AbstractController, dispatching is triggered directly by calling #process on a new controller. - # ActionController::Http provides an #action method that returns a valid Rack application for a + # ActionController::Metal provides an #action method that returns a valid Rack application for a # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, # can dispatch directly to the action returned by FooController.action(:index). - class Http < AbstractController::Base + class Metal < AbstractController::Base abstract! # :api: public @@ -72,11 +70,11 @@ module ActionController def call(name, env) @_env = env process(name) - to_rack + to_a end # :api: private - def to_rack + def to_a [status, headers, response_body] end diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 13813ffd17..f94d1c669c 100644 --- a/actionpack/lib/action_controller/base/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -64,6 +64,8 @@ module ActionController cattr_accessor :ip_spoofing_check self.ip_spoofing_check = true + + cattr_accessor :trusted_proxies end # For old tests @@ -72,7 +74,7 @@ module ActionController # TODO: Remove this after we flip def template - @template ||= _action_view + @template ||= view_context end def process_action(*) @@ -141,7 +143,7 @@ module ActionController end def view_paths - _action_view.view_paths + view_context.view_paths end end end diff --git a/actionpack/lib/action_controller/base/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index d287ec4994..8575d30335 100644 --- a/actionpack/lib/action_controller/base/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -29,11 +29,7 @@ module ActionController response.last_modified = options[:last_modified] if options[:last_modified] if options[:public] - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - cache_control.delete("private") - cache_control.delete("no-cache") - cache_control << "public" - response.headers["Cache-Control"] = cache_control.join(', ') + response.cache_control[:public] = true end if request.fresh?(response) @@ -59,14 +55,15 @@ module ActionController elsif args.empty? raise ArgumentError, "too few arguments to head" end - options = args.extract_options! - status = args.shift || options.delete(:status) || :ok + options = args.extract_options! + status = args.shift || options.delete(:status) || :ok + location = options.delete(:location) options.each do |key, value| headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s end - render :nothing => true, :status => status + render :nothing => true, :status => status, :location => location end # Sets the etag and/or last_modified on the response and checks it against @@ -107,21 +104,10 @@ module ActionController # This method will overwrite an existing Cache-Control header. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. def expires_in(seconds, options = {}) #:doc: - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public)) + options.delete(:private) - cache_control << "max-age=#{seconds}" - cache_control.delete("no-cache") - if options[:public] - cache_control.delete("private") - cache_control << "public" - else - cache_control << "private" - end - - # This allows for additional headers to be passed through like 'max-stale' => 5.hours - cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} - - response.headers["Cache-Control"] = cache_control.join(', ') + response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"} end # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or diff --git a/actionpack/lib/action_controller/base/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index d4806623c3..d4806623c3 100644 --- a/actionpack/lib/action_controller/base/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb diff --git a/actionpack/lib/action_controller/base/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index d0811254cb..d0811254cb 100644 --- a/actionpack/lib/action_controller/base/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb index 065e62a37f..065e62a37f 100644 --- a/actionpack/lib/action_controller/base/filter_parameter_logging.rb +++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb diff --git a/actionpack/lib/action_controller/base/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index 590f9be3ac..590f9be3ac 100644 --- a/actionpack/lib/action_controller/base/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 7c52779064..7c52779064 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb diff --git a/actionpack/lib/action_controller/base/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb index af68c772b1..af68c772b1 100644 --- a/actionpack/lib/action_controller/base/hide_actions.rb +++ b/actionpack/lib/action_controller/metal/hide_actions.rb diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 525787bf92..2b62a1be85 100644 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -141,7 +141,7 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') + ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '') end def encode_credentials(user_name, password) @@ -197,9 +197,10 @@ module ActionController return false unless password method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url [true, false].any? do |password_is_ha1| - expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected = expected_response(method, uri, credentials, password, password_is_ha1) expected == credentials[:response] end end diff --git a/actionpack/lib/action_controller/base/layouts.rb b/actionpack/lib/action_controller/metal/layouts.rb index 365351b421..cac529b1ae 100644 --- a/actionpack/lib/action_controller/base/layouts.rb +++ b/actionpack/lib/action_controller/metal/layouts.rb @@ -158,7 +158,7 @@ module ActionController module Layouts extend ActiveSupport::Concern - include ActionController::Renderer + include ActionController::RenderingController include AbstractController::Layouts module ClassMethods diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index f4a4007a43..c8d042acb5 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -177,106 +177,67 @@ module ActionController #:nodoc: # Be sure to check respond_with and respond_to documentation for more examples. # def respond_to(*mimes, &block) - options = mimes.extract_options! raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? - resource = options.delete(:with) - responder = Responder.new - + collector = Collector.new mimes = collect_mimes_from_class_level if mimes.empty? - mimes.each { |mime| responder.send(mime) } - block.call(responder) if block_given? + mimes.each { |mime| collector.send(mime) } + block.call(collector) if block_given? + + if format = request.negotiate_mime(collector.order) + self.formats = [format.to_sym] - if format = request.negotiate_mime(responder.order) - respond_to_block_or_template_or_resource(format, resource, - options, &responder.response_for(format)) + if response = collector.response_for(format) + response.call + else + default_render + end else head :not_acceptable end end - # respond_with allows you to respond an action with a given resource. It - # requires that you set your class with a :respond_to method with the - # formats allowed: + # respond_with wraps a resource around a responder for default representation. + # First it invokes respond_to, if a response cannot be found (ie. no block + # for the request was given and template was not available), it instantiates + # an ActionController::Responder with the controller and resource. # - # class PeopleController < ApplicationController - # respond_to :html, :xml, :json + # ==== Example # - # def index - # @people = Person.find(:all) - # respond_with(@person) - # end + # def index + # @users = User.all + # respond_with(@users) # end # - # When a request comes with format :xml, the respond_with will first search - # for a template as person/index.xml, if the template is not available, it - # will see if the given resource responds to :to_xml. - # - # If neither are available, it will raise an error. + # It also accepts a block to be given. It's used to overwrite a default + # response: # - # Extra parameters given to respond_with are used when :to_format is invoked. - # This allows you to set status and location for several formats at the same - # time. Consider this restful controller response on create for both xml - # and json formats: + # def destroy + # @user = User.find(params[:id]) + # flash[:notice] = "User was successfully created." if @user.save # - # class PeopleController < ApplicationController - # respond_to :xml, :json - # - # def create - # @person = Person.new(params[:person]) - # - # if @person.save - # respond_with(@person, :status => :ok, :location => person_url(@person)) - # else - # respond_with(@person.errors, :status => :unprocessable_entity) - # end + # respond_with(@user) do |format| + # format.html { render } # end # end # - # Finally, respond_with also accepts blocks, as in respond_to. Let's take - # the same controller and create action above and add common html behavior: - # - # class PeopleController < ApplicationController - # respond_to :html, :xml, :json - # - # def create - # @person = Person.new(params[:person]) - # - # if @person.save - # options = { :status => :ok, :location => person_url(@person) } - # - # respond_with(@person, options) do |format| - # format.html { redirect_to options[:location] } - # end - # else - # respond_with(@person.errors, :status => :unprocessable_entity) do - # format.html { render :action => :new } - # end - # end - # end - # end + # All options given to respond_with are sent to the underlying responder, + # except for the option :responder itself. Since the responder interface + # is quite simple (it just needs to respond to call), you can even give + # a proc to it. # def respond_with(resource, options={}, &block) - respond_to(options.merge!(:with => resource), &block) + respond_to(&block) + rescue ActionView::MissingTemplate + (options.delete(:responder) || responder).call(self, resource, options) end - protected - - def respond_to_block_or_template_or_resource(format, resource, options) - self.formats = [format.to_sym] - return yield if block_given? - - begin - default_render - rescue ActionView::MissingTemplate => e - if resource && resource.respond_to?(:"to_#{format.to_sym}") - render options.merge(format.to_sym => resource) - else - raise e - end - end + def responder + ActionController::Responder end + protected + # Collect mimes declared in the class method respond_to valid for the # current action. # @@ -296,7 +257,7 @@ module ActionController #:nodoc: end end - class Responder #:nodoc: + class Collector #:nodoc: attr_accessor :order def initialize diff --git a/actionpack/lib/action_controller/base/rack_convenience.rb b/actionpack/lib/action_controller/metal/rack_convenience.rb index 805157b0e3..5fac445dab 100644 --- a/actionpack/lib/action_controller/base/rack_convenience.rb +++ b/actionpack/lib/action_controller/metal/rack_convenience.rb @@ -20,7 +20,7 @@ module ActionController end # :api: private - def to_rack + def to_a @_response.prepare! @_response.to_a end diff --git a/actionpack/lib/action_controller/base/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb index 20060b001f..20060b001f 100644 --- a/actionpack/lib/action_controller/base/redirector.rb +++ b/actionpack/lib/action_controller/metal/redirector.rb diff --git a/actionpack/lib/action_controller/base/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb index fc9a02626f..0d69ca10df 100644 --- a/actionpack/lib/action_controller/base/render_options.rb +++ b/actionpack/lib/action_controller/metal/render_options.rb @@ -13,7 +13,7 @@ module ActionController <<-RUBY_EVAL if options.key?(:#{r}) _process_options(options) - return _render_#{r}(options[:#{r}], options) + return render_#{r}(options[:#{r}], options) end RUBY_EVAL end @@ -47,12 +47,12 @@ module ActionController end end - module Renderers + module RenderOptions module Json extend RenderOption register_renderer :json - def _render_json(json, options) + def render_json(json, options) json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) json = "#{options[:callback]}(#{json})" unless options[:callback].blank? self.content_type ||= Mime::JSON @@ -64,9 +64,9 @@ module ActionController extend RenderOption register_renderer :js - def _render_js(js, options) + def render_js(js, options) self.content_type ||= Mime::JS - self.response_body = js + self.response_body = js.respond_to?(:to_js) ? js.to_js : js end end @@ -74,7 +74,7 @@ module ActionController extend RenderOption register_renderer :xml - def _render_xml(xml, options) + def render_xml(xml, options) self.content_type ||= Mime::XML self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml end @@ -84,8 +84,8 @@ module ActionController extend RenderOption register_renderer :update - def _render_update(proc, options) - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc) + def render_update(proc, options) + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) self.content_type = Mime::JS self.response_body = generator.to_s end @@ -94,10 +94,10 @@ module ActionController module All extend ActiveSupport::Concern - include ActionController::Renderers::Json - include ActionController::Renderers::Js - include ActionController::Renderers::Xml - include ActionController::Renderers::RJS + include ActionController::RenderOptions::Json + include ActionController::RenderOptions::Js + include ActionController::RenderOptions::Xml + include ActionController::RenderOptions::RJS end end end diff --git a/actionpack/lib/action_controller/base/renderer.rb b/actionpack/lib/action_controller/metal/rendering_controller.rb index 572da451ff..5b1be763ad 100644 --- a/actionpack/lib/action_controller/base/renderer.rb +++ b/actionpack/lib/action_controller/metal/rendering_controller.rb @@ -1,8 +1,8 @@ module ActionController - module Renderer + module RenderingController extend ActiveSupport::Concern - include AbstractController::Renderer + include AbstractController::RenderingController def process_action(*) self.formats = request.formats.map {|x| x.to_sym} @@ -22,7 +22,8 @@ module ActionController _process_options(options) if options.key?(:partial) - _render_partial(options[:partial], options) + options[:partial] = action_name if options[:partial] == true + options[:_details] = {:formats => formats} end super @@ -52,21 +53,6 @@ module ActionController super end - def _render_partial(partial, options) - case partial - when true - options[:_prefix] = _prefix - when String - options[:_prefix] = _prefix unless partial.index('/') - options[:_template_name] = partial - else - options[:_partial_object] = true - return - end - - options[:_partial] = options[:object] || true - end - def _process_options(options) status, content_type, location = options.values_at(:status, :content_type, :location) self.status = status if status diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index ad06657f86..ad06657f86 100644 --- a/actionpack/lib/action_controller/base/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb diff --git a/actionpack/lib/action_controller/base/rescuable.rb b/actionpack/lib/action_controller/metal/rescuable.rb index 029e643d93..029e643d93 100644 --- a/actionpack/lib/action_controller/base/rescuable.rb +++ b/actionpack/lib/action_controller/metal/rescuable.rb diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb new file mode 100644 index 0000000000..9ed99ca623 --- /dev/null +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -0,0 +1,181 @@ +module ActionController #:nodoc: + # Responder is responsible to expose a resource for different mime requests, + # usually depending on the HTTP verb. The responder is triggered when + # respond_with is called. The simplest case to study is a GET request: + # + # class PeopleController < ApplicationController + # respond_to :html, :xml, :json + # + # def index + # @people = Person.find(:all) + # respond_with(@people) + # end + # end + # + # When a request comes, for example with format :xml, three steps happen: + # + # 1) respond_with searches for a template at people/index.xml; + # + # 2) if the template is not available, it will create a responder, passing + # the controller and the resource and invoke :to_xml on it; + # + # 3) if the responder does not respond_to :to_xml, call to_format on it. + # + # === Builtin HTTP verb semantics + # + # Rails default responder holds semantics for each HTTP verb. Depending on the + # content type, verb and the resource status, it will behave differently. + # + # Using Rails default responder, a POST request for creating an object could + # be written as: + # + # def create + # @user = User.new(params[:user]) + # flash[:notice] = 'User was successfully created.' if @user.save + # respond_with(@user) + # end + # + # Which is exactly the same as: + # + # def create + # @user = User.new(params[:user]) + # + # respond_to do |format| + # if @user.save + # flash[:notice] = 'User was successfully created.' + # format.html { redirect_to(@user) } + # format.xml { render :xml => @user, :status => :created, :location => @user } + # else + # format.html { render :action => "new" } + # format.xml { render :xml => @user.errors, :status => :unprocessable_entity } + # end + # end + # end + # + # The same happens for PUT and DELETE requests. + # + # === Nested resources + # + # You can given nested resource as you do in form_for and polymorphic_url. + # Consider the project has many tasks example. The create action for + # TasksController would be like: + # + # def create + # @project = Project.find(params[:project_id]) + # @task = @project.comments.build(params[:task]) + # flash[:notice] = 'Task was successfully created.' if @task.save + # respond_with([@project, @task]) + # end + # + # Giving an array of resources, you ensure that the responder will redirect to + # project_task_url instead of task_url. + # + # Namespaced and singleton resources requires a symbol to be given, as in + # polymorphic urls. If a project has one manager which has many tasks, it + # should be invoked as: + # + # respond_with([@project, :manager, @task]) + # + # Check polymorphic_url documentation for more examples. + # + class Responder + attr_reader :controller, :request, :format, :resource, :resource_location, :options + + def initialize(controller, resource, options={}) + @controller = controller + @request = controller.request + @format = controller.formats.first + @resource = resource.is_a?(Array) ? resource.last : resource + @resource_location = options[:location] || resource + @options = options + end + + delegate :head, :render, :redirect_to, :to => :controller + delegate :get?, :post?, :put?, :delete?, :to => :request + + # Undefine :to_json since it's defined on Object + undef_method :to_json + + # Initializes a new responder an invoke the proper format. If the format is + # not defined, call to_format. + # + def self.call(*args) + responder = new(*args) + method = :"to_#{responder.format}" + responder.respond_to?(method) ? responder.send(method) : responder.to_format + end + + # HTML format does not render the resource, it always attempt to render a + # template. + # + def to_html + if get? + render + elsif has_errors? + render :action => default_action + else + redirect_to resource_location + end + end + + # All others formats try to render the resource given instead. For this + # purpose a helper called display as a shortcut to render a resource with + # the current format. + # + def to_format + return render unless resourceful? + + if get? + display resource + elsif has_errors? + display resource.errors, :status => :unprocessable_entity + elsif post? + display resource, :status => :created, :location => resource_location + else + head :ok + end + end + + protected + + # Checks whether the resource responds to the current format or not. + # + def resourceful? + resource.respond_to?(:"to_#{format}") + end + + # display is just a shortcut to render a resource with the current format. + # + # display @user, :status => :ok + # + # For xml request is equivalent to: + # + # render :xml => @user, :status => :ok + # + # Options sent by the user are also used: + # + # respond_with(@user, :status => :created) + # display(@user, :status => :ok) + # + # Results in: + # + # render :xml => @user, :status => :created + # + def display(resource, given_options={}) + render given_options.merge!(options).merge!(format => resource) + end + + # Check if the resource has errors or not. + # + def has_errors? + resource.respond_to?(:errors) && !resource.errors.empty? + end + + # By default, render the :edit action for html requests with failure, unless + # the verb is post. + # + def default_action + request.post? ? :new : :edit + end + end +end diff --git a/actionpack/lib/action_controller/base/session.rb b/actionpack/lib/action_controller/metal/session.rb index bcedd6e1c7..bcedd6e1c7 100644 --- a/actionpack/lib/action_controller/base/session.rb +++ b/actionpack/lib/action_controller/metal/session.rb diff --git a/actionpack/lib/action_controller/base/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb index ffce8e1bd1..ffce8e1bd1 100644 --- a/actionpack/lib/action_controller/base/session_management.rb +++ b/actionpack/lib/action_controller/metal/session_management.rb diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 9ff4f25f43..57318e8747 100644 --- a/actionpack/lib/action_controller/base/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -1,10 +1,12 @@ +require 'active_support/core_ext/string/bytesize' + module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, # instead of rendering. module Streaming extend ActiveSupport::Concern - include ActionController::Renderer + include ActionController::RenderingController DEFAULT_SEND_FILE_OPTIONS = { :type => 'application/octet-stream'.freeze, @@ -142,7 +144,7 @@ module ActionController #:nodoc: # instead. See ActionController::Base#render for more information. def send_data(data, options = {}) #:doc: logger.info "Sending data #{options[:filename]}" if logger - send_file_headers! options.merge(:length => data.size) + send_file_headers! options.merge(:length => data.bytesize) @performed_render = false render :status => options[:status], :text => data end @@ -179,7 +181,7 @@ module ActionController #:nodoc: # after it displays the "open/save" dialog, which means that if you # hit "open" the file isn't there anymore when the application that # is called for handling the download is run, so let's workaround that - headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' + response.cache_control[:public] ||= false end end end diff --git a/actionpack/lib/action_controller/base/testing.rb b/actionpack/lib/action_controller/metal/testing.rb index a4a1116d9e..a4a1116d9e 100644 --- a/actionpack/lib/action_controller/base/testing.rb +++ b/actionpack/lib/action_controller/metal/testing.rb diff --git a/actionpack/lib/action_controller/base/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 7119c14cd3..7119c14cd3 100644 --- a/actionpack/lib/action_controller/base/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/metal/verification.rb index 951ae1bee1..d3d78e3749 100644 --- a/actionpack/lib/action_controller/base/verification.rb +++ b/actionpack/lib/action_controller/metal/verification.rb @@ -2,7 +2,7 @@ module ActionController #:nodoc: module Verification #:nodoc: extend ActiveSupport::Concern - include AbstractController::Callbacks, Session, Flash, Renderer + include AbstractController::Callbacks, Session, Flash, RenderingController # This module provides a class-level method for specifying that certain # actions are guarded against being called without certain prerequisites @@ -127,4 +127,4 @@ module ActionController #:nodoc: end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/old_base.rb b/actionpack/lib/action_controller/old_base.rb deleted file mode 100644 index dd22bfd617..0000000000 --- a/actionpack/lib/action_controller/old_base.rb +++ /dev/null @@ -1,84 +0,0 @@ -#-- -# Copyright (c) 2004-2009 David Heinemeier Hansson -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#++ - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' - -require File.join(File.dirname(__FILE__), "action_pack") - -module ActionController - # TODO: Review explicit to see if they will automatically be handled by - # the initilizer if they are really needed. - def self.load_all! - [Base, Request, Response, UrlRewriter, UrlWriter] - [ActionDispatch::Http::Headers] - end - - autoload :Base, 'action_controller/base/base' - autoload :Benchmarking, 'action_controller/base/chained/benchmarking' - autoload :Caching, 'action_controller/caching' - autoload :Cookies, 'action_controller/base/cookies' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Filters, 'action_controller/base/chained/filters' - autoload :Flash, 'action_controller/base/chained/flash' - autoload :Helpers, 'action_controller/base/helpers' - autoload :HttpAuthentication, 'action_controller/base/http_authentication' - autoload :Integration, 'action_controller/testing/integration' - autoload :IntegrationTest, 'action_controller/testing/integration' - autoload :Layout, 'action_controller/base/layout' - autoload :MimeResponds, 'action_controller/base/mime_responds' - autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Redirector, 'action_controller/base/redirect' - autoload :Renderer, 'action_controller/base/render' - autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' - autoload :Rescue, 'action_controller/base/rescue' - autoload :Resources, 'action_controller/routing/resources' - autoload :Responder, 'action_controller/base/responder' - autoload :Routing, 'action_controller/routing' - autoload :SessionManagement, 'action_controller/base/session_management' - autoload :Streaming, 'action_controller/base/streaming' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :TestProcess, 'action_controller/testing/process' - autoload :Translation, 'action_controller/translation' - autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser' - autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' - autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - autoload :Verification, 'action_controller/base/verification' - autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' - - module Assertions - autoload :DomAssertions, 'action_controller/testing/assertions/dom' - autoload :ModelAssertions, 'action_controller/testing/assertions/model' - autoload :ResponseAssertions, 'action_controller/testing/assertions/response' - autoload :RoutingAssertions, 'action_controller/testing/assertions/routing' - autoload :SelectorAssertions, 'action_controller/testing/assertions/selector' - autoload :TagAssertions, 'action_controller/testing/assertions/tag' - end -end - -autoload :HTML, 'action_controller/vendor/html-scanner' - -require 'action_dispatch' -require 'action_view' diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index b4408e4e1d..1165c3b7c5 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -36,21 +36,6 @@ module ActionController JOIN = '_'.freeze NEW = 'new'.freeze - # Returns plural/singular for a record or class. Example: - # - # partial_path(post) # => "posts/post" - # partial_path(Person) # => "people/person" - # partial_path(Person, "admin/games") # => "admin/people/person" - def partial_path(record_or_class, controller_path = nil) - name = model_name_from_record_or_class(record_or_class) - - if controller_path && controller_path.include?("/") - "#{File.dirname(controller_path)}/#{name.partial_path}" - else - name.partial_path - end - end - # The DOM class convention is to use the singular form of an object or class. Examples: # # dom_class(post) # => "post" diff --git a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb index 159d869a54..2adf3575a7 100644 --- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb @@ -50,6 +50,7 @@ module ActionController # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1" # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1" # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1" + # polymorphic_url(Comment) # => "http://example.com/comments" # # ==== Options # @@ -70,6 +71,9 @@ module ActionController # record = Comment.new # polymorphic_url(record) # same as comments_url() # + # # the class of a record will also map to the collection + # polymorphic_url(Comment) # same as comments_url() + # def polymorphic_url(record_or_hash_or_array, options = {}) if record_or_hash_or_array.kind_of?(Array) record_or_hash_or_array = record_or_hash_or_array.compact @@ -86,17 +90,19 @@ module ActionController else [ record_or_hash_or_array ] end - inflection = - case - when options[:action].to_s == "new" - args.pop - :singular - when record.respond_to?(:new_record?) && record.new_record? - args.pop - :plural - else - :singular - end + inflection = if options[:action].to_s == "new" + args.pop + :singular + elsif (record.respond_to?(:new_record?) && record.new_record?) || + (record.respond_to?(:destroyed?) && record.destroyed?) + args.pop + :plural + elsif record.is_a?(Class) + args.pop + :plural + else + :singular + end args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)} diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 040a7e2cb6..0c499f3b46 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -463,13 +463,6 @@ module ActionController routes_by_controller[controller][action][merged.keys] end - def routes_for_controller_and_action(controller, action) - selected = routes.select do |route| - route.matches_controller_and_action? controller, action - end - (selected.length == routes.length) ? routes : selected - end - def routes_for_controller_and_action_and_keys(controller, action, keys) selected = routes.select do |route| route.matches_controller_and_action? controller, action diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index e7c64d0942..d32d5562e8 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -52,7 +52,7 @@ module ActionController #:nodoc: class TestResponse < ActionDispatch::TestResponse def recycle! @status = 200 - @header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS) + @header = Rack::Utils::HeaderHash.new @writer = lambda { |x| @body << x } @block = nil @length = 0 diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 5f9463eb91..4190fa21cd 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -246,7 +246,7 @@ module ActionDispatch remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].scan(/[^,\s]+/) unless remote_addr_list.blank? - not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES} + not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES || addr =~ ActionController::Base.trusted_proxies} return not_trusted_addrs.first unless not_trusted_addrs.empty? end remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') @@ -265,7 +265,7 @@ EOM end if remote_ips - while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip + while remote_ips.size > 1 && (TRUSTED_PROXIES =~ remote_ips.last.strip || ActionController::Base.trusted_proxies =~ remote_ips.last.strip) remote_ips.pop end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index e58b4b5e19..03d1780b77 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -32,8 +32,8 @@ module ActionDispatch # :nodoc: # end # end class Response < Rack::Response - DEFAULT_HEADERS = { "Cache-Control" => "no-cache" } attr_accessor :request + attr_reader :cache_control attr_writer :header alias_method :headers=, :header= @@ -42,21 +42,26 @@ module ActionDispatch # :nodoc: def initialize super - @header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS) + @cache_control = {} + @header = Rack::Utils::HeaderHash.new + end + + def status=(status) + @status = status.to_i end # The response code of the request def response_code - status.to_s[0,3].to_i rescue 0 + @status end # Returns a String to ensure compatibility with Net::HTTPResponse def code - status.to_s.split(' ')[0] + @status.to_s end def message - status.to_s.split(' ',2)[1] || StatusCodes::STATUS_CODES[response_code] + StatusCodes::STATUS_CODES[@status] end alias_method :status_message, :message @@ -142,10 +147,7 @@ module ActionDispatch # :nodoc: def prepare! assign_default_content_type_and_charset! handle_conditional_get! - set_content_length! - convert_content_type! - convert_language! - convert_cookies! + self["Set-Cookie"] ||= "" end def each(&callback) @@ -196,22 +198,24 @@ module ActionDispatch # :nodoc: private def handle_conditional_get! - if etag? || last_modified? + if etag? || last_modified? || !cache_control.empty? set_conditional_cache_control! elsif nonempty_ok_response? self.etag = body if request && request.etag_matches?(etag) - self.status = '304 Not Modified' + self.status = 304 self.body = [] end set_conditional_cache_control! + else + headers["Cache-Control"] = "no-cache" end end def nonempty_ok_response? - ok = !status || status.to_s[0..2] == '200' + ok = !@status || @status == 200 ok && string_body? end @@ -220,40 +224,20 @@ module ActionDispatch # :nodoc: end def set_conditional_cache_control! - if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control'] - headers['Cache-Control'] = 'private, max-age=0, must-revalidate' + if cache_control.empty? + cache_control.merge!(:public => false, :max_age => 0, :must_revalidate => true) end - end - def convert_content_type! - headers['Content-Type'] ||= "text/html" - headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset'] - end + public_cache, max_age, must_revalidate, extras = + cache_control.values_at(:public, :max_age, :must_revalidate, :extras) - # Don't set the Content-Length for block-based bodies as that would mean - # reading it all into memory. Not nice for, say, a 2GB streaming file. - def set_content_length! - if status && status.to_s[0..2] == '204' - headers.delete('Content-Length') - elsif length = headers['Content-Length'] - headers['Content-Length'] = length.to_s - elsif string_body? && (!status || status.to_s[0..2] != '304') - headers["Content-Length"] = Rack::Utils.bytesize(body).to_s - end - end - - def convert_language! - headers["Content-Language"] = headers.delete("language") if headers["language"] - end + options = [] + options << "max-age=#{max_age}" if max_age + options << (public_cache ? "public" : "private") + options << "must-revalidate" if must_revalidate + options.concat(extras) if extras - def convert_cookies! - headers['Set-Cookie'] = - if header = headers['Set-Cookie'] - header = header.split("\n") if header.respond_to?(:to_str) - header.compact - else - [] - end + headers["Cache-Control"] = options.join(", ") end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index dd75cda6b9..d22adfa749 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -345,14 +345,17 @@ module ActionDispatch # # Use the first argument to narrow down assertions to only statements # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>, - # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and - # <tt>:insert_html</tt>. + # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>, + # <tt>:insert_html</tt> and <tt>:redirect</tt>. # # Use the argument <tt>:insert</tt> followed by an insertion position to narrow # down the assertion to only statements that insert elements in that # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt> # and <tt>:after</tt>. # + # Use the argument <tt>:redirect</tt> follwed by a path to check that an statement + # which redirects to the specified path is generated. + # # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will # be ignored as there is no HTML passed for this statement. # @@ -399,6 +402,9 @@ module ActionDispatch # # # The same, but shorter. # assert_select "ol>li", 4 + # + # # Checking for a redirect. + # assert_select_rjs :redirect, root_path def assert_select_rjs(*args, &block) rjs_type = args.first.is_a?(Symbol) ? args.shift : nil id = args.first.is_a?(String) ? args.shift : nil @@ -576,7 +582,8 @@ module ActionDispatch :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)", :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)", :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", - :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)" + :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", + :redirect => "window.location.href = #{RJS_ANY_ID}" } [:remove, :show, :hide, :toggle].each do |action| RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)" diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 9e696af83b..c171a5a8f5 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -164,6 +164,9 @@ module ActionView #:nodoc: # # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details. class Base + module Subclasses + end + include Helpers, Rendering, Partials, ::ERB::Util extend ActiveSupport::Memoizable @@ -195,14 +198,16 @@ module ActionView #:nodoc: attr_internal :request, :layout - delegate :controller_path, :to => :controller, :allow_nil => true + def controller_path + @controller_path ||= controller && controller.controller_path + end delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers, :flash, :action_name, :controller_name, :to => :controller delegate :logger, :to => :controller, :allow_nil => true - delegate :find_by_parts, :to => :view_paths + delegate :find, :to => :view_paths include Context @@ -210,30 +215,35 @@ module ActionView #:nodoc: ActionView::PathSet.new(Array(value)) end + extlib_inheritable_accessor :helpers attr_reader :helpers - class ProxyModule < Module - def initialize(receiver) - @receiver = receiver - end + def self.for_controller(controller) + @views ||= {} - def include(*args) - super(*args) - @receiver.extend(*args) - end - end + # TODO: Decouple this so helpers are a separate concern in AV just like + # they are in AC. + if controller.class.respond_to?(:_helper_serial) + klass = @views[controller.class._helper_serial] ||= Class.new(self) do + Subclasses.const_set(controller.class.name.gsub(/::/, '__'), self) - def self.for_controller(controller) - new(controller.class.view_paths, {}, controller).tap do |view| - view.helpers.include(controller._helpers) if controller.respond_to?(:_helpers) + if controller.respond_to?(:_helpers) + include controller._helpers + self.helpers = controller._helpers + end + end + else + klass = self end + + klass.new(controller.class.view_paths, {}, controller) end def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: @formats = formats || [:html] @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } @controller = controller - @helpers = ProxyModule.new(self) + @helpers = self.class.helpers || Module.new @_content_for = Hash.new {|h,k| h[k] = "" } self.view_paths = view_paths end @@ -248,7 +258,7 @@ module ActionView #:nodoc: def with_template(current_template) _evaluate_assigns_and_ivars last_template, self.template = template, current_template - last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols + last_formats, self.formats = formats, current_template.formats yield ensure self.template, self.formats = last_template, last_formats diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index f212fe25eb..df078a7151 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -12,11 +12,11 @@ module ActionView # # Context.for_controller[controller] Create a new ActionView instance for a # controller - # Context#_render_partial_from_controller[options] + # Context#render_partial[options] # - responsible for setting options[:_template] # - Returns String with the rendered partial # options<Hash>:: see _render_partial in ActionView::Base - # Context#_render_template_from_controller[template, layout, options, partial] + # Context#render_template[template, layout, options, partial] # - Returns String with the rendered template # template<ActionView::Template>:: The template to render # layout<ActionView::Template>:: The layout to render around the template diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 4fd7f7d83c..3e6e62237d 100644 --- a/actionpack/lib/action_view/helpers/active_model_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -278,9 +278,7 @@ module ActionView end %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| - define_method meth do |*| - error_wrapping(super) - end + module_eval "def #{meth}(*) error_wrapping(super) end" end def error_wrapping(html_tag) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 3fde79dfa4..c71840d41f 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -171,7 +171,7 @@ module ActionView end # Computes the path to a javascript asset in the public javascripts directory. - # If the +source+ filename has no extension, .js will be appended. + # If the +source+ filename has no extension, .js will be appended (except for explicit URIs) # Full paths from the document root will be passed through. # Used internally by javascript_include_tag to build the script path. # @@ -179,7 +179,7 @@ module ActionView # javascript_path "xmlhr" # => /javascripts/xmlhr.js # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js - # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js + # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr # javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js def javascript_path(source) compute_public_path(source, 'javascripts', 'js') @@ -337,7 +337,7 @@ module ActionView end # Computes the path to a stylesheet asset in the public stylesheets directory. - # If the +source+ filename has no extension, <tt>.css</tt> will be appended. + # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs). # Full paths from the document root will be passed through. # Used internally by +stylesheet_link_tag+ to build the stylesheet path. # @@ -345,8 +345,8 @@ module ActionView # stylesheet_path "style" # => /stylesheets/style.css # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css # stylesheet_path "/dir/style.css" # => /dir/style.css - # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css - # stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css + # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style + # stylesheet_path "http://www.railsapplication.com/css/style.css" # => http://www.railsapplication.com/css/style.css def stylesheet_path(source) compute_public_path(source, 'stylesheets', 'css') end @@ -557,7 +557,7 @@ module ActionView # video_tag("trailer.ogg") # => # <video src="/videos/trailer.ogg" /> # video_tag("trailer.ogg", :controls => true, :autobuffer => true) # => - # <video autobuffer="true" controls="true" src="/videos/trailer.ogg" /> + # <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" /> # video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # => # <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" /> # video_tag("/trailers/hd.avi", :size => "16x16") # => @@ -629,11 +629,11 @@ module ActionView has_request = @controller.respond_to?(:request) source_ext = File.extname(source)[1..-1] - if ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")))) + if ext && !is_uri?(source) && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")))) source += ".#{ext}" end - unless source =~ %r{^[-a-z]+://} + unless is_uri?(source) source = "/#{dir}/#{source}" unless source[0] == ?/ source = rewrite_asset_path(source) @@ -645,10 +645,10 @@ module ActionView end end - if include_host && source !~ %r{^[-a-z]+://} + if include_host && !is_uri?(source) host = compute_asset_host(source) - if has_request && !host.blank? && host !~ %r{^[-a-z]+://} + if has_request && !host.blank? && !is_uri?(host) host = "#{@controller.request.protocol}#{host}" end @@ -658,6 +658,10 @@ module ActionView end end + def is_uri?(path) + path =~ %r{^[-a-z]+://} + end + # Pick an asset host for this source. Returns +nil+ if no host is set, # the host if no wildcard is set, the host interpolated with the # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), @@ -798,7 +802,7 @@ module ActionView end def asset_file_path!(path) - unless path =~ %r{^[-a-z]+://} + unless is_uri?(path) absolute_path = asset_file_path(path) raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path) return absolute_path diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index dc4497581c..9951e11a37 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -98,7 +98,7 @@ module ActionView options[:schema_date] = "2005" # The Atom spec copyright date end - xml = options[:xml] || eval("xml", block.binding) + xml = options.delete(:xml) || eval("xml", block.binding) xml.instruct! if options[:instruct] options[:instruct].each do |target,attrs| diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 2d1d19d5f3..72f162cb20 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -872,8 +872,8 @@ module ActionView private def add_default_name_and_id_for_value(tag_value, options) - if tag_value - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + unless tag_value.nil? + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase specified_id = options["id"] add_default_name_and_id(options) options["id"] += "_#{pretty_tag_value}" unless specified_id @@ -932,6 +932,14 @@ module ActionView attr_accessor :object_name, :object, :options + def self.model_name + @model_name ||= Struct.new(:partial_path).new(name.demodulize.underscore.sub!(/_builder$/, '')) + end + + def to_model + self + end + def initialize(object_name, object, template, options, proc) @nested_child_index = {} @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index e126b35e90..1d851ecbd7 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -79,6 +79,13 @@ module ActionView # # <option>Paris</option><option>Rome</option></select> def select_tag(name, option_tags = nil, options = {}) html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name + if blank = options.delete(:include_blank) + if blank.kind_of?(String) + option_tags = "<option value=\"\">#{blank}</option>" + option_tags + else + option_tags = "<option value=\"\"></option>" + option_tags + end + end content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end @@ -263,7 +270,7 @@ module ActionView escape = options.key?("escape") ? options.delete("escape") : true content = html_escape(content) if escape - content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) + content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options) end # Creates a check box form input tag. diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 999d5b34fc..897a7cc348 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -215,7 +215,7 @@ module ActionView delimiter ||= (options[:delimiter] || defaults[:delimiter]) begin - rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision + rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision number_with_delimiter("%01.#{precision}f" % rounded_number, :separator => separator, :delimiter => delimiter) diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 66d7592874..ff5a2134ff 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -8,7 +8,10 @@ module ActionView module TagHelper include ERB::Util - BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set + BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer + autoplay controls loop selected hidden scoped async + defer reversed ismap seemless muted required + autofocus novalidate formnovalidate open).to_set BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym }) # Returns an empty HTML tag of type +name+ which by default is XHTML @@ -131,16 +134,14 @@ module ActionView def tag_options(options, escape = true) unless options.blank? attrs = [] - if escape - options.each_pair do |key, value| - if BOOLEAN_ATTRIBUTES.include?(key) - attrs << %(#{key}="#{key}") if value - else - attrs << %(#{key}="#{escape_once(value)}") if !value.nil? - end + options.each_pair do |key, value| + if BOOLEAN_ATTRIBUTES.include?(key) + attrs << %(#{key}="#{key}") if value + elsif !value.nil? + final_value = value.is_a?(Array) ? value.join(" ") : value + final_value = escape_once(final_value) if escape + attrs << %(#{key}="#{final_value}") end - else - attrs = options.map { |key, value| %(#{key}="#{value}") } end " #{attrs.sort * ' '}" unless attrs.empty? end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index c3ce4c671e..1d92bcb763 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -33,13 +33,15 @@ module ActionView end # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt> - # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "..."). + # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...") + # for a total length not exceeding <tt>:length</tt>. + # # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. # # ==== Examples # # truncate("Once upon a time in a world far far away") - # # => Once upon a time in a world f... + # # => Once upon a time in a world... # # truncate("Once upon a time in a world far far away", :separator => ' ') # # => Once upon a time in a world... @@ -48,19 +50,19 @@ module ActionView # # => Once upon a... # # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") - # # => And they found that many (clipped) + # # => And they found t(clipped) # - # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25) + # # => And they f... (continued) # # You can still use <tt>truncate</tt> with the old API that accepts the # +length+ as its optional second and the +ellipsis+ as its # optional third parameter: # truncate("Once upon a time in a world far far away", 14) - # # => Once upon a time in a world f... + # # => Once upon a... # - # truncate("And they found that many people were sleeping better.", 15, "... (continued)") - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", 25, "... (continued)") + # # => And they f... (continued) def truncate(text, *args) options = args.extract_options! unless args.empty? @@ -239,12 +241,20 @@ module ActionView # # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.) # # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>" - def textilize(text) + # + # textilize("This is worded <strong>strongly</strong>") + # # => "<p>This is worded <strong>strongly</strong></p>" + # + # textilize("This is worded <strong>strongly</strong>", :filter_html) + # # => "<p>This is worded <strong>strongly</strong></p>" + # + def textilize(text, *options) + options ||= [:hard_breaks] + if text.blank? "" else - textilized = RedCloth.new(text, [ :hard_breaks ]) - textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=) + textilized = RedCloth.new(text, options) textilized.to_html end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 074b475819..4001757a9b 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -33,12 +33,12 @@ module ActionView #:nodoc: super(*objs.map { |obj| self.class.type_cast(obj) }) end - def find_by_parts(path, details = {}, prefix = nil, partial = false) + def find(path, details = {}, prefix = nil, partial = false) # template_path = path.sub(/^\//, '') template_path = path each do |load_path| - if template = load_path.find_by_parts(template_path, details, prefix, partial) + if template = load_path.find(template_path, details, prefix, partial) return template end end @@ -48,11 +48,11 @@ module ActionView #:nodoc: raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path} - #{details.inspect} - partial: #{!!partial}") end - def find_by_parts?(path, extension = nil, prefix = nil, partial = false) + def exists?(path, extension = nil, prefix = nil, partial = false) template_path = path.sub(/^\//, '') each do |load_path| - return true if template = load_path.find_by_parts(template_path, extension, prefix, partial) + return true if template = load_path.find(template_path, extension, prefix, partial) end false end @@ -62,7 +62,7 @@ module ActionView #:nodoc: template_path = original_template_path.sub(/^\//, '') each do |load_path| - if template = load_path.find_by_parts(template_path, format) + if template = load_path.find(template_path, format) return template # Try to find html version if the format is javascript elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"] diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index ccb14d513a..64f08c447d 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -170,133 +170,117 @@ module ActionView # <%- end -%> # <% end %> module Partials - extend ActiveSupport::Memoizable extend ActiveSupport::Concern - - included do - attr_accessor :_partial - end - def _render_partial_from_controller(*args) - @assigns_added = false - _render_partial(*args) - end - - def _render_partial(options = {}) #:nodoc: - options[:locals] ||= {} + class PartialRenderer + def self.partial_names + @partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new } + end - case path = partial = options[:partial] - when *_array_like_objects - return _render_partial_collection(partial, options) - else - if partial.is_a?(ActionView::Helpers::FormBuilder) - path = partial.class.to_s.demodulize.underscore.sub(/_builder$/, '') - options[:locals].merge!(path.to_sym => partial) - elsif !partial.is_a?(String) - options[:object] = object = partial - path = ActionController::RecordIdentifier.partial_path(object, controller_path) - end - _, _, prefix, object = parts = partial_parts(path, options) - parts[1] = {:formats => parts[1]} - template = find_by_parts(*parts) - _render_partial_object(template, options, (object unless object == true)) + def self.formats + @formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}} end - end - private - def partial_parts(name, options) - segments = name.split("/") - parts = segments.pop.split(".") + def initialize(view_context, options, block) + partial = options[:partial] - case parts.size - when 1 - parts - when 2, 3 - extension = parts.delete_at(1).to_sym - if formats.include?(extension) - self.formats.replace [extension] - end - parts.pop if parts.size == 2 - end + @view = view_context + @options = options + @locals = options[:locals] || {} + @block = block + + # Set up some instance variables to speed up memoizing + @partial_names = self.class.partial_names[@view.controller.class] + @templates = self.class.formats + @format = view_context.formats - path = parts.join(".") - prefix = segments[0..-1].join("/") - prefix = prefix.blank? ? controller_path : prefix - parts = [path, formats, prefix] - parts.push options[:object] || true + # Set up the object and path + @object = partial.is_a?(String) ? options[:object] : partial + @path = partial_path(partial) end - def _render_partial_with_block(layout, block, options) - @_proc_for_layout = block - concat(_render_partial(options.merge(:partial => layout))) - ensure - @_proc_for_layout = nil + def render + return render_collection if collection + + template = find_template + render_template(template, @object || @locals[template.variable_name]) end - def _render_partial_with_layout(layout, options) - if layout - prefix = controller && !layout.include?("/") ? controller.controller_path : nil - layout = find_by_parts(layout, {:formats => formats}, prefix, true) + def render_collection + # Even if no template is rendered, this will ensure that the MIME type + # for the empty response is the same as the provided template + @options[:_template] = default_template = find_template + + return nil if collection.blank? + + if @options.key?(:spacer_template) + spacer = find_template(@options[:spacer_template]).render(@view, @locals) end - content = _render_partial(options) - return _render_content_with_layout(content, layout, options[:locals]) - end - def _array_like_objects - array_like = [Array] - if defined?(ActiveRecord) - array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope) + segments = [] + + collection.each_with_index do |object, index| + template = default_template || find_template(partial_path(object)) + @locals[template.counter_name] = index + segments << render_template(template, object) end - array_like + + segments.join(spacer) end - def _render_partial_object(template, options, object = nil) - if options.key?(:collection) - _render_partial_collection(options.delete(:collection), options, template) - else - locals = (options[:locals] ||= {}) - object ||= locals[:object] || locals[template.variable_name] - - _set_locals(object, locals, template, options) - - options[:_template] = template - - _render_template(template, locals) + def render_template(template, object = @object) + @options[:_template] ||= template + + # TODO: is locals[:object] really necessary? + @locals[:object] = @locals[template.variable_name] = object + @locals[@options[:as]] = object if @options[:as] + + content = @view._render_single_template(template, @locals, &@block) + return content if @block || !@options[:layout] + find_template(@options[:layout]).render(@view, @locals) { content } + end + + + private + def collection + @collection ||= if @object.respond_to?(:to_ary) + @object + elsif @options.key?(:collection) + @options[:collection] || [] end end - def _set_locals(object, locals, template, options) - locals[:object] = locals[template.variable_name] = object - locals[options[:as]] = object if options[:as] + def find_template(path = @path) + return if !path + @templates[path][@view.controller_path][@format][I18n.locale] ||= begin + prefix = @view.controller.controller_path unless path.include?(?/) + @view.find(path, {:formats => @view.formats}, prefix, true) + end end - def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc: - return nil if collection.blank? - - spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : '' + def partial_path(object = @object) + return object if object.is_a?(String) + @partial_names[object.class] ||= begin + return nil unless object.respond_to?(:to_model) - locals = (options[:locals] ||= {}) - index, @_partial_path = 0, nil - collection.map do |object| - options[:_template] = template = passed_template || begin - _partial_path = - ActionController::RecordIdentifier.partial_path(object, controller_path) - template = _pick_partial_template(_partial_path) + object.to_model.class.model_name.partial_path.dup.tap do |partial| + path = @view.controller_path + partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/) end - - _set_locals(object, locals, template, options) - locals[template.counter_name] = index - - index += 1 - - _render_template(template, locals) - end.join(spacer) + end end + end + + def render_partial(options) + @assigns_added = false + # TODO: Handle other details here. + self.formats = options[:_details][:formats] if options[:_details] + _render_partial(options) + end + + def _render_partial(options, &block) #:nodoc: + PartialRenderer.new(self, options, block).render + end - def _pick_partial_template(partial_path) #:nodoc: - prefix = controller_path unless partial_path.include?('/') - find_by_parts(partial_path, {:formats => formats}, prefix, true) - end - memoize :_pick_partial_template end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 162e38c484..c7afc56e3b 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -10,24 +10,24 @@ module ActionView # # 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 = {}, local_assigns = {}, &block) #:nodoc: - local_assigns ||= {} - - @exempt_from_layout = true - + def render(options = {}, locals = {}, &block) #:nodoc: case options + when String, NilClass + _render_partial(:partial => options, :locals => locals || {}) when Hash - options[:locals] ||= {} layout = options[:layout] - - return _render_partial_with_layout(layout, options) if options.key?(:partial) - return _render_partial_with_block(layout, block, options) if block_given? - - layout = find_by_parts(layout, {:formats => formats}) if layout - + + if block_given? + return concat(_render_partial(options.merge(:partial => layout), &block)) + elsif options.key?(:partial) + return _render_partial(options) + end + + layout = find(layout, {:formats => formats}) if layout + if file = options[:file] - template = find_by_parts(file, {:formats => formats}) - _render_template_with_layout(template, layout, :locals => options[:locals]) + template = find(file, {:formats => formats}) + _render_template(template, layout, :locals => options[:locals] || {}) elsif inline = options[:inline] _render_inline(inline, layout, options) elsif text = options[:text] @@ -35,35 +35,33 @@ module ActionView end when :update update_page(&block) - when String, NilClass - _render_partial(:partial => options, :locals => local_assigns) end end - - def _render_content_with_layout(content, layout, locals) + + def _render_content(content, layout, locals) return content unless layout - + locals ||= {} if controller && layout @_layout = layout.identifier logger.info("Rendering template within #{layout.identifier}") if logger end - + begin old_content, @_content_for[:layout] = @_content_for[:layout], content @cached_content_for_layout = @_content_for[:layout] - _render_template(layout, locals) + _render_single_template(layout, locals) ensure @_content_for[:layout] = old_content end end - # You can think of a layout as a method that is called with a block. This method - # returns the block that the layout is called with. If the user calls yield :some_name, - # the block, by default, returns content_for(:some_name). If the user calls yield, - # the default block returns content_for(:layout). + # You can think of a layout as a method that is called with a block. _layout_for + # returns the contents that are yielded to the layout. If the user calls yield + # :some_name, the block, by default, returns content_for(:some_name). If the user + # calls yield, the default block returns content_for(:layout). # # The user can override this default by passing a block to the layout. # @@ -92,15 +90,28 @@ module ActionView # In this case, the layout would receive the block passed into <tt>render :layout</tt>, # and the Struct specified in the layout would be passed into the block. The result # would be <html>Hello David</html>. - def layout_proc(name) - @_default_layout ||= proc { |*names| @_content_for[names.first || :layout] } - !@_content_for.key?(name) && @_proc_for_layout || @_default_layout + def _layout_for(names, &block) + with_output_buffer do + # This is due to the potentially ambiguous use of yield when + # a block is passed in to a template *and* there is a content_for() + # of the same name. Suggested solution: require explicit use of content_for + # in these ambiguous cases. + # + # We would be able to continue supporting yield in all non-ambiguous + # cases. Question: should we deprecate yield in favor of content_for + # and reserve yield for cases where there is a yield into a real block? + if @_content_for.key?(names.first) || !block_given? + return @_content_for[names.first || :layout] + else + return yield(names) + end + end end - def _render_template(template, local_assigns = {}) + def _render_single_template(template, locals = {}, &block) with_template(template) do - template.render(self, local_assigns) do |*names| - capture(*names, &layout_proc(names.first)) + template.render(self, locals) do |*names| + _layout_for(names, &block) end end rescue Exception => e @@ -115,32 +126,42 @@ module ActionView def _render_inline(inline, layout, options) handler = Template.handler_class_for_extension(options[:type] || "erb") template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) - content = _render_template(template, options[:locals] || {}) - layout ? _render_content_with_layout(content, layout, options[:locals]) : content + content = _render_single_template(template, options[:locals] || {}) + layout ? _render_content(content, layout, options[:locals]) : content end def _render_text(text, layout, options) - layout ? _render_content_with_layout(text, layout, options[:locals]) : text + layout ? _render_content(text, layout, options[:locals]) : text end - def _render_template_from_controller(*args) + # This is the API to render a ViewContext's template from a controller. + # + # Internal Options: + # _template:: The Template object to render + # _layout:: The layout, if any, to wrap the Template in + # _partial:: true if the template is a partial + def render_template(options) @assigns_added = nil - _render_template_with_layout(*args) + template, layout, partial = options.values_at(:_template, :_layout, :_partial) + _render_template(template, layout, options, partial) end - def _render_template_with_layout(template, layout = nil, options = {}, partial = false) - logger && logger.info("Rendering #{template.identifier}#{' (#{options[:status]})' if options[:status]}") + def _render_template(template, layout = nil, options = {}, partial = nil) + logger && logger.info do + msg = "Rendering #{template.identifier}" + msg << " (#{options[:status]})" if options[:status] + msg + end locals = options[:locals] || {} content = if partial - object = partial unless partial == true - _render_partial_object(template, options, object) + _render_partial_object(template, options) else - _render_template(template, locals) + _render_single_template(template, locals) end - - layout ? _render_content_with_layout(content, layout, locals) : content + + _render_content(content, layout, locals) end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index d15f53a11b..ebfc6cc8ce 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -10,7 +10,7 @@ module ActionView end # Normalizes the arguments and passes it on to find_template - def find_by_parts(*args) + def find(*args) find_all_by_parts(*args).first end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 4145045e2d..33d3f79ad3 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -7,19 +7,22 @@ require "action_view/template/resolver" module ActionView class Template extend TemplateHandlers - attr_reader :source, :identifier, :handler, :mime_type, :details + attr_reader :source, :identifier, :handler, :mime_type, :formats, :details def initialize(source, identifier, handler, details) @source = source @identifier = identifier @handler = handler @details = details + @method_names = {} format = details.delete(:format) || begin # TODO: Clean this up handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" end @mime_type = Mime::Type.lookup_by_extension(format.to_s) + @formats = [format.to_sym] + @formats << :html if format == :js @details[:formats] = Array.wrap(format.to_sym) end @@ -30,12 +33,12 @@ module ActionView # TODO: Figure out how to abstract this def variable_name - identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym + @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym end # TODO: Figure out how to abstract this def counter_name - "#{variable_name}_counter".to_sym + @counter_name ||= "#{variable_name}_counter".to_sym end # TODO: kill hax @@ -90,7 +93,8 @@ module ActionView def build_method_name(locals) # TODO: is locals.keys.hash reliably the same? - "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + @method_names[locals.keys.hash] ||= + "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") end end end diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index 81944ff546..9f12e5e0a8 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -15,7 +15,9 @@ module ActionView #:nodoc: def render(*) self end def mime_type() @content_type end - + + def formats() [mime_type] end + def partial?() false end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 3f3951509a..e51744d095 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -9,14 +9,14 @@ module ActionView end attr_internal :rendered - alias_method :_render_template_without_template_tracking, :_render_template - def _render_template(template, local_assigns = {}) + alias_method :_render_template_without_template_tracking, :_render_single_template + def _render_single_template(template, local_assigns, &block) if template.respond_to?(:identifier) && template.present? @_rendered[:partials][template] += 1 if template.partial? @_rendered[:template] ||= [] @_rendered[:template] << template end - _render_template_without_template_tracking(template, local_assigns) + _render_template_without_template_tracking(template, local_assigns, &block) end end diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index 56ec6a6a31..9438a4dfc9 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -27,7 +27,7 @@ module AbstractController # Test Render mixin # ==== class RenderingController < AbstractController::Base - include Renderer + include ::AbstractController::RenderingController def _prefix() end @@ -65,8 +65,8 @@ module AbstractController self.response_body = render_to_string :_template_name => "naked_render.erb" end end - - class TestRenderer < ActiveSupport::TestCase + + class TestRenderingController < ActiveSupport::TestCase test "rendering templates works" do result = Me2.new.process(:index) assert_equal "Hello from index.erb", result.response_body @@ -139,10 +139,10 @@ module AbstractController private def self.layout(formats) begin - view_paths.find_by_parts(name.underscore, {:formats => formats}, "layouts") + view_paths.find(name.underscore, {:formats => formats}, "layouts") rescue ActionView::MissingTemplate begin - view_paths.find_by_parts("application", {:formats => formats}, "layouts") + view_paths.find("application", {:formats => formats}, "layouts") rescue ActionView::MissingTemplate end end diff --git a/actionpack/test/abstract_controller/helper_test.rb b/actionpack/test/abstract_controller/helper_test.rb index 0a2535f834..e9a60c0307 100644 --- a/actionpack/test/abstract_controller/helper_test.rb +++ b/actionpack/test/abstract_controller/helper_test.rb @@ -4,7 +4,7 @@ module AbstractController module Testing class ControllerWithHelpers < AbstractController::Base - include Renderer + include AbstractController::RenderingController include Helpers def render(string) @@ -40,4 +40,4 @@ module AbstractController end end -end
\ No newline at end of file +end diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index b28df7743f..42f73faa61 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -6,7 +6,7 @@ module AbstractControllerTests # Base controller for these tests class Base < AbstractController::Base - include AbstractController::Renderer + include AbstractController::RenderingController include AbstractController::Layouts self.view_paths = [ActionView::FixtureResolver.new( diff --git a/actionpack/test/abstract_controller/test_helper.rb b/actionpack/test/abstract_controller/test_helper.rb index 915054f7fb..ba4302d914 100644 --- a/actionpack/test/abstract_controller/test_helper.rb +++ b/actionpack/test/abstract_controller/test_helper.rb @@ -6,7 +6,7 @@ require 'rubygems' require 'test/unit' require 'active_support' require 'active_support/test_case' -require 'action_controller/abstract' +require 'abstract_controller' require 'action_view' require 'action_view/base' require 'action_dispatch' diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 6e71b85645..b062a71442 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -13,7 +13,6 @@ require 'test/unit' require 'active_support' require 'active_support/test_case' -require 'action_controller/abstract' require 'action_controller' require 'fixture_template' require 'action_controller/testing/process' @@ -78,7 +77,7 @@ module ActionController def assert_template(options = {}, message = nil) validate_request! - hax = @controller._action_view.instance_variable_get(:@_rendered) + hax = @controller.view_context.instance_variable_get(:@_rendered) case options when NilClass, String diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb index 2036d1eeb5..37f1f6dff8 100644 --- a/actionpack/test/activerecord/polymorphic_routes_test.rb +++ b/actionpack/test/activerecord/polymorphic_routes_test.rb @@ -45,6 +45,12 @@ class PolymorphicRoutesTest < ActionController::TestCase assert_equal "http://example.com/projects/#{@project.id}", polymorphic_url(@project) end end + + def test_with_class + with_test_routes do + assert_equal "http://example.com/projects", polymorphic_url(@project.class) + end + end def test_with_new_record with_test_routes do @@ -52,6 +58,13 @@ class PolymorphicRoutesTest < ActionController::TestCase end end + def test_with_destroyed_record + with_test_routes do + @project.destroy + assert_equal "http://example.com/projects", polymorphic_url(@project) + end + end + def test_with_record_and_action with_test_routes do assert_equal "http://example.com/projects/new", polymorphic_url(@project, :action => 'new') @@ -129,6 +142,27 @@ class PolymorphicRoutesTest < ActionController::TestCase end end + def test_with_nested_destroyed + with_test_routes do + @project.save + @task.destroy + assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task]) + end + end + + def test_with_nested_class + with_test_routes do + @project.save + assert_equal "http://example.com/projects/#{@project.id}/tasks", polymorphic_url([@project, @task.class]) + end + end + + def test_class_with_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/projects", polymorphic_url([:admin, @project.class]) + end + end + def test_new_with_array_and_namespace with_admin_test_routes do assert_equal "http://example.com/admin/projects/new", polymorphic_url([:admin, @project], :action => 'new') @@ -252,11 +286,24 @@ class PolymorphicRoutesTest < ActionController::TestCase end end + def test_with_irregular_plural_class + with_test_routes do + assert_equal "http://example.com/taxes", polymorphic_url(@tax.class) + end + end + def test_with_irregular_plural_new_record with_test_routes do assert_equal "http://example.com/taxes", polymorphic_url(@tax) end end + + def test_with_irregular_plural_destroyed_record + with_test_routes do + @tax.destroy + assert_equal "http://example.com/taxes", polymorphic_url(@tax) + end + end def test_with_irregular_plural_record_and_action with_test_routes do @@ -298,6 +345,12 @@ class PolymorphicRoutesTest < ActionController::TestCase end end + def test_class_with_irregular_plural_array_and_namespace + with_admin_test_routes do + assert_equal "http://example.com/admin/taxes", polymorphic_url([:admin, @tax.class]) + end + end + def test_unsaved_with_irregular_plural_array_and_namespace with_admin_test_routes do assert_equal "http://example.com/admin/taxes", polymorphic_url([:admin, @tax]) @@ -395,4 +448,4 @@ class PolymorphicRoutesTest < ActionController::TestCase end end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index ad17d1288b..2e77d2f8ad 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -257,6 +257,13 @@ class AssertSelectTest < ActionController::TestCase end assert_raise(Assertion) {assert_select_rjs :insert, :top, "test2"} end + + def test_assert_select_rjs_for_redirect_to + render_rjs do |page| + page.redirect_to '/' + end + assert_select_rjs :redirect, '/' + end def test_elect_with_xml_namespace_attributes render_html %Q{<link xlink:href="http://nowhere.com"></link>} diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index c286976315..68529cc8f7 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -625,6 +625,21 @@ class FragmentCachingTest < ActionController::TestCase assert !fragment_computed assert_equal 'generated till now -> fragment content', buffer end + + def test_fragment_for_logging + fragment_computed = false + + @controller.class.expects(:benchmark).with('Cached fragment exists?: views/expensive') + @controller.class.expects(:benchmark).with('Cached fragment miss: views/expensive') + @controller.class.expects(:benchmark).with('Cached fragment hit: views/expensive').never + + buffer = 'generated till now -> ' + @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } + + assert fragment_computed + assert_equal 'generated till now -> ', buffer + end + end class FunctionalCachingController < ActionController::Base diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index fbc94a0df7..23688ca584 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -4,6 +4,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base before_filter :authenticate, :only => :index before_filter :authenticate_with_request, :only => :display + before_filter :authenticate_long_credentials, :only => :show def index render :text => "Hello Secret" @@ -12,6 +13,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase def display render :text => 'Definitely Maybe' end + + def show + render :text => 'Only for loooooong credentials' + end private @@ -28,6 +33,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase request_http_basic_authentication("SuperSecret") end end + + def authenticate_long_credentials + authenticate_or_request_with_http_basic do |username, password| + username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890' + end + end end AUTH_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION'] @@ -42,6 +53,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :success assert_equal 'Hello Secret', @response.body, "Authentication failed for request header #{header}" end + test "successful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890') + get :show + + assert_response :success + assert_equal 'Only for loooooong credentials', @response.body, "Authentication failed for request header #{header} and long credentials" + end end AUTH_HEADERS.each do |header| @@ -52,6 +70,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header}" end + test "unsuccessful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('h4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0r', 'worldworldworldworldworldworldworldworld') + get :show + + assert_response :unauthorized + assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header} and long credentials" + end end test "authentication request without credential" do diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 58f3b88075..7e9a2625f1 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -136,7 +136,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end - test "authentication request with request-uri that doesn't match credentials digest-uri" do + test "authentication request with request-uri that doesn't match credentials digest-uri" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') @request.env['REQUEST_URI'] = "/http_digest_authentication_test/dummy_digest/altered/uri" get :display @@ -145,10 +145,33 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal "Authentication Failed", @response.body end - test "authentication request with absolute uri" do - @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:uri => "http://test.host/http_digest_authentication_test/dummy_digest/display", + test "authentication request with absolute request uri (as in webrick)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in credentials (as in IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", :username => 'pretty', :password => 'please') - @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest/display" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in both request and credentials (as in Webrick with IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", + :username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + get :display assert_response :success @@ -202,11 +225,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase credentials = decode_credentials(@response.headers['WWW-Authenticate']) credentials.merge!(options) - credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}") + credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s) ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1]) end def decode_credentials(header) ActionController::HttpAuthentication::Digest.decode_credentials(@response.headers['WWW-Authenticate']) end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb index a7ed1b8665..98ffbc3813 100644 --- a/actionpack/test/controller/logging_test.rb +++ b/actionpack/test/controller/logging_test.rb @@ -17,9 +17,10 @@ class LoggingTest < ActionController::TestCase @level = Logger::DEBUG end - def method_missing(method, *args) + def method_missing(method, *args, &blk) @logged ||= [] @logged << args.first + @logged << blk.call if block_given? end end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 117f4ea4f0..8319b5c573 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'controller/fake_models' class RespondToController < ActionController::Base layout :set_layout @@ -471,22 +472,10 @@ class RespondToControllerTest < ActionController::TestCase end end -class RespondResource - undef_method :to_json - - def to_xml - "XML" - end - - def to_js - "JS" - end -end - class RespondWithController < ActionController::Base respond_to :html, :json respond_to :xml, :except => :using_defaults - respond_to :js, :only => :using_defaults + respond_to :js, :only => [ :using_defaults, :using_resource ] def using_defaults respond_to do |format| @@ -498,20 +487,27 @@ class RespondWithController < ActionController::Base respond_to(:js, :xml) end + def default_overwritten + respond_to do |format| + format.html { render :text => "HTML" } + end + end + def using_resource - respond_with(RespondResource.new) + respond_with(Customer.new("david", 13)) end - def using_resource_with_options - respond_with(RespondResource.new, :status => :unprocessable_entity) do |format| - format.js - end + def using_resource_with_parent + respond_with([Quiz::Store.new("developer?", 11), Customer.new("david", 13)]) end - def default_overwritten - respond_to do |format| - format.html { render :text => "HTML" } - end + def using_resource_with_status_and_location + respond_with(Customer.new("david", 13), :location => "http://test.host/", :status => :created) + end + + def using_resource_with_responder + responder = proc { |c, r, o| c.render :text => "Resource name is #{r.name}" } + respond_with(Customer.new("david", 13), :responder => responder) end protected @@ -527,7 +523,7 @@ class InheritedRespondWithController < RespondWithController respond_to :xml, :json def index - respond_with(RespondResource.new) do |format| + respond_with(Customer.new("david", 13)) do |format| format.json { render :text => "JSON" } end end @@ -540,6 +536,11 @@ class RespondWithControllerTest < ActionController::TestCase super ActionController::Base.use_accept_header = true @request.host = "www.example.com" + + ActionController::Routing::Routes.draw do |map| + map.resources :customers + map.resources :quiz_stores, :has_many => :customers + end end def teardown @@ -576,11 +577,17 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "<p>Hello world!</p>\n", @response.body end + def test_default_overwritten + get :default_overwritten + assert_equal "text/html", @response.content_type + assert_equal "HTML", @response.body + end + def test_using_resource - @request.accept = "text/html" + @request.accept = "text/javascript" get :using_resource - assert_equal "text/html", @response.content_type - assert_equal "Hello world!", @response.body + assert_equal "text/javascript", @response.content_type + assert_equal '$("body").visualEffect("highlight");', @response.body @request.accept = "application/xml" get :using_resource @@ -593,24 +600,114 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_with_options + def test_using_resource_for_post_with_html + post :using_resource + assert_equal "text/html", @response.content_type + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers/13", @response.location + assert @response.redirect? + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "text/html", @response.content_type + assert_equal 200, @response.status + assert_equal "New world!\n", @response.body + assert_nil @response.location + end + + def test_using_resource_for_post_with_xml @request.accept = "application/xml" - get :using_resource_with_options + + post :using_resource assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status + assert_equal 201, @response.status assert_equal "XML", @response.body + assert_equal "http://www.example.com/customers/13", @response.location - @request.accept = "text/javascript" - get :using_resource_with_options - assert_equal "text/javascript", @response.content_type + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "application/xml", @response.content_type assert_equal 422, @response.status - assert_equal "JS", @response.body + assert_equal errors.to_xml, @response.body + assert_nil @response.location end - def test_default_overwritten - get :default_overwritten + def test_using_resource_for_put_with_html + put :using_resource assert_equal "text/html", @response.content_type - assert_equal "HTML", @response.body + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers/13", @response.location + assert @response.redirect? + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + put :using_resource + assert_equal "text/html", @response.content_type + assert_equal 200, @response.status + assert_equal "Edit world!\n", @response.body + assert_nil @response.location + end + + def test_using_resource_for_put_with_xml + @request.accept = "application/xml" + + put :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 200, @response.status + assert_equal " ", @response.body + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + put :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 422, @response.status + assert_equal errors.to_xml, @response.body + assert_nil @response.location + end + + def test_using_resource_for_delete_with_html + Customer.any_instance.stubs(:destroyed?).returns(true) + delete :using_resource + assert_equal "text/html", @response.content_type + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers", @response.location + end + + def test_using_resource_for_delete_with_xml + Customer.any_instance.stubs(:destroyed?).returns(true) + @request.accept = "application/xml" + delete :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 200, @response.status + assert_equal " ", @response.body + end + + def test_using_resource_with_parent_for_get + @request.accept = "application/xml" + get :using_resource_with_parent + assert_equal "application/xml", @response.content_type + assert_equal 200, @response.status + assert_equal "XML", @response.body + end + + def test_using_resource_with_parent_for_post + @request.accept = "application/xml" + + post :using_resource_with_parent + assert_equal "application/xml", @response.content_type + assert_equal 201, @response.status + assert_equal "XML", @response.body + assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 422, @response.status + assert_equal errors.to_xml, @response.body + assert_nil @response.location end def test_clear_respond_to @@ -628,6 +725,29 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "XML", @response.body end + def test_no_double_render_is_raised + @request.accept = "text/html" + assert_raise ActionView::MissingTemplate do + get :using_resource + end + end + + def test_using_resource_with_status_and_location + @request.accept = "text/html" + post :using_resource_with_status_and_location + assert @response.redirect? + assert_equal "http://test.host/", @response.location + + @request.accept = "application/xml" + get :using_resource_with_status_and_location + assert_equal 201, @response.status + end + + def test_using_resource_with_responder + get :using_resource_with_responder + assert_equal "Resource name is david", @response.body + end + def test_not_acceptable @request.accept = "application/xml" get :using_defaults @@ -642,14 +762,12 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal 406, @response.status @request.accept = "text/javascript" - get :using_resource + get :default_overwritten assert_equal 406, @response.status end end class AbstractPostController < ActionController::Base - respond_to :html, :iphone - self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/" end @@ -658,7 +776,7 @@ class PostController < AbstractPostController around_filter :with_iphone def index - respond_to # It will use formats declared above + respond_to(:html, :iphone) end protected diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 44e49ed3f8..6b6d154faa 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -54,24 +54,6 @@ class RecordIdentifierTest < Test::Unit::TestCase assert_equal "edit_#{@singular}_1", dom_id(@record, :edit) end - def test_partial_path - expected = "#{@plural}/#{@singular}" - assert_equal expected, partial_path(@record) - assert_equal expected, partial_path(Comment) - end - - def test_partial_path_with_namespaced_controller_path - expected = "admin/#{@plural}/#{@singular}" - assert_equal expected, partial_path(@record, "admin/posts") - assert_equal expected, partial_path(@klass, "admin/posts") - end - - def test_partial_path_with_not_namespaced_controller_path - expected = "#{@plural}/#{@singular}" - assert_equal expected, partial_path(@record, "posts") - assert_equal expected, partial_path(@klass, "posts") - end - def test_dom_class assert_equal @singular, dom_class(@record) end @@ -101,42 +83,3 @@ class RecordIdentifierTest < Test::Unit::TestCase RecordIdentifier.send(method, *args) end end - -class NestedRecordIdentifierTest < RecordIdentifierTest - def setup - @klass = Comment::Nested - @record = @klass.new - @singular = 'comment_nested' - @plural = 'comment_nesteds' - end - - def test_partial_path - expected = "comment/nesteds/nested" - assert_equal expected, partial_path(@record) - assert_equal expected, partial_path(Comment::Nested) - end - - def test_partial_path_with_namespaced_controller_path - expected = "admin/comment/nesteds/nested" - assert_equal expected, partial_path(@record, "admin/posts") - assert_equal expected, partial_path(@klass, "admin/posts") - end - - def test_partial_path_with_deeper_namespaced_controller_path - expected = "deeper/admin/comment/nesteds/nested" - assert_equal expected, partial_path(@record, "deeper/admin/posts") - assert_equal expected, partial_path(@klass, "deeper/admin/posts") - end - - def test_partial_path_with_even_deeper_namespaced_controller_path - expected = "even/more/deeper/admin/comment/nesteds/nested" - assert_equal expected, partial_path(@record, "even/more/deeper/admin/posts") - assert_equal expected, partial_path(@klass, "even/more/deeper/admin/posts") - end - - def test_partial_path_with_not_namespaced_controller_path - expected = "comment/nesteds/nested" - assert_equal expected, partial_path(@record, "posts") - assert_equal expected, partial_path(@klass, "posts") - end -end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index b3321303c0..7755af592d 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -34,6 +34,10 @@ class RedirectController < ActionController::Base redirect_to({:action => "hello_world"}, {:status => 301}) end + def redirect_with_protocol + redirect_to :action => "hello_world", :protocol => "https" + end + def url_redirect_with_status redirect_to("http://www.example.com", :status => :moved_permanently) end @@ -132,6 +136,12 @@ class RedirectTest < ActionController::TestCase assert_equal "http://test.host/redirect/hello_world", redirect_to_url end + def test_redirect_with_protocol + get :redirect_with_protocol + assert_response 302 + assert_equal "https://test.host/redirect/hello_world", redirect_to_url + end + def test_url_redirect_with_status get :url_redirect_with_status assert_response 301 diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index acb0c895e0..0c0599679c 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -17,8 +17,9 @@ class MockLogger @logged = [] end - def method_missing(method, *args) + def method_missing(method, *args, &blk) @logged << args.first + @logged << blk.call if block_given? end end @@ -457,6 +458,10 @@ class TestController < ActionController::Base head :location => "/foo" end + def head_with_location_object + head :location => Customer.new("david", 1) + end + def head_with_symbolic_status head :status => params[:status].intern end @@ -617,6 +622,7 @@ class TestController < ActionController::Base end private + def determine_layout case action_name when "hello_world", "layout_test", "rendering_without_layout", @@ -1083,6 +1089,18 @@ class RenderTest < ActionController::TestCase assert_response :ok end + def test_head_with_location_object + ActionController::Routing::Routes.draw do |map| + map.resources :customers + map.connect ':controller/:action/:id' + end + + get :head_with_location_object + assert @response.body.blank? + assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"] + assert_response :ok + end + def test_head_with_custom_header get :head_with_custom_header assert @response.body.blank? @@ -1219,7 +1237,6 @@ class RenderTest < ActionController::TestCase def test_partial_collection_with_spacer get :partial_collection_with_spacer assert_equal "Hello: davidonly partialHello: mary", @response.body - assert_template :partial => 'test/_partial_only' assert_template :partial => '_customer' end @@ -1331,7 +1348,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_200_should_set_etag get :render_hello_world_from_variable assert_equal etag_for("hello david"), @response.headers['ETag'] - assert_equal "private, max-age=0, must-revalidate", @response.headers['Cache-Control'] + assert_equal "max-age=0, private, must-revalidate", @response.headers['Cache-Control'] end def test_render_against_etag_request_should_304_when_match diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index 052b4f0b52..139f55d8bd 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -11,7 +11,7 @@ class TestController < ActionController::Base def render_with_object_location customer = Customer.new("Some guy", 1) - render :xml => "<customer/>", :location => customer_url(customer), :status => :created + render :xml => "<customer/>", :location => customer, :status => :created end def render_with_to_xml @@ -78,4 +78,4 @@ class RenderTest < ActionController::TestCase get :implicit_content_type, :format => 'atom' assert_equal Mime::ATOM, @response.content_type end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index fb83dba395..5f9ae6292c 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 require 'abstract_unit' require 'controller/fake_controllers' require 'active_support/dependencies' @@ -1179,7 +1180,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo")) token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian - token.force_encoding("UTF-8") if token.respond_to?(:force_encoding) escaped_token = CGI::escape(token) assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index ae32ee5649..0afebac68c 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -1,9 +1,10 @@ +# encoding: utf-8 require 'abstract_unit' module TestFileUtils def file_name() File.basename(__FILE__) end def file_path() File.expand_path(__FILE__) end - def file_data() File.open(file_path, 'rb') { |f| f.read } end + def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end end class SendFileController < ActionController::Base @@ -22,6 +23,10 @@ class SendFileController < ActionController::Base def data send_data(file_data, options) end + + def multibyte_text_data + send_data("Кирилица\n祝您好運", options) + end end class SendFileTest < ActionController::TestCase @@ -55,6 +60,7 @@ class SendFileTest < ActionController::TestCase require 'stringio' output = StringIO.new output.binmode + output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding) assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } } assert_equal file_data, output.string end @@ -123,7 +129,7 @@ class SendFileTest < ActionController::TestCase # test overriding Cache-Control: no-cache header to fix IE open/save dialog @controller.headers = { 'Cache-Control' => 'no-cache' } @controller.send(:send_file_headers!, options) - h = @controller.headers + @controller.response.prepare! assert_equal 'private', h['Cache-Control'] end @@ -163,4 +169,11 @@ class SendFileTest < ActionController::TestCase assert_equal 200, @response.status end end + + def test_send_data_content_length_header + @controller.headers = {} + @controller.options = { :type => :text, :filename => 'file_with_utf8_text' } + process('multibyte_text_data') + assert_equal '29', @controller.headers['Content-Length'] + end end diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 9e008a9ae8..301080842e 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -120,8 +120,6 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest fixture = FIXTURE_PATH + "/mona_lisa.jpg" params = { :uploaded_data => fixture_file_upload(fixture, "image/jpg") } post '/read', params - expected_length = 'File: '.length + File.size(fixture) - assert_equal expected_length, response.content_length end end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 8ebf9aa186..f3500fca34 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -72,6 +72,34 @@ class RequestTest < ActiveSupport::TestCase assert_equal '9.9.9.9', request.remote_ip end + test "remote ip with user specified trusted proxies" do + ActionController::Base.trusted_proxies = /^67\.205\.106\.73$/i + + request = stub_request 'REMOTE_ADDR' => '67.205.106.73', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '172.16.0.1,67.205.106.73', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '67.205.106.73,172.16.0.1', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '67.205.106.74,172.16.0.1', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '67.205.106.74', request.remote_ip + + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73' + assert_equal 'unknown', request.remote_ip + + request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73' + assert_equal '3.4.5.6', request.remote_ip + + ActionController::Base.trusted_proxies = nil + end + test "domains" do request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org' assert_equal "rubyonrails.org", request.domain diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 2ddc6cb2b5..256ed06a45 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -13,10 +13,9 @@ class ResponseTest < ActiveSupport::TestCase assert_equal 200, status assert_equal({ "Content-Type" => "text/html; charset=utf-8", - "Cache-Control" => "private, max-age=0, must-revalidate", + "Cache-Control" => "max-age=0, private, must-revalidate", "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"', - "Set-Cookie" => "", - "Content-Length" => "13" + "Set-Cookie" => "" }, headers) parts = [] @@ -32,10 +31,9 @@ class ResponseTest < ActiveSupport::TestCase assert_equal 200, status assert_equal({ "Content-Type" => "text/html; charset=utf-8", - "Cache-Control" => "private, max-age=0, must-revalidate", + "Cache-Control" => "max-age=0, private, must-revalidate", "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"', - "Set-Cookie" => "", - "Content-Length" => "8" + "Set-Cookie" => "" }, headers) end diff --git a/actionpack/test/fixtures/respond_with/edit.html.erb b/actionpack/test/fixtures/respond_with/edit.html.erb new file mode 100644 index 0000000000..ae82dfa4fc --- /dev/null +++ b/actionpack/test/fixtures/respond_with/edit.html.erb @@ -0,0 +1 @@ +Edit world! diff --git a/actionpack/test/fixtures/respond_with/new.html.erb b/actionpack/test/fixtures/respond_with/new.html.erb new file mode 100644 index 0000000000..96c8f1b88b --- /dev/null +++ b/actionpack/test/fixtures/respond_with/new.html.erb @@ -0,0 +1 @@ +New world! diff --git a/actionpack/test/fixtures/respond_with/using_resource.html.erb b/actionpack/test/fixtures/respond_with/using_resource.html.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionpack/test/fixtures/respond_with/using_resource.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world!
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_resource.js.rjs b/actionpack/test/fixtures/respond_with/using_resource.js.rjs new file mode 100644 index 0000000000..737c175a4e --- /dev/null +++ b/actionpack/test/fixtures/respond_with/using_resource.js.rjs @@ -0,0 +1 @@ +page[:body].visual_effect :highlight diff --git a/actionpack/test/fixtures/test/greeting.xml.erb b/actionpack/test/fixtures/test/greeting.xml.erb new file mode 100644 index 0000000000..62fb0293f0 --- /dev/null +++ b/actionpack/test/fixtures/test/greeting.xml.erb @@ -0,0 +1 @@ +<p>This is grand!</p> diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index c6726432ec..0faf8f3f9a 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -4,9 +4,27 @@ class Customer < Struct.new(:name, :id) extend ActiveModel::Naming include ActiveModel::Conversion + undef_method :to_json + def to_param id.to_s end + + def to_xml + "XML" + end + + def to_js + "JS" + end + + def errors + [] + end + + def destroyed? + false + end end class BadCustomer < Customer @@ -24,4 +42,8 @@ module Quiz id.to_s end end + + class Store < Question + end end + diff --git a/actionpack/test/new_base/base_test.rb b/actionpack/test/new_base/base_test.rb index d9d552f9e5..1b2e917ced 100644 --- a/actionpack/test/new_base/base_test.rb +++ b/actionpack/test/new_base/base_test.rb @@ -34,7 +34,6 @@ module Dispatching assert_body "success" assert_status 200 assert_content_type "text/html; charset=utf-8" - assert_header "Content-Length", "7" end # :api: plugin @@ -42,7 +41,6 @@ module Dispatching get "/dispatching/simple/modify_response_body" assert_body "success" - assert_header "Content-Length", "7" # setting the body manually sets the content length end # :api: plugin @@ -50,7 +48,6 @@ module Dispatching get "/dispatching/simple/modify_response_body_twice" assert_body "success!" - assert_header "Content-Length", "8" end test "controller path" do diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 921bfeb93a..28f9d48671 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -83,7 +83,10 @@ class AssetTagHelperTest < ActionView::TestCase %(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>), %(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>), %(javascript_include_tag(:defaults, "bank")) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), - %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>) + %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), + + %(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all" type="text/javascript"></script>), + %(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js" type="text/javascript"></script>), } StylePathToTag = { @@ -111,7 +114,8 @@ class AssetTagHelperTest < ActionView::TestCase %(stylesheet_link_tag(:all, :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="all" rel="stylesheet" type="text/css" />), %(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />\n<link href="/elsewhere/file.css" media="screen" rel="stylesheet" type="text/css" />), - %(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" type="text/css" />) + %(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style" media="screen" rel="stylesheet" type="text/css" />), + %(stylesheet_link_tag("http://www.example.com/styles/style.css")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" type="text/css" />), } ImagePathToTag = { @@ -158,8 +162,8 @@ class AssetTagHelperTest < ActionView::TestCase VideoLinkToTag = { %(video_tag("xml.ogg")) => %(<video src="/videos/xml.ogg" />), - %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="true" controls="true" src="/videos/rss.m4v" />), - %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="true" src="/videos/rss.m4v" />), + %(video_tag("rss.m4v", :autoplay => true, :controls => true)) => %(<video autoplay="autoplay" controls="controls" src="/videos/rss.m4v" />), + %(video_tag("rss.m4v", :autobuffer => true)) => %(<video autobuffer="autobuffer" src="/videos/rss.m4v" />), %(video_tag("gold.m4v", :size => "160x120")) => %(<video height="120" src="/videos/gold.m4v" width="160" />), %(video_tag("gold.m4v", "size" => "320x240")) => %(<video height="240" src="/videos/gold.m4v" width="320" />), %(video_tag("trailer.ogg", :poster => "screenshot.png")) => %(<video poster="/images/screenshot.png" src="/videos/trailer.ogg" />), @@ -168,7 +172,7 @@ class AssetTagHelperTest < ActionView::TestCase %(video_tag("error.avi", "size" => "x")) => %(<video src="/videos/error.avi" />), %(video_tag("http://media.rubyonrails.org/video/rails_blog_2.mov")) => %(<video src="http://media.rubyonrails.org/video/rails_blog_2.mov" />), %(video_tag(["multiple.ogg", "multiple.avi"])) => %(<video><source src="multiple.ogg" /><source src="multiple.avi" /></video>), - %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="true" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>) + %(video_tag(["multiple.ogg", "multiple.avi"], :size => "160x120", :controls => true)) => %(<video controls="controls" height="120" width="160"><source src="multiple.ogg" /><source src="multiple.avi" /></video>) } AudioPathToTag = { @@ -187,7 +191,7 @@ class AssetTagHelperTest < ActionView::TestCase AudioLinkToTag = { %(audio_tag("xml.wav")) => %(<audio src="/audios/xml.wav" />), - %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="true" controls="true" src="/audios/rss.wav" />), + %(audio_tag("rss.wav", :autoplay => true, :controls => true)) => %(<audio autoplay="autoplay" controls="controls" src="/audios/rss.wav" />), %(audio_tag("http://media.rubyonrails.org/audio/rails_blog_2.mov")) => %(<audio src="http://media.rubyonrails.org/audio/rails_blog_2.mov" />), } diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 3acaecd142..6a5fb0acff 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -157,6 +157,26 @@ class ScrollsController < ActionController::Base end end EOT + FEEDS["provide_builder"] = <<-'EOT' + # we pass in the new_xml to the helper so it doesn't + # call anything on the original builder + new_xml = Builder::XmlMarkup.new(:target=>'') + atom_feed(:xml => new_xml) do |feed| + feed.title("My great blog!") + feed.updated((@scrolls.first.created_at)) + + for scroll in @scrolls + feed.entry(scroll) do |entry| + entry.title(scroll.title) + entry.content(scroll.body, :type => 'html') + + entry.author do |author| + author.name("DHH") + end + end + end + end + EOT def index @scrolls = [ Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)), @@ -202,6 +222,15 @@ class AtomFeedTest < ActionController::TestCase end end + def test_providing_builder_to_atom_feed + with_restful_routing(:scrolls) do + get :index, :id=>"provide_builder" + # because we pass in the non-default builder, the content generated by the + # helper should go 'nowhere'. Leaving the response body blank. + assert @response.body.blank? + end + end + def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_record with_restful_routing(:scrolls) do get :index, :id => "entry_options" diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb index bac67c1a7d..defe85107e 100644 --- a/actionpack/test/template/body_parts_test.rb +++ b/actionpack/test/template/body_parts_test.rb @@ -4,7 +4,7 @@ class BodyPartsTest < ActionController::TestCase RENDERINGS = [Object.new, Object.new, Object.new] class TestController < ActionController::Base - def performed?() true end + def response_body() "" end def index RENDERINGS.each do |rendering| diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 99160dd8b1..2b1d80b1bf 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -307,6 +307,16 @@ class FormHelperTest < ActionView::TestCase ) end + def test_radio_button_with_booleans + assert_dom_equal('<input id="post_secret_true" name="post[secret]" type="radio" value="true" />', + radio_button("post", "secret", true) + ) + + assert_dom_equal('<input id="post_secret_false" name="post[secret]" type="radio" value="false" />', + radio_button("post", "secret", false) + ) + end + def test_text_area assert_dom_equal( '<textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea>', diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 79004264fd..d64b9492e2 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -136,6 +136,18 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, input_elem['id'] end + def test_select_tag_with_include_blank + actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :include_blank => true + expected = %(<select id="places" name="places"><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>) + assert_dom_equal expected, actual + end + + def test_select_tag_with_include_blank_with_string + actual = select_tag "places", "<option>Home</option><option>Work</option><option>Pub</option>", :include_blank => "string" + expected = %(<select id="places" name="places"><option value="">string</option><option>Home</option><option>Work</option><option>Pub</option></select>) + assert_dom_equal expected, actual + end + def test_text_area_tag_size_string actual = text_area_tag "body", "hello world", "size" => "20x40" expected = %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>) diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index 57b740032e..85a97d570c 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -88,6 +88,7 @@ class NumberHelperTest < ActionView::TestCase assert_equal("111.00", number_with_precision(111, :precision => 2)) assert_equal("111.235", number_with_precision("111.2346")) assert_equal("31.83", number_with_precision("31.825", :precision => 2)) + assert_equal("3268", number_with_precision((32.675 * 100.00), :precision => 0)) assert_equal("112", number_with_precision(111.50, :precision => 0)) assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0)) diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index ef88cae5b8..2aa3d5b5fa 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -71,6 +71,19 @@ class TagHelperTest < ActionView::TestCase assert_equal '<p><b>Hello</b></p>', output_buffer end + def test_content_tag_with_escaped_array_class + str = content_tag('p', "limelight", :class => ["song", "play>"]) + assert_equal "<p class=\"song play>\">limelight</p>", str + + str = content_tag('p', "limelight", :class => ["song", "play"]) + assert_equal "<p class=\"song play\">limelight</p>", str + end + + def test_content_tag_with_unescaped_array_class + str = content_tag('p', "limelight", {:class => ["song", "play>"]}, false) + assert_equal "<p class=\"song play>\">limelight</p>", str + end + def test_cdata_section assert_equal "<![CDATA[<hello world>]]>", cdata_section("<hello world>") end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 706b5085f4..b7823b9394 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'testing_sandbox' +require 'redcloth' class TextHelperTest < ActionView::TestCase tests ActionView::Helpers::TextHelper @@ -528,4 +529,20 @@ class TextHelperTest < ActionView::TestCase assert_equal("red", cycle("red", "blue")) assert_equal(%w{Specialized Fuji Giant}, @cycles) end + + def test_textilize + assert_equal("<p><strong>This is Textile!</strong> Rejoice!</p>", textilize("*This is Textile!* Rejoice!")) + end + + def test_textilize_with_blank + assert_equal("", textilize("")) + end + + def test_textilize_with_options + assert_equal("<p>This is worded <strong>strongly</strong></p>", textilize("This is worded <strong>strongly</strong>", :filter_html)) + end + + def test_textilize_with_hard_breaks + assert_equal("<p>This is one scary world.<br />\n True.</p>", textilize("This is one scary world.\n True.")) + end end |