diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/base.rb | 65 | ||||
-rw-r--r-- | actionmailer/test/mail_service_test.rb | 26 | ||||
-rw-r--r-- | actionpack/Rakefile | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller.rb | 98 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/base.rb (renamed from actionpack/lib/action_controller/base.rb) | 527 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/chained/benchmarking.rb (renamed from actionpack/lib/action_controller/benchmarking.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/chained/filters.rb (renamed from actionpack/lib/action_controller/filters.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/chained/flash.rb (renamed from actionpack/lib/action_controller/flash.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/cookies.rb (renamed from actionpack/lib/action_controller/cookies.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/helpers.rb (renamed from actionpack/lib/action_controller/helpers.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/http_authentication.rb (renamed from actionpack/lib/action_controller/http_authentication.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/layout.rb (renamed from actionpack/lib/action_controller/layout.rb) | 144 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/redirect.rb | 91 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/render.rb | 378 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/request_forgery_protection.rb (renamed from actionpack/lib/action_controller/request_forgery_protection.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/responder.rb | 41 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/streaming.rb (renamed from actionpack/lib/action_controller/streaming.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base/verification.rb (renamed from actionpack/lib/action_controller/verification.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi/ext.rb (renamed from actionpack/lib/action_controller/cgi_ext.rb) | 6 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi/ext/cookie.rb (renamed from actionpack/lib/action_controller/cgi_ext/cookie.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi/ext/query_extension.rb (renamed from actionpack/lib/action_controller/cgi_ext/query_extension.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi/ext/stdinput.rb (renamed from actionpack/lib/action_controller/cgi_ext/stdinput.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi/process.rb (renamed from actionpack/lib/action_controller/cgi_process.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/dispatcher.rb (renamed from actionpack/lib/action_controller/dispatcher.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/middlewares.rb (renamed from actionpack/lib/action_controller/middlewares.rb) | 10 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/rescue.rb (renamed from actionpack/lib/action_controller/rescue.rb) | 14 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb (renamed from actionpack/lib/action_controller/templates/rescues/_request_and_response.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb (renamed from actionpack/lib/action_controller/templates/rescues/_trace.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb (renamed from actionpack/lib/action_controller/templates/rescues/diagnostics.erb) | 5 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb (renamed from actionpack/lib/action_controller/templates/rescues/layout.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb (renamed from actionpack/lib/action_controller/templates/rescues/missing_template.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb (renamed from actionpack/lib/action_controller/templates/rescues/routing_error.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb (renamed from actionpack/lib/action_controller/templates/rescues/template_error.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb (renamed from actionpack/lib/action_controller/templates/rescues/unknown_action.erb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/mime/responds.rb (renamed from actionpack/lib/action_controller/mime_responds.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/rack_ext.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_controller/record_identifier.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb (renamed from actionpack/lib/action_controller/polymorphic_routes.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/generation/url_rewriter.rb (renamed from actionpack/lib/action_controller/url_rewriter.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/resources.rb (renamed from actionpack/lib/action_controller/resources.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/route_set.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session/management.rb (renamed from actionpack/lib/action_controller/session_management.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/dom.rb (renamed from actionpack/lib/action_controller/assertions/dom_assertions.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/model.rb (renamed from actionpack/lib/action_controller/assertions/model_assertions.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/response.rb (renamed from actionpack/lib/action_controller/assertions/response_assertions.rb) | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/routing.rb (renamed from actionpack/lib/action_controller/assertions/routing_assertions.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/selector.rb (renamed from actionpack/lib/action_controller/assertions/selector_assertions.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/assertions/tag.rb (renamed from actionpack/lib/action_controller/assertions/tag_assertions.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/integration.rb (renamed from actionpack/lib/action_controller/integration.rb) | 6 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/performance.rb (renamed from actionpack/lib/action_controller/performance_test.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/process.rb (renamed from actionpack/lib/action_controller/test_process.rb) | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/testing/test_case.rb (renamed from actionpack/lib/action_controller/test_case.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch.rb | 64 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/headers.rb (renamed from actionpack/lib/action_controller/headers.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/mime_type.rb (renamed from actionpack/lib/action_controller/mime_type.rb) | 8 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/mime_types.rb (renamed from actionpack/lib/action_controller/mime_types.rb) | 0 | ||||
-rwxr-xr-x | actionpack/lib/action_dispatch/http/request.rb (renamed from actionpack/lib/action_controller/request.rb) | 27 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/response.rb (renamed from actionpack/lib/action_controller/response.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/status_codes.rb (renamed from actionpack/lib/action_controller/status_codes.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/failsafe.rb (renamed from actionpack/lib/action_controller/failsafe.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/params_parser.rb (renamed from actionpack/lib/action_controller/params_parser.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/rewindable_input.rb (renamed from actionpack/lib/action_controller/rewindable_input.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/session/abstract_store.rb (renamed from actionpack/lib/action_controller/session/abstract_store.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/session/cookie_store.rb (renamed from actionpack/lib/action_controller/session/cookie_store.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb (renamed from actionpack/lib/action_controller/session/mem_cache_store.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/rack.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/rack/lock.rb (renamed from actionpack/lib/action_controller/rack_ext/lock.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/rack/multipart.rb (renamed from actionpack/lib/action_controller/rack_ext/multipart.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/rack/parse_query.rb (renamed from actionpack/lib/action_controller/rack_ext/parse_query.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/utils/middleware_stack.rb (renamed from actionpack/lib/action_controller/middleware_stack.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/utils/uploaded_file.rb (renamed from actionpack/lib/action_controller/uploaded_file.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/utils/url_encoded_pair_parser.rb (renamed from actionpack/lib/action_controller/url_encoded_pair_parser.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_view/base.rb | 89 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/prototype_helper.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_view/paths.rb | 20 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/partials.rb (renamed from actionpack/lib/action_view/partials.rb) | 179 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/rendering.rb | 119 | ||||
-rw-r--r-- | actionpack/lib/action_view/renderable_partial.rb | 47 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/error.rb (renamed from actionpack/lib/action_view/template_error.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handler.rb (renamed from actionpack/lib/action_view/template_handler.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handlers.rb (renamed from actionpack/lib/action_view/template_handlers.rb) | 6 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handlers/builder.rb (renamed from actionpack/lib/action_view/template_handlers/builder.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handlers/erb.rb (renamed from actionpack/lib/action_view/template_handlers/erb.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handlers/rjs.rb (renamed from actionpack/lib/action_view/template_handlers/rjs.rb) | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/inline.rb (renamed from actionpack/lib/action_view/inline_template.rb) | 0 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/partial.rb | 18 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/renderable.rb (renamed from actionpack/lib/action_view/renderable.rb) | 36 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/template.rb (renamed from actionpack/lib/action_view/template.rb) | 34 | ||||
-rw-r--r-- | actionpack/lib/action_view/test_case.rb | 19 | ||||
-rw-r--r-- | actionpack/test/abstract_unit.rb | 3 | ||||
-rw-r--r-- | actionpack/test/controller/dispatcher_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/header_test.rb | 14 | ||||
-rw-r--r-- | actionpack/test/controller/layout_test.rb | 38 | ||||
-rw-r--r-- | actionpack/test/controller/mime_responds_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/render_test.rb | 10 | ||||
-rw-r--r-- | actionpack/test/controller/rescue_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/send_file_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/session/cookie_store_test.rb | 19 | ||||
-rw-r--r-- | actionpack/test/controller/session/mem_cache_store_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/test_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/view_paths_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/header_test.rb | 16 | ||||
-rw-r--r-- | actionpack/test/dispatch/middleware_stack_test.rb (renamed from actionpack/test/controller/middleware_stack_test.rb) | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/mime_type_test.rb (renamed from actionpack/test/controller/mime_type_test.rb) | 51 | ||||
-rw-r--r-- | actionpack/test/dispatch/rack_test.rb (renamed from actionpack/test/controller/rack_test.rb) | 71 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/json_params_parsing_test.rb (renamed from actionpack/test/controller/request/json_params_parsing_test.rb) | 0 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/multipart_params_parsing_test.rb (renamed from actionpack/test/controller/request/multipart_params_parsing_test.rb) | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/query_string_parsing_test.rb (renamed from actionpack/test/controller/request/query_string_parsing_test.rb) | 0 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb (renamed from actionpack/test/controller/request/url_encoded_params_parsing_test.rb) | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/xml_params_parsing_test.rb (renamed from actionpack/test/controller/request/xml_params_parsing_test.rb) | 0 | ||||
-rw-r--r-- | actionpack/test/dispatch/request_test.rb (renamed from actionpack/test/controller/request_test.rb) | 76 | ||||
-rw-r--r-- | actionpack/test/fixtures/layout_tests/views/goodbye.rhtml | 1 | ||||
-rw-r--r-- | actionpack/test/template/javascript_helper_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/template/prototype_helper_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/template/render_test.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/session_store.rb | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support.rb | 1 | ||||
-rw-r--r-- | activesupport/lib/active_support/concurrent_hash.rb | 26 | ||||
-rw-r--r-- | activesupport/lib/active_support/memoizable.rb | 38 | ||||
-rw-r--r-- | ci/cruise_config.rb | 4 | ||||
-rw-r--r-- | railties/lib/dispatcher.rb | 2 |
123 files changed, 1437 insertions, 1122 deletions
diff --git a/.gitignore b/.gitignore index 28ee9cf2a8..bb1ca166e6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ railties/pkg railties/test/500.html railties/doc/guides/html/images railties/doc/guides/html/stylesheets +benches *.rbc *.swp *.swo diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index eda5de4e18..473703b629 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -371,6 +371,14 @@ module ActionMailer #:nodoc: attr_reader :mail attr_reader :template_name, :default_template_name, :action_name + def controller_path + self.class.controller_path + end + + def formats + @template.formats + end + class << self attr_writer :mailer_name @@ -464,7 +472,7 @@ module ActionMailer #:nodoc: # have not already been specified manually. if @parts.empty? Dir.glob("#{template_path}/#{@template}.*").each do |path| - template = template_root["#{mailer_name}/#{File.basename(path)}"] + template = template_root.find_template("#{mailer_name}/#{File.basename(path)}") # Skip unless template has a multipart format next unless template && template.multipart? @@ -473,7 +481,7 @@ module ActionMailer #:nodoc: :content_type => template.content_type, :disposition => "inline", :charset => charset, - :body => render_message(template, @body) + :body => render_template(template, @body) ) end unless @parts.empty? @@ -487,7 +495,7 @@ module ActionMailer #:nodoc: # normal template exists (or if there were no implicit parts) we render # it. template_exists = @parts.empty? - template_exists ||= template_root["#{mailer_name}/#{@template}"] + template_exists ||= template_root.find_template("#{mailer_name}/#{@template}") @body = render_message(@template, @body) if template_exists # Finally, if there are other message parts and a textual body exists, @@ -512,6 +520,7 @@ module ActionMailer #:nodoc: # no alternate has been given as the parameter, this will fail. def deliver!(mail = @mail) raise "no mail object available for delivery!" unless mail + unless logger.nil? logger.info "Sent mail to #{Array(recipients).join(', ')}" logger.debug "\n#{mail.encoded}" @@ -543,27 +552,43 @@ module ActionMailer #:nodoc: @mime_version = @@default_mime_version.dup if @@default_mime_version end - def render_message(method_name, body) - if method_name.respond_to?(:content_type) - @current_template_content_type = method_name.content_type + def render_template(template, body) + if template.respond_to?(:content_type) + @current_template_content_type = template.content_type end + + @template = initialize_template_class(body) + layout = _pick_layout(layout, true) unless template.exempt_from_layout? + @template._render_template_with_layout(template, layout, {}) + ensure + @current_template_content_type = nil + end + + def render_message(method_name, body) render :file => method_name, :body => body ensure @current_template_content_type = nil end def render(opts) - body = opts.delete(:body) - if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render)) - opts[:file] = "#{mailer_name}/#{opts[:file]}" - end - + layout, file = opts.delete(:layout), opts[:file] + begin - old_template, @template = @template, initialize_template_class(body) - layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false - @template.render(opts.merge(:layout => layout)) - ensure - @template = old_template + @template = initialize_template_class(opts.delete(:body)) + + if file + prefix = mailer_name unless file =~ /\// + template = view_paths.find_by_parts(file, formats, prefix) + end + + layout = _pick_layout(layout, + !template || !template.exempt_from_layout?) + + if template + @template._render_template_with_layout(template, layout, opts) + elsif inline = opts[:inline] + @template._render_inline(inline, layout, opts) + end end end @@ -575,12 +600,6 @@ module ActionMailer #:nodoc: end end - def candidate_for_layout?(options) - !self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout? - rescue ActionView::MissingTemplate - return true - end - def template_root self.class.template_root end @@ -595,7 +614,7 @@ module ActionMailer #:nodoc: def initialize_template_class(assigns) template = ActionView::Base.new(view_paths, assigns, self) - template.template_format = default_template_format + template.formats = [default_template_format] template end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index a886b1143e..d14f326163 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -566,13 +566,31 @@ class ActionMailerTest < Test::Unit::TestCase TestMailer.deliver_signed_up(@recipient) end + class FakeLogger + attr_reader :info_contents, :debug_contents + + def initialize + @info_contents, @debug_contents = "", "" + end + + def info(str) + @info_contents << str + end + + def debug(str) + @debug_contents << str + end + end + def test_delivery_logs_sent_mail mail = TestMailer.create_signed_up(@recipient) - logger = mock() - logger.expects(:info).with("Sent mail to #{@recipient}") - logger.expects(:debug).with("\n#{mail.encoded}") - TestMailer.logger = logger + # logger = mock() + # logger.expects(:info).with("Sent mail to #{@recipient}") + # logger.expects(:debug).with("\n#{mail.encoded}") + TestMailer.logger = FakeLogger.new TestMailer.deliver_signed_up(@recipient) + assert(TestMailer.logger.info_contents =~ /Sent mail to #{@recipient}/) + assert_equal(TestMailer.logger.debug_contents, "\n#{mail.encoded}") end def test_unquote_quoted_printable_subject diff --git a/actionpack/Rakefile b/actionpack/Rakefile index c389e5a8d6..230a78c069 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -30,7 +30,7 @@ Rake::TestTask.new(:test_action_pack) do |t| # make sure we include the tests in alphabetical order as on some systems # this will not happen automatically and the tests (as a whole) will error - t.test_files = Dir.glob( "test/[cft]*/**/*_test.rb" ).sort + t.test_files = Dir.glob( "test/[cdft]*/**/*_test.rb" ).sort t.verbose = true #t.warning = true diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 3e77970367..eb596ba40e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,83 +31,59 @@ rescue LoadError end end -gem 'rack', '>= 0.9.0' -require 'rack' -require 'action_controller/rack_ext' +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, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter] + [Base, Request, Response, UrlRewriter, UrlWriter] + [ActionDispatch::Http::Headers] end - autoload :AbstractRequest, 'action_controller/request' - autoload :Base, 'action_controller/base' - autoload :Benchmarking, 'action_controller/benchmarking' + autoload :Base, 'action_controller/base/base' + autoload :Benchmarking, 'action_controller/base/chained/benchmarking' autoload :Caching, 'action_controller/caching' - autoload :Cookies, 'action_controller/cookies' - autoload :Dispatcher, 'action_controller/dispatcher' - autoload :Failsafe, 'action_controller/failsafe' - autoload :Filters, 'action_controller/filters' - autoload :Flash, 'action_controller/flash' - autoload :Helpers, 'action_controller/helpers' - autoload :HttpAuthentication, 'action_controller/http_authentication' - autoload :Integration, 'action_controller/integration' - autoload :IntegrationTest, 'action_controller/integration' - autoload :Layout, 'action_controller/layout' - autoload :MiddlewareStack, 'action_controller/middleware_stack' - autoload :MimeResponds, 'action_controller/mime_responds' - autoload :ParamsParser, 'action_controller/params_parser' - autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' + 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/mime/responds' + autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Request, 'action_controller/request' - autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection' - autoload :Rescue, 'action_controller/rescue' - autoload :Resources, 'action_controller/resources' - autoload :Response, 'action_controller/response' - autoload :RewindableInput, 'action_controller/rewindable_input' + autoload :Redirector, 'action_controller/base/redirect' + autoload :Renderer, 'action_controller/base/render' + autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' + autoload :Rescue, 'action_controller/dispatch/rescue' + autoload :Resources, 'action_controller/routing/resources' + autoload :Responder, 'action_controller/base/responder' autoload :Routing, 'action_controller/routing' - autoload :SessionManagement, 'action_controller/session_management' - autoload :StatusCodes, 'action_controller/status_codes' - autoload :Streaming, 'action_controller/streaming' - autoload :TestCase, 'action_controller/test_case' - autoload :TestProcess, 'action_controller/test_process' + autoload :SessionManagement, 'action_controller/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 :UploadedFile, 'action_controller/uploaded_file' - autoload :UploadedStringIO, 'action_controller/uploaded_file' - autoload :UploadedTempfile, 'action_controller/uploaded_file' - autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' - autoload :UrlRewriter, 'action_controller/url_rewriter' - autoload :UrlWriter, 'action_controller/url_rewriter' - autoload :Verification, 'action_controller/verification' + 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' module Assertions - autoload :DomAssertions, 'action_controller/assertions/dom_assertions' - autoload :ModelAssertions, 'action_controller/assertions/model_assertions' - autoload :ResponseAssertions, 'action_controller/assertions/response_assertions' - autoload :RoutingAssertions, 'action_controller/assertions/routing_assertions' - autoload :SelectorAssertions, 'action_controller/assertions/selector_assertions' - autoload :TagAssertions, 'action_controller/assertions/tag_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 - - module Http - autoload :Headers, 'action_controller/headers' - end - - module Session - autoload :AbstractStore, 'action_controller/session/abstract_store' - autoload :CookieStore, 'action_controller/session/cookie_store' - autoload :MemCacheStore, 'action_controller/session/mem_cache_store' - end - - # DEPRECATE: Remove CGI support - autoload :CgiRequest, 'action_controller/cgi_process' - autoload :CGIHandler, 'action_controller/cgi_process' end -autoload :Mime, 'action_controller/mime_type' - autoload :HTML, 'action_controller/vendor/html-scanner' +require 'action_dispatch' require 'action_view' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base/base.rb index 36b80d5780..a01d8f25cc 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base/base.rb @@ -58,22 +58,6 @@ module ActionController #:nodoc: end end - class DoubleRenderError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - class RedirectBackError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - class UnknownHttpMethod < ActionControllerError #:nodoc: end @@ -247,9 +231,8 @@ module ActionController #:nodoc: # end # class Base - DEFAULT_RENDER_STATUS_CODE = "200 OK" - include StatusCodes + include ActionDispatch::StatusCodes cattr_reader :protected_instance_variables # Controller specific instance variables which will not be accessible inside views. @@ -301,7 +284,10 @@ module ActionController #:nodoc: # A YAML parser is also available and can be turned on with: # # ActionController::Base.param_parsers[Mime::YAML] = :yaml - @@param_parsers = {} + @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form, + Mime::URL_ENCODED_FORM => :url_encoded_form, + Mime::XML => :xml_simple, + Mime::JSON => :json } cattr_accessor :param_parsers # Controls the default charset for all renders. @@ -381,8 +367,8 @@ module ActionController #:nodoc: class << self def call(env) # HACK: For global rescue to have access to the original request and response - request = env["action_controller.rescue.request"] ||= Request.new(env) - response = env["action_controller.rescue.response"] ||= Response.new + request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env) + response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new process(request, response) end @@ -514,8 +500,8 @@ module ActionController #:nodoc: def process(request, response, method = :perform_action, *arguments) #:nodoc: response.request = request - initialize_template_class(response) assign_shortcuts(request, response) + initialize_template_class(response) initialize_current_url assign_names @@ -678,339 +664,6 @@ module ActionController #:nodoc: @template.view_paths.push(*path) end - protected - # Renders the content that will be returned to the browser as the response body. - # - # === Rendering an action - # - # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is - # specified. By default, actions are rendered within the current layout (if one exists). - # - # # Renders the template for the action "goal" within the current controller - # render :action => "goal" - # - # # Renders the template for the action "short_goal" within the current controller, - # # but without the current active layout - # render :action => "short_goal", :layout => false - # - # # Renders the template for the action "long_goal" within the current controller, - # # but with a custom layout - # render :action => "long_goal", :layout => "spectacular" - # - # === Rendering partials - # - # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page - # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in - # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the - # controller action responding to Ajax calls). By default, the current layout is not used. - # - # # Renders the same partial with a local variable. - # render :partial => "person", :locals => { :name => "david" } - # - # # Renders the partial, making @new_person available through - # # the local variable 'person' - # render :partial => "person", :object => @new_person - # - # # Renders a collection of the same partial by making each element - # # of @winners available through the local variable "person" as it - # # builds the complete response. - # render :partial => "person", :collection => @winners - # - # # Renders a collection of partials but with a custom local variable name - # render :partial => "admin_person", :collection => @winners, :as => :person - # - # # Renders the same collection of partials, but also renders the - # # person_divider partial between each person partial. - # render :partial => "person", :collection => @winners, :spacer_template => "person_divider" - # - # # Renders a collection of partials located in a view subfolder - # # outside of our current controller. In this example we will be - # # rendering app/views/shared/_note.r(html|xml) Inside the partial - # # each element of @new_notes is available as the local var "note". - # render :partial => "shared/note", :collection => @new_notes - # - # # Renders the partial with a status code of 500 (internal error). - # render :partial => "broken", :status => 500 - # - # Note that the partial filename must also be a valid Ruby variable name, - # so e.g. 2005 and register-user are invalid. - # - # - # == Automatic etagging - # - # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the - # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified - # and the response body will be set to an empty string. No etag header will be inserted if it's already set. - # - # === Rendering a template - # - # Template rendering works just like action rendering except that it takes a path relative to the template root. - # The current layout is automatically applied. - # - # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb) - # render :template => "weblog/show" - # - # # Renders the template with a local variable - # render :template => "weblog/show", :locals => {:customer => Customer.new} - # - # === Rendering a file - # - # File rendering works just like action rendering except that it takes a filesystem path. By default, the path - # is assumed to be absolute, and the current layout is not applied. - # - # # Renders the template located at the absolute filesystem path - # render :file => "/path/to/some/template.erb" - # render :file => "c:/path/to/some/template.erb" - # - # # Renders a template within the current layout, and with a 404 status code - # render :file => "/path/to/some/template.erb", :layout => true, :status => 404 - # render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404 - # - # === Rendering text - # - # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text - # rendering is not done within the active layout. - # - # # Renders the clear text "hello world" with status code 200 - # render :text => "hello world!" - # - # # Renders the clear text "Explosion!" with status code 500 - # render :text => "Explosion!", :status => 500 - # - # # Renders the clear text "Hi there!" within the current active layout (if one exists) - # render :text => "Hi there!", :layout => true - # - # # Renders the clear text "Hi there!" within the layout - # # placed in "app/views/layouts/special.r(html|xml)" - # render :text => "Hi there!", :layout => "special" - # - # The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should - # generally be avoided, as it violates the separation between code and content, and because almost everything that can be - # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates. - # - # # Renders "Hello from code!" - # render :text => proc { |response, output| output.write("Hello from code!") } - # - # === Rendering XML - # - # Rendering XML sets the content type to application/xml. - # - # # Renders '<name>David</name>' - # render :xml => {:name => "David"}.to_xml - # - # It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will - # automatically do that for you: - # - # # Also renders '<name>David</name>' - # render :xml => {:name => "David"} - # - # === Rendering JSON - # - # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected - # that the response will be parsed (or eval'd) for use as a data structure. - # - # # Renders '{"name": "David"}' - # render :json => {:name => "David"}.to_json - # - # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will - # automatically do that for you: - # - # # Also renders '{"name": "David"}' - # render :json => {:name => "David"} - # - # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag), - # so the <tt>:callback</tt> option is provided for these cases. - # - # # Renders 'show({"name": "David"})' - # render :json => {:name => "David"}.to_json, :callback => 'show' - # - # === Rendering an inline template - # - # Rendering of an inline template works as a cross between text and action rendering where the source for the template - # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering - # and the current layout is not used. - # - # # Renders "hello, hello, hello, again" - # render :inline => "<%= 'hello, ' * 3 + 'again' %>" - # - # # Renders "<p>Good seeing you!</p>" using Builder - # render :inline => "xml.p { 'Good seeing you!' }", :type => :builder - # - # # Renders "hello david" - # render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" } - # - # === Rendering inline JavaScriptGenerator page updates - # - # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details), - # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline. - # - # render :update do |page| - # page.replace_html 'user_list', :partial => 'user', :collection => @users - # page.visual_effect :highlight, 'user_list' - # end - # - # === Rendering vanilla JavaScript - # - # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js. - # - # # Renders "alert('hello')" and sets the mime type to text/javascript - # render :js => "alert('hello')" - # - # === Rendering with status and location headers - # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together: - # - # render :xml => post.to_xml, :status => :created, :location => post_url(post) - def render(options = nil, extra_options = {}, &block) #:doc: - raise DoubleRenderError, "Can only render or redirect once per action" if performed? - - validate_render_arguments(options, extra_options, block_given?) - - if options.nil? - options = { :template => default_template, :layout => true } - elsif options == :update - options = extra_options.merge({ :update => true }) - elsif options.is_a?(String) || options.is_a?(Symbol) - case options.to_s.index('/') - when 0 - extra_options[:file] = options - when nil - extra_options[:action] = options - else - extra_options[:template] = options - end - - options = extra_options - end - - layout = pick_layout(options) - response.layout = layout.path_without_format_and_extension if layout - logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout - - if content_type = options[:content_type] - response.content_type = content_type.to_s - end - - if location = options[:location] - response.headers["Location"] = url_for(location) - end - - if options.has_key?(:text) - text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text] - render_for_text(text, options[:status]) - - else - if file = options[:file] - render_for_file(file, options[:status], layout, options[:locals] || {}) - - elsif template = options[:template] - render_for_file(template, options[:status], layout, options[:locals] || {}) - - elsif inline = options[:inline] - render_for_text(@template.render(options.merge(:layout => layout)), options[:status]) - - elsif action_name = options[:action] - render_for_file(default_template(action_name.to_s), options[:status], layout) - - elsif xml = options[:xml] - response.content_type ||= Mime::XML - render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status]) - - elsif js = options[:js] - response.content_type ||= Mime::JS - render_for_text(js, options[:status]) - - elsif json = options[:json] - json = json.to_json unless json.is_a?(String) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - response.content_type ||= Mime::JSON - render_for_text(json, options[:status]) - - elsif options[:partial] - options[:partial] = default_template_name if options[:partial] == true - if layout - render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status]) - else - render_for_text(@template.render(options), options[:status]) - end - - elsif options[:update] - @template.send(:_evaluate_assigns_and_ivars) - - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block) - response.content_type = Mime::JS - render_for_text(generator.to_s, options[:status]) - - elsif options[:nothing] - render_for_text(nil, options[:status]) - - else - render_for_file(default_template, options[:status], layout) - end - end - end - - # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead - # of sending it as the response body to the browser. - def render_to_string(options = nil, &block) #:doc: - render(options, &block) - ensure - response.content_type = nil - erase_render_results - reset_variables_added_to_assigns - end - - # Return a response that has no content (merely headers). The options - # argument is interpreted to be a hash of header names and values. - # This allows you to easily return a response that consists only of - # significant headers: - # - # head :created, :location => person_path(@person) - # - # It can also be used to return exceptional conditions: - # - # return head(:method_not_allowed) unless request.post? - # return head(:bad_request) unless valid_request? - # render - def head(*args) - if args.length > 2 - raise ArgumentError, "too many arguments to head" - elsif args.empty? - raise ArgumentError, "too few arguments to head" - end - options = args.extract_options! - status = interpret_status(args.shift || options.delete(:status) || :ok) - - 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 - end - - # Clears the rendered results, allowing for another render to be performed. - def erase_render_results #:nodoc: - response.body = nil - @performed_render = false - end - - # Clears the redirected results from the headers, resets the status to 200 and returns - # the URL that was used to redirect or nil if there was no redirected URL - # Note that +redirect_to+ will change the body of the response to indicate a redirection. - # The response body is not reset here, see +erase_render_results+ - def erase_redirect_results #:nodoc: - @performed_redirect = false - response.redirected_to = nil - response.redirected_to_method_params = nil - response.status = DEFAULT_RENDER_STATUS_CODE - response.headers.delete('Location') - end - - # Erase both render and redirect results - def erase_results #:nodoc: - erase_render_results - erase_redirect_results - end - def rewrite_options(options) #:nodoc: if defaults = default_url_options(options) defaults.merge(options) @@ -1032,73 +685,6 @@ module ActionController #:nodoc: def default_url_options(options = nil) end - # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: - # - # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+. - # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection. - # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string. - # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt> - # - # Examples: - # redirect_to :action => "show", :id => 5 - # redirect_to post - # redirect_to "http://www.rubyonrails.org" - # redirect_to "/images/screenshot.jpg" - # redirect_to articles_url - # redirect_to :back - # - # The redirection happens as a "302 Moved" header unless otherwise specified. - # - # Examples: - # redirect_to post_url(@post), :status=>:found - # redirect_to :action=>'atom', :status=>:moved_permanently - # redirect_to post_url(@post), :status=>301 - # redirect_to :action=>'atom', :status=>302 - # - # When using <tt>redirect_to :back</tt>, if there is no referrer, - # RedirectBackError will be raised. You may specify some fallback - # behavior for this case by rescuing RedirectBackError. - def redirect_to(options = {}, response_status = {}) #:doc: - raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? - - if options.is_a?(Hash) && options[:status] - status = options.delete(:status) - elsif response_status[:status] - status = response_status[:status] - else - status = 302 - end - - response.redirected_to = options - logger.info("Redirected to #{options}") if logger && logger.info? - - case options - # The scheme name consist of a letter followed by any combination of - # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") - # characters; and is terminated by a colon (":"). - when %r{^\w[\w\d+.-]*:.*} - redirect_to_full_url(options, status) - when String - redirect_to_full_url(request.protocol + request.host_with_port + options, status) - when :back - if referer = request.headers["Referer"] - redirect_to(referer, :status=>status) - else - raise RedirectBackError - end - else - redirect_to_full_url(url_for(options), status) - end - end - - def redirect_to_full_url(url, status) - raise DoubleRenderError if performed? - response.redirect(url, interpret_status(status)) - @performed_redirect = true - end - # Sets the etag and/or last_modified on the response and checks it against # the client request. If the request doesn't match the options provided, the # request is considered stale and should be generated from scratch. Otherwise, @@ -1174,41 +760,20 @@ module ActionController #:nodoc: end private - def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc: - path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path - logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger - render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status - end - - def render_for_text(text = nil, status = nil, append_response = false) #:nodoc: - @performed_render = true - - response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE) - - if append_response - response.body ||= '' - response.body << text.to_s - else - response.body = case text - when Proc then text - when nil then " " # Safari doesn't pass the headers of the return if the response is zero length - else text.to_s - end + def _process_options(options) + if content_type = options[:content_type] + response.content_type = content_type.to_s end - end - def validate_render_arguments(options, extra_options, has_block) - if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol) - raise RenderError, "You called render with invalid options : #{options.inspect}" + if location = options[:location] + response.headers["Location"] = url_for(location) end - if !extra_options.is_a?(Hash) - raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}" - end + response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE) end def initialize_template_class(response) - response.template = ActionView::Base.new(self.class.view_paths, {}, self) + @template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats) response.template.helpers.send :include, self.class.master_helper_module response.redirected_to = nil @performed_render = @performed_redirect = false @@ -1221,7 +786,6 @@ module ActionController #:nodoc: @_response.session = request.session @_session = @_response.session - @template = @_response.template @_headers = @_response.headers end @@ -1257,23 +821,21 @@ module ActionController #:nodoc: end def perform_action - if action_methods.include?(action_name) - send(action_name) - default_render unless performed? - elsif respond_to? :method_missing - method_missing action_name - default_render unless performed? - else - begin - default_render - rescue ActionView::MissingTemplate => e - # Was the implicit template missing, or was it another template? - if e.path == default_template_name - raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller - else - raise e - end - end + if called = action_methods.include?(action_name) + ret = send(action_name) + elsif called = respond_to?(:method_missing) + ret = method_missing(action_name) + end + + return (performed? ? ret : default_render) if called + + begin + default_render + rescue ActionView::MissingTemplate => e + raise e unless e.path == action_name + # If the path is the same as the action_name, the action is completely missing + raise UnknownAction, "No action responded to #{action_name}. Actions: " + + "#{action_methods.sort.to_sentence}", caller end end @@ -1285,22 +847,6 @@ module ActionController #:nodoc: @action_name = (params['action'] || 'index') end - def action_methods - self.class.action_methods - end - - def self.action_methods - @action_methods ||= - # All public instance methods of this class, including ancestors - public_instance_methods(true).map { |m| m.to_s }.to_set - - # Except for public instance methods of Base and its ancestors - Base.public_instance_methods(true).map { |m| m.to_s } + - # Be sure to include shadowed public instance methods of this class - public_instance_methods(false).map { |m| m.to_s } - - # And always exclude explicitly hidden actions - hidden_actions - end - def reset_variables_added_to_assigns @template.instance_variable_set("@assigns_added", nil) end @@ -1315,6 +861,10 @@ module ActionController #:nodoc: "#{request.protocol}#{request.host}#{request.request_uri}" end + def close_session + @_session.close if @_session && @_session.respond_to?(:close) + end + def default_template(action_name = self.action_name) self.view_paths.find_template(default_template_name(action_name), default_template_format) end @@ -1326,7 +876,7 @@ module ActionController #:nodoc: action_name = strip_out_controller(action_name) end end - "#{self.controller_path}/#{action_name}" + "#{controller_path}/#{action_name}" end def strip_out_controller(path) @@ -1338,14 +888,15 @@ module ActionController #:nodoc: end def process_cleanup + close_session end end Base.class_eval do - [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers, + [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers, Cookies, Caching, Verification, Streaming, SessionManagement, - HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, - RecordIdentifier, RequestForgeryProtection, Translation + HttpAuthentication::Basic::ControllerMethods, RecordIdentifier, + RequestForgeryProtection, Translation ].each do |mod| include mod end diff --git a/actionpack/lib/action_controller/benchmarking.rb b/actionpack/lib/action_controller/base/chained/benchmarking.rb index 47377e5fa9..066150f58a 100644 --- a/actionpack/lib/action_controller/benchmarking.rb +++ b/actionpack/lib/action_controller/base/chained/benchmarking.rb @@ -64,7 +64,7 @@ module ActionController #:nodoc: private def perform_action_with_benchmark - if logger + if logger && logger.info? ms = [Benchmark.ms { perform_action_without_benchmark }, 0.01].max logging_view = defined?(@view_runtime) logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/base/chained/filters.rb index 9022b8b279..9022b8b279 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/base/chained/filters.rb diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/base/chained/flash.rb index 56ee9c67e2..56ee9c67e2 100644 --- a/actionpack/lib/action_controller/flash.rb +++ b/actionpack/lib/action_controller/base/chained/flash.rb diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/base/cookies.rb index 840ceb5abd..840ceb5abd 100644 --- a/actionpack/lib/action_controller/cookies.rb +++ b/actionpack/lib/action_controller/base/cookies.rb diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index ba65032f6a..ba65032f6a 100644 --- a/actionpack/lib/action_controller/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb index 5d915fda08..5d915fda08 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/base/http_authentication.rb diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/base/layout.rb index 183d56c2e8..88a15aa6ca 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/base/layout.rb @@ -2,11 +2,7 @@ module ActionController #:nodoc: module Layout #:nodoc: def self.included(base) base.extend(ClassMethods) - base.class_eval do - class << self - alias_method_chain :inherited, :layout - end - end + base.class_inheritable_accessor :layout_name, :layout_conditions end # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in @@ -159,122 +155,90 @@ module ActionController #:nodoc: # # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. module ClassMethods + extend ActiveSupport::Memoizable + # If a layout is specified, all rendered actions will have their result rendered # when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action # performance and have access to them as any normal template would. def layout(template_name, conditions = {}, auto = false) add_layout_conditions(conditions) - write_inheritable_attribute(:layout, template_name) - write_inheritable_attribute(:auto_layout, auto) + self.layout_name = template_name end - def layout_conditions #:nodoc: - @layout_conditions ||= read_inheritable_attribute(:layout_conditions) + def memoized_default_layout(formats) #:nodoc: + self.layout_name || begin + layout = default_layout_name + layout.is_a?(String) ? find_layout(layout, formats) : layout + rescue ActionView::MissingTemplate + end end - def default_layout(format) #:nodoc: - layout = read_inheritable_attribute(:layout) - return layout unless read_inheritable_attribute(:auto_layout) - find_layout(layout, format) + def default_layout(*args) + (@_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_default_layout(*args) + end + + 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, prefix) + end + + def find_layout(*args) + (@_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_find_layout(*args) end def layout_list #:nodoc: Array(view_paths).sum([]) { |path| Dir["#{path.to_str}/layouts/**/*"] } end + memoize :layout_list - def find_layout(layout, *formats) #:nodoc: - return layout if layout.respond_to?(:render) - view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats) - rescue ActionView::MissingTemplate - nil + def default_layout_name + layout_match = name.underscore.sub(/_controller$/, '') + if layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty? + superclass.default_layout_name if superclass.respond_to?(:default_layout_name) + else + layout_match + end end + memoize :default_layout_name private - def inherited_with_layout(child) - inherited_without_layout(child) - unless child.name.blank? - layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '') - child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty? - end - end - def add_layout_conditions(conditions) - write_inheritable_hash(:layout_conditions, normalize_conditions(conditions)) - end - - def normalize_conditions(conditions) - conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})} + # :except => :foo == :except => [:foo] == :except => "foo" == :except => ["foo"] + conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} } + write_inheritable_hash(:layout_conditions, conditions) end end - - # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method - # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method - # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return - # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard. - def active_layout(passed_layout = nil) - layout = passed_layout || self.class.default_layout(default_template_format) - - active_layout = case layout - when Symbol then __send__(layout) - when Proc then layout.call(self) - else layout + + def active_layout(name) + name = self.class.default_layout(formats) if name == true + + layout_name = case name + when Symbol then __send__(name) + when Proc then name.call(self) + else name end - if active_layout - if layout = self.class.find_layout(active_layout, @template.template_format) - layout - else - raise ActionView::MissingTemplate.new(self.class.view_paths, active_layout) - end - end + self.class.find_layout(layout_name, formats) end - private - def candidate_for_layout?(options) - template = options[:template] || default_template(options[:action]) - if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? - begin - !self.view_paths.find_template(template, default_template_format).exempt_from_layout? - rescue ActionView::MissingTemplate - true - end - end - rescue ActionView::MissingTemplate - false - end - - def pick_layout(options) - if options.has_key?(:layout) - case layout = options.delete(:layout) - when FalseClass - nil - when NilClass, TrueClass - active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name) - else - active_layout(layout) - end - else - active_layout if action_has_layout? && candidate_for_layout?(options) - end - end + def _pick_layout(layout_name, implicit = false) + return unless layout_name || implicit + layout_name = true if layout_name.nil? + active_layout(layout_name) if action_has_layout? && layout_name + end + private def action_has_layout? if conditions = self.class.layout_conditions - case - when only = conditions[:only] - only.include?(action_name) - when except = conditions[:except] - !except.include?(action_name) - else - true + if only = conditions[:only] + return only.include?(action_name) + elsif except = conditions[:except] + return !except.include?(action_name) end - else - true end + true end - def default_template_format - response.template.template_format - end end end diff --git a/actionpack/lib/action_controller/base/redirect.rb b/actionpack/lib/action_controller/base/redirect.rb new file mode 100644 index 0000000000..83af793978 --- /dev/null +++ b/actionpack/lib/action_controller/base/redirect.rb @@ -0,0 +1,91 @@ +module ActionController + class RedirectBackError < ActionControllerError #:nodoc: + DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + module Redirector + + # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: + # + # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+. + # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. + # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection. + # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string. + # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places. + # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt> + # + # Examples: + # redirect_to :action => "show", :id => 5 + # redirect_to post + # redirect_to "http://www.rubyonrails.org" + # redirect_to "/images/screenshot.jpg" + # redirect_to articles_url + # redirect_to :back + # + # The redirection happens as a "302 Moved" header unless otherwise specified. + # + # Examples: + # redirect_to post_url(@post), :status=>:found + # redirect_to :action=>'atom', :status=>:moved_permanently + # redirect_to post_url(@post), :status=>301 + # redirect_to :action=>'atom', :status=>302 + # + # When using <tt>redirect_to :back</tt>, if there is no referrer, + # RedirectBackError will be raised. You may specify some fallback + # behavior for this case by rescuing RedirectBackError. + def redirect_to(options = {}, response_status = {}) #:doc: + raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? + + if options.is_a?(Hash) && options[:status] + status = options.delete(:status) + elsif response_status[:status] + status = response_status[:status] + else + status = 302 + end + + response.redirected_to = options + logger.info("Redirected to #{options}") if logger && logger.info? + + case options + # The scheme name consist of a letter followed by any combination of + # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") + # characters; and is terminated by a colon (":"). + when %r{^\w[\w\d+.-]*:.*} + redirect_to_full_url(options, status) + when String + redirect_to_full_url(request.protocol + request.host_with_port + options, status) + when :back + if referer = request.headers["Referer"] + redirect_to(referer, :status=>status) + else + raise RedirectBackError + end + else + redirect_to_full_url(url_for(options), status) + end + end + + def redirect_to_full_url(url, status) + raise DoubleRenderError if performed? + response.redirect(url, interpret_status(status)) + @performed_redirect = true + end + + # Clears the redirected results from the headers, resets the status to 200 and returns + # the URL that was used to redirect or nil if there was no redirected URL + # Note that +redirect_to+ will change the body of the response to indicate a redirection. + # The response body is not reset here, see +erase_render_results+ + def erase_redirect_results #:nodoc: + @performed_redirect = false + response.redirected_to = nil + response.redirected_to_method_params = nil + response.status = DEFAULT_RENDER_STATUS_CODE + response.headers.delete('Location') + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/base/render.rb b/actionpack/lib/action_controller/base/render.rb new file mode 100644 index 0000000000..abba059969 --- /dev/null +++ b/actionpack/lib/action_controller/base/render.rb @@ -0,0 +1,378 @@ +module ActionController + DEFAULT_RENDER_STATUS_CODE = "200 OK" + + class DoubleRenderError < ActionControllerError #:nodoc: + DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + module Renderer + + protected + # Renders the content that will be returned to the browser as the response body. + # + # === Rendering an action + # + # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is + # specified. By default, actions are rendered within the current layout (if one exists). + # + # # Renders the template for the action "goal" within the current controller + # render :action => "goal" + # + # # Renders the template for the action "short_goal" within the current controller, + # # but without the current active layout + # render :action => "short_goal", :layout => false + # + # # Renders the template for the action "long_goal" within the current controller, + # # but with a custom layout + # render :action => "long_goal", :layout => "spectacular" + # + # === Rendering partials + # + # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page + # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in + # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the + # controller action responding to Ajax calls). By default, the current layout is not used. + # + # # Renders the same partial with a local variable. + # render :partial => "person", :locals => { :name => "david" } + # + # # Renders the partial, making @new_person available through + # # the local variable 'person' + # render :partial => "person", :object => @new_person + # + # # Renders a collection of the same partial by making each element + # # of @winners available through the local variable "person" as it + # # builds the complete response. + # render :partial => "person", :collection => @winners + # + # # Renders a collection of partials but with a custom local variable name + # render :partial => "admin_person", :collection => @winners, :as => :person + # + # # Renders the same collection of partials, but also renders the + # # person_divider partial between each person partial. + # render :partial => "person", :collection => @winners, :spacer_template => "person_divider" + # + # # Renders a collection of partials located in a view subfolder + # # outside of our current controller. In this example we will be + # # rendering app/views/shared/_note.r(html|xml) Inside the partial + # # each element of @new_notes is available as the local var "note". + # render :partial => "shared/note", :collection => @new_notes + # + # # Renders the partial with a status code of 500 (internal error). + # render :partial => "broken", :status => 500 + # + # Note that the partial filename must also be a valid Ruby variable name, + # so e.g. 2005 and register-user are invalid. + # + # + # == Automatic etagging + # + # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the + # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified + # and the response body will be set to an empty string. No etag header will be inserted if it's already set. + # + # === Rendering a template + # + # Template rendering works just like action rendering except that it takes a path relative to the template root. + # The current layout is automatically applied. + # + # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb) + # render :template => "weblog/show" + # + # # Renders the template with a local variable + # render :template => "weblog/show", :locals => {:customer => Customer.new} + # + # === Rendering a file + # + # File rendering works just like action rendering except that it takes a filesystem path. By default, the path + # is assumed to be absolute, and the current layout is not applied. + # + # # Renders the template located at the absolute filesystem path + # render :file => "/path/to/some/template.erb" + # render :file => "c:/path/to/some/template.erb" + # + # # Renders a template within the current layout, and with a 404 status code + # render :file => "/path/to/some/template.erb", :layout => true, :status => 404 + # render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404 + # + # === Rendering text + # + # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text + # rendering is not done within the active layout. + # + # # Renders the clear text "hello world" with status code 200 + # render :text => "hello world!" + # + # # Renders the clear text "Explosion!" with status code 500 + # render :text => "Explosion!", :status => 500 + # + # # Renders the clear text "Hi there!" within the current active layout (if one exists) + # render :text => "Hi there!", :layout => true + # + # # Renders the clear text "Hi there!" within the layout + # # placed in "app/views/layouts/special.r(html|xml)" + # render :text => "Hi there!", :layout => "special" + # + # The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should + # generally be avoided, as it violates the separation between code and content, and because almost everything that can be + # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates. + # + # # Renders "Hello from code!" + # render :text => proc { |response, output| output.write("Hello from code!") } + # + # === Rendering XML + # + # Rendering XML sets the content type to application/xml. + # + # # Renders '<name>David</name>' + # render :xml => {:name => "David"}.to_xml + # + # It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will + # automatically do that for you: + # + # # Also renders '<name>David</name>' + # render :xml => {:name => "David"} + # + # === Rendering JSON + # + # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected + # that the response will be parsed (or eval'd) for use as a data structure. + # + # # Renders '{"name": "David"}' + # render :json => {:name => "David"}.to_json + # + # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will + # automatically do that for you: + # + # # Also renders '{"name": "David"}' + # render :json => {:name => "David"} + # + # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag), + # so the <tt>:callback</tt> option is provided for these cases. + # + # # Renders 'show({"name": "David"})' + # render :json => {:name => "David"}.to_json, :callback => 'show' + # + # === Rendering an inline template + # + # Rendering of an inline template works as a cross between text and action rendering where the source for the template + # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering + # and the current layout is not used. + # + # # Renders "hello, hello, hello, again" + # render :inline => "<%= 'hello, ' * 3 + 'again' %>" + # + # # Renders "<p>Good seeing you!</p>" using Builder + # render :inline => "xml.p { 'Good seeing you!' }", :type => :builder + # + # # Renders "hello david" + # render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" } + # + # === Rendering inline JavaScriptGenerator page updates + # + # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details), + # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline. + # + # render :update do |page| + # page.replace_html 'user_list', :partial => 'user', :collection => @users + # page.visual_effect :highlight, 'user_list' + # end + # + # === Rendering vanilla JavaScript + # + # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js. + # + # # Renders "alert('hello')" and sets the mime type to text/javascript + # render :js => "alert('hello')" + # + # === Rendering with status and location headers + # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together: + # + # render :xml => post.to_xml, :status => :created, :location => post_url(post) + def render(options = nil, extra_options = {}, &block) #:doc: + raise DoubleRenderError, "Can only render or redirect once per action" if performed? + + options = { :layout => true } if options.nil? + original, options = options, extra_options unless options.is_a?(Hash) + + layout_name = options.delete(:layout) + + _process_options(options) + + if block_given? + @template.send(:_evaluate_assigns_and_ivars) + + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block) + response.content_type = Mime::JS + return render_for_text(generator.to_s) + end + + if original + return render_for_name(original, layout_name, options) unless block_given? + end + + if options.key?(:text) + return render_for_text(@template._render_text(options[:text], + _pick_layout(layout_name), options)) + end + + file, template = options.values_at(:file, :template) + if file || template + file = template.sub(/^\//, '') if template + return render_for_file(file, [layout_name, !!template], options) + end + + if action_option = options[:action] + return render_for_action(action_option, [layout_name, true], options) + end + + if inline = options[:inline] + render_for_text(@template._render_inline(inline, _pick_layout(layout_name), options)) + + elsif xml = options[:xml] + response.content_type ||= Mime::XML + render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml) + + elsif js = options[:js] + response.content_type ||= Mime::JS + render_for_text(js) + + elsif json = options[:json] + json = json.to_json unless json.is_a?(String) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + response.content_type ||= Mime::JSON + render_for_text(json) + + elsif partial = options[:partial] + if partial == true + parts = [action_name_base, formats, controller_name, true] + elsif partial.is_a?(String) + parts = partial_parts(partial, options) + else + return render_for_text(@template._render_partial(options)) + end + + render_for_parts(parts, layout_name, options) + + elsif options[:nothing] + render_for_text(nil) + + else + render_for_parts([action_name, formats, controller_path], layout_name, options) + end + end + + def partial_parts(name, options) + segments = name.split("/") + parts = segments.pop.split(".") + + 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 + + path = parts.join(".") + prefix = segments[0..-1].join("/") + prefix = prefix.blank? ? controller_path : prefix + parts = [path, formats, prefix] + parts.push options[:object] || true + end + + def formats + @_request.formats.map {|f| f.symbol }.compact + end + + def action_name_base(name = action_name) + (name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s + end + + # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead + # of sending it as the response body to the browser. + def render_to_string(options = nil, &block) #:doc: + render(options, &block) + ensure + response.content_type = nil + erase_render_results + reset_variables_added_to_assigns + end + + # Clears the rendered results, allowing for another render to be performed. + def erase_render_results #:nodoc: + response.body = nil + @performed_render = false + end + + # Erase both render and redirect results + def erase_results #:nodoc: + erase_render_results + erase_redirect_results + end + + # Return a response that has no content (merely headers). The options + # argument is interpreted to be a hash of header names and values. + # This allows you to easily return a response that consists only of + # significant headers: + # + # head :created, :location => person_path(@person) + # + # It can also be used to return exceptional conditions: + # + # return head(:method_not_allowed) unless request.post? + # return head(:bad_request) unless valid_request? + # render + def head(*args) + if args.length > 2 + raise ArgumentError, "too many arguments to head" + elsif args.empty? + raise ArgumentError, "too few arguments to head" + end + options = args.extract_options! + status = interpret_status(args.shift || options.delete(:status) || :ok) + + 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 + end + + private + def render_for_name(name, layout, options) + case name.to_s.index('/') + when 0 + render_for_file(name, layout, options) + when nil + render_for_action(name, layout, options) + else + render_for_file(name.sub(/^\//, ''), [layout, true], options) + end + end + + def render_for_parts(parts, layout, options = {}) + tmp = view_paths.find_by_parts(*parts) + layout = _pick_layout(*layout) unless tmp.exempt_from_layout? + + render_for_text( + @template._render_template_with_layout(tmp, layout, options, parts[3])) + end + + def render_for_file(file, layout, options) + render_for_parts([file, [request.format.to_sym]], layout, options) + end + + def render_for_action(name, layout, options) + parts = [action_name_base(name), formats, controller_name] + render_for_parts(parts, layout, options) + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb index f3e6288c26..f3e6288c26 100644 --- a/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb diff --git a/actionpack/lib/action_controller/base/responder.rb b/actionpack/lib/action_controller/base/responder.rb new file mode 100644 index 0000000000..f83abb5a4b --- /dev/null +++ b/actionpack/lib/action_controller/base/responder.rb @@ -0,0 +1,41 @@ +module ActionController + module Responder + def self.included(klass) + klass.extend ClassMethods + end + + private + def render_for_text(text = nil, append_response = false) #:nodoc: + @performed_render = true + + if append_response + response.body ||= '' + response.body << text.to_s + else + response.body = case text + when Proc then text + when nil then " " # Safari doesn't pass the headers of the return if the response is zero length + else text.to_s + end + end + end + + def action_methods + self.class.action_methods + end + + module ClassMethods + def action_methods + @action_methods ||= + # All public instance methods of this class, including ancestors + public_instance_methods(true).map { |m| m.to_s }.to_set - + # Except for public instance methods of Base and its ancestors + Base.public_instance_methods(true).map { |m| m.to_s } + + # Be sure to include shadowed public instance methods of this class + public_instance_methods(false).map { |m| m.to_s } - + # And always exclude explicitly hidden actions + hidden_actions + end + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb index e1786913a7..e1786913a7 100644 --- a/actionpack/lib/action_controller/streaming.rb +++ b/actionpack/lib/action_controller/base/streaming.rb diff --git a/actionpack/lib/action_controller/verification.rb b/actionpack/lib/action_controller/base/verification.rb index 7bf09ba6ea..7bf09ba6ea 100644 --- a/actionpack/lib/action_controller/verification.rb +++ b/actionpack/lib/action_controller/base/verification.rb diff --git a/actionpack/lib/action_controller/cgi_ext.rb b/actionpack/lib/action_controller/cgi/ext.rb index 406b6f06d6..558748f4bd 100644 --- a/actionpack/lib/action_controller/cgi_ext.rb +++ b/actionpack/lib/action_controller/cgi/ext.rb @@ -1,6 +1,6 @@ -require 'action_controller/cgi_ext/stdinput' -require 'action_controller/cgi_ext/query_extension' -require 'action_controller/cgi_ext/cookie' +require 'action_controller/cgi/ext/stdinput' +require 'action_controller/cgi/ext/query_extension' +require 'action_controller/cgi/ext/cookie' class CGI #:nodoc: include ActionController::CgiExt::Stdinput diff --git a/actionpack/lib/action_controller/cgi_ext/cookie.rb b/actionpack/lib/action_controller/cgi/ext/cookie.rb index 9cd19bb12d..9cd19bb12d 100644 --- a/actionpack/lib/action_controller/cgi_ext/cookie.rb +++ b/actionpack/lib/action_controller/cgi/ext/cookie.rb diff --git a/actionpack/lib/action_controller/cgi_ext/query_extension.rb b/actionpack/lib/action_controller/cgi/ext/query_extension.rb index 9620fd2873..9620fd2873 100644 --- a/actionpack/lib/action_controller/cgi_ext/query_extension.rb +++ b/actionpack/lib/action_controller/cgi/ext/query_extension.rb diff --git a/actionpack/lib/action_controller/cgi_ext/stdinput.rb b/actionpack/lib/action_controller/cgi/ext/stdinput.rb index 5e9b6784af..5e9b6784af 100644 --- a/actionpack/lib/action_controller/cgi_ext/stdinput.rb +++ b/actionpack/lib/action_controller/cgi/ext/stdinput.rb diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi/process.rb index 7e5e95e135..ffcad5666a 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi/process.rb @@ -1,5 +1,3 @@ -require 'action_controller/cgi_ext' - module ActionController #:nodoc: class CGIHandler module ProperStream diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb index 781bc48887..e205245f13 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb @@ -45,7 +45,7 @@ module ActionController end cattr_accessor :middleware - self.middleware = MiddlewareStack.new do |middleware| + self.middleware = ActionDispatch::MiddlewareStack.new do |middleware| middlewares = File.join(File.dirname(__FILE__), "middlewares.rb") middleware.instance_eval(File.read(middlewares)) end diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/dispatch/middlewares.rb index f9cfc2b18e..3bf3dbebab 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/dispatch/middlewares.rb @@ -2,10 +2,10 @@ use "Rack::Lock", :if => lambda { !ActionController::Base.allow_concurrency } -use "ActionController::Failsafe" +use "ActionDispatch::Failsafe" -["ActionController::Session::CookieStore", - "ActionController::Session::MemCacheStore", +["ActionDispatch::Session::CookieStore", + "ActionDispatch::Session::MemCacheStore", "ActiveRecord::SessionStore"].each do |store| use(store, ActionController::Base.session_options, :if => lambda { @@ -16,6 +16,6 @@ use "ActionController::Failsafe" ) end -use "ActionController::RewindableInput" -use "ActionController::ParamsParser" +use "ActionDispatch::RewindableInput" +use "ActionDispatch::ParamsParser" use "Rack::MethodOverride" diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/dispatch/rescue.rb index 4b7d1e32fd..df0a976204 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/dispatch/rescue.rb @@ -60,8 +60,8 @@ module ActionController #:nodoc: module ClassMethods def call_with_exception(env, exception) #:nodoc: - request = env["action_controller.rescue.request"] ||= Request.new(env) - response = env["action_controller.rescue.response"] ||= Response.new + request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env) + response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new new.process(request, response, :rescue_action, exception) end end @@ -125,11 +125,13 @@ module ActionController #:nodoc: @template.instance_variable_set("@exception", exception) @template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH) @template.instance_variable_set("@contents", - @template.render(:file => template_path_for_local_rescue(exception))) + @template._render_template(template_path_for_local_rescue(exception))) response.content_type = Mime::HTML - render_for_file(rescues_path("layout"), - response_code_for_rescue(exception)) + response.status = interpret_status(response_code_for_rescue(exception)) + + content = @template._render_template(rescues_path("layout")) + render_for_text(content) end def rescue_action_without_handler(exception) @@ -157,7 +159,7 @@ module ActionController #:nodoc: end def rescues_path(template_name) - RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"] + RESCUES_TEMPLATE_PATH.find_template("rescues/#{template_name}.erb") end def template_path_for_local_rescue(exception) diff --git a/actionpack/lib/action_controller/templates/rescues/_request_and_response.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb index 64b34650b1..64b34650b1 100644 --- a/actionpack/lib/action_controller/templates/rescues/_request_and_response.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb diff --git a/actionpack/lib/action_controller/templates/rescues/_trace.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb index bb2d8375bd..bb2d8375bd 100644 --- a/actionpack/lib/action_controller/templates/rescues/_trace.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb diff --git a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb index 669da1b26e..95be64511d 100644 --- a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb @@ -6,6 +6,5 @@ </h1> <pre><%=h @exception.clean_message %></pre> -<%= render :file => @rescues_path["rescues/_trace.erb"] %> - -<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %> +<%= @template._render_template(@rescues_path.find_template("rescues/_trace.erb")) %> +<%= @template._render_template(@rescues_path.find_template("rescues/_request_and_response.erb")) %>
\ No newline at end of file diff --git a/actionpack/lib/action_controller/templates/rescues/layout.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb index 4a04742e40..4a04742e40 100644 --- a/actionpack/lib/action_controller/templates/rescues/layout.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb diff --git a/actionpack/lib/action_controller/templates/rescues/missing_template.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb index dbfdf76947..dbfdf76947 100644 --- a/actionpack/lib/action_controller/templates/rescues/missing_template.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb diff --git a/actionpack/lib/action_controller/templates/rescues/routing_error.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb index ccfa858cce..ccfa858cce 100644 --- a/actionpack/lib/action_controller/templates/rescues/routing_error.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb diff --git a/actionpack/lib/action_controller/templates/rescues/template_error.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb index 2e34e03bd5..2e34e03bd5 100644 --- a/actionpack/lib/action_controller/templates/rescues/template_error.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb diff --git a/actionpack/lib/action_controller/templates/rescues/unknown_action.erb b/actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb index 683379da10..683379da10 100644 --- a/actionpack/lib/action_controller/templates/rescues/unknown_action.erb +++ b/actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime/responds.rb index 4b3d14c2d4..bac225ab2a 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime/responds.rb @@ -127,7 +127,7 @@ module ActionController #:nodoc: @order << mime_type @responses[mime_type] ||= Proc.new do - @response.template.template_format = mime_type.to_sym + @response.template.formats = [mime_type.to_sym] @response.content_type = mime_type.to_s block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) end diff --git a/actionpack/lib/action_controller/rack_ext.rb b/actionpack/lib/action_controller/rack_ext.rb deleted file mode 100644 index 2ba6654e3d..0000000000 --- a/actionpack/lib/action_controller/rack_ext.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'action_controller/rack_ext/lock' -require 'action_controller/rack_ext/multipart' -require 'action_controller/rack_ext/parse_query' diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 742d290ad6..6bda27e23a 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionController # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or # Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate # the view actions to a higher logical level. Example: diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb index 924d1aa6bd..924d1aa6bd 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb index bb6cb437b7..bb6cb437b7 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/routing/resources.rb index e8988aa737..e8988aa737 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/routing/resources.rb diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 044ace7de1..70cd1f642d 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -428,7 +428,7 @@ module ActionController end def call(env) - request = Request.new(env) + request = ActionDispatch::Request.new(env) app = Routing::Routes.recognize(request) app.call(env).to_a end diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session/management.rb index b556f04649..ffce8e1bd1 100644 --- a/actionpack/lib/action_controller/session_management.rb +++ b/actionpack/lib/action_controller/session/management.rb @@ -26,7 +26,7 @@ module ActionController #:nodoc: if defined? @@session_store @@session_store else - Session::CookieStore + ActionDispatch::Session::CookieStore end end diff --git a/actionpack/lib/action_controller/assertions/dom_assertions.rb b/actionpack/lib/action_controller/testing/assertions/dom.rb index 5ffe5f1883..5ffe5f1883 100644 --- a/actionpack/lib/action_controller/assertions/dom_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/dom.rb diff --git a/actionpack/lib/action_controller/assertions/model_assertions.rb b/actionpack/lib/action_controller/testing/assertions/model.rb index 3a7b39b106..3a7b39b106 100644 --- a/actionpack/lib/action_controller/assertions/model_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/model.rb diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/testing/assertions/response.rb index 5976090273..ca0a9bbf52 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/response.rb @@ -11,7 +11,7 @@ module ActionController # # You can also pass an explicit status number like assert_response(501) # or its symbolic equivalent assert_response(:not_implemented). - # See ActionController::StatusCodes for a full list. + # See ActionDispatch::StatusCodes for a full list. # # ==== Examples # @@ -27,7 +27,7 @@ module ActionController assert_block("") { true } # to count the assertion elsif type.is_a?(Fixnum) && @response.response_code == type assert_block("") { true } # to count the assertion - elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type] + elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type] assert_block("") { true } # to count the assertion else if @response.error? diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/testing/assertions/routing.rb index 5101751cea..5101751cea 100644 --- a/actionpack/lib/action_controller/assertions/routing_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/routing.rb diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/testing/assertions/selector.rb index 0d56ea5ef7..0d56ea5ef7 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/selector.rb diff --git a/actionpack/lib/action_controller/assertions/tag_assertions.rb b/actionpack/lib/action_controller/testing/assertions/tag.rb index 80249e0e83..80249e0e83 100644 --- a/actionpack/lib/action_controller/assertions/tag_assertions.rb +++ b/actionpack/lib/action_controller/testing/assertions/tag.rb diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/testing/integration.rb index 163ba84a3e..0da23f9dc8 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/testing/integration.rb @@ -316,7 +316,7 @@ module ActionController @html_document = nil @status = status.to_i - @status_message = StatusCodes::STATUS_CODES[@status] + @status_message = ActionDispatch::StatusCodes::STATUS_CODES[@status] @headers = Rack::Utils::HeaderHash.new(headers) @@ -335,7 +335,7 @@ module ActionController else # Decorate responses from Rack Middleware and Rails Metal # as an Response for the purposes of integration testing - @response = Response.new + @response = ActionDispatch::Response.new @response.status = status.to_s @response.headers.replace(@headers) @response.body = @body @@ -374,7 +374,7 @@ module ActionController "SERVER_PORT" => https? ? "443" : "80", "HTTPS" => https? ? "on" : "off" } - UrlRewriter.new(Request.new(env), {}) + UrlRewriter.new(ActionDispatch::Request.new(env), {}) end def name_with_prefix(prefix, name) diff --git a/actionpack/lib/action_controller/performance_test.rb b/actionpack/lib/action_controller/testing/performance.rb index d88180087d..d88180087d 100644 --- a/actionpack/lib/action_controller/performance_test.rb +++ b/actionpack/lib/action_controller/testing/performance.rb diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/testing/process.rb index 4b5fc3a3c1..38e37c7a18 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -1,5 +1,5 @@ module ActionController #:nodoc: - class TestRequest < Request #:nodoc: + class TestRequest < ActionDispatch::Request #:nodoc: attr_accessor :cookies, :session_options attr_accessor :query_parameters, :path, :session attr_accessor :host @@ -271,7 +271,7 @@ module ActionController #:nodoc: # controller actions. # # See Response for more information on controller response objects. - class TestResponse < Response + class TestResponse < ActionDispatch::Response include TestResponseBehavior def recycle! diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb index 0b0d0c799b..c14785ba83 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/testing/test_case.rb @@ -1,5 +1,5 @@ require 'active_support/test_case' -require 'action_controller/test_process' +require 'action_controller/testing/process' module ActionController # Superclass for ActionController functional tests. Functional tests allow you to diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb new file mode 100644 index 0000000000..393ccaa795 --- /dev/null +++ b/actionpack/lib/action_dispatch.rb @@ -0,0 +1,64 @@ +#-- +# 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. +#++ + +begin + require 'active_support' +rescue LoadError + activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" + if File.directory?(activesupport_path) + $:.unshift activesupport_path + require 'active_support' + end +end + +gem 'rack', '>= 0.9.0' +require 'rack' +require 'action_dispatch/rack' + +module ActionDispatch + autoload :Request, 'action_dispatch/http/request' + autoload :Response, 'action_dispatch/http/response' + autoload :StatusCodes, 'action_dispatch/http/status_codes' + + autoload :Failsafe, 'action_dispatch/middleware/failsafe' + autoload :ParamsParser, 'action_dispatch/middleware/params_parser' + autoload :RewindableInput, 'action_dispatch/middleware/rewindable_input' + + autoload :MiddlewareStack, 'action_dispatch/utils/middleware_stack' + autoload :UploadedFile, 'action_dispatch/utils/uploaded_file' + autoload :UploadedStringIO, 'action_dispatch/utils/uploaded_file' + autoload :UploadedTempfile, 'action_dispatch/utils/uploaded_file' + autoload :UrlEncodedPairParser, 'action_dispatch/utils/url_encoded_pair_parser' + + module Http + autoload :Headers, 'action_dispatch/http/headers' + end + + module Session + autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' + end +end + +autoload :Mime, 'action_dispatch/http/mime_type' diff --git a/actionpack/lib/action_controller/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 139669c66f..2a41b4dbad 100644 --- a/actionpack/lib/action_controller/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -1,6 +1,6 @@ require 'active_support/memoizable' -module ActionController +module ActionDispatch module Http class Headers < ::Hash extend ActiveSupport::Memoizable diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 7af8510b6b..0a7b1001c8 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -6,7 +6,7 @@ module Mime LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? } def self.[](type) - Type.lookup_by_extension(type) + Type.lookup_by_extension(type.to_s) end # Encapsulates the notion of a mime type. Can be used at render time, for example, with: @@ -31,7 +31,7 @@ module Mime # only needs to protect against these types. @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text] cattr_reader :browser_generated_types - + attr_reader :symbol @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml] def self.unverifiable_types @@ -176,6 +176,8 @@ module Mime def ==(mime_type) return false if mime_type.blank? (@synonyms + [ self ]).any? do |synonym| + require "ruby-debug" + debugger if mime_type.is_a?(Array) synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym end end @@ -209,4 +211,4 @@ module Mime end end -require 'action_controller/mime_types' +require 'action_dispatch/http/mime_types' diff --git a/actionpack/lib/action_controller/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 2d7fba1173..2d7fba1173 100644 --- a/actionpack/lib/action_controller/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_dispatch/http/request.rb index a8729bb6f5..366ac26421 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -5,7 +5,7 @@ require 'strscan' require 'active_support/memoizable' require 'action_controller/cgi_ext' -module ActionController +module ActionDispatch class Request < Rack::Request %w[ AUTH_TYPE GATEWAY_INTERFACE @@ -32,7 +32,7 @@ module ActionController # <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS # constant above, an UnknownHttpMethod exception is raised. def request_method - @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") + HTTP_METHOD_LOOKUP[super] || raise(ActionController::UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") end # Returns the HTTP request \method used for action processing as a @@ -73,7 +73,7 @@ module ActionController # # request.headers["Content-Type"] # => "text/plain" def headers - @headers ||= ActionController::Http::Headers.new(@env) + Http::Headers.new(@env) end # Returns the content length of the request as an integer. @@ -100,11 +100,16 @@ module ActionController @accepts ||= begin header = @env['HTTP_ACCEPT'].to_s.strip - if header.empty? - [content_type, Mime::ALL].compact - else - Mime::Type.parse(header) + fallback = xhr? ? Mime::JS : Mime::HTML + + if header.empty? + [content_type, fallback, Mime::ALL].compact + else + ret = Mime::Type.parse(header) + if ret.last == Mime::ALL + ret.insert(-2, fallback) end + ret end end @@ -150,11 +155,11 @@ module ActionController # GET /posts/5.xhtml | request.format => Mime::HTML # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt> - def format + def format(view_path = []) @format ||= if parameters[:format] Mime[parameters[:format]] - elsif Base.use_accept_header && !(accepts == ONLY_ALL) + elsif ActionController::Base.use_accept_header && !(accepts == ONLY_ALL) accepts.first elsif xhr? then Mime::JS else Mime::HTML @@ -163,7 +168,7 @@ module ActionController def formats @formats = - if Base.use_accept_header + if ActionController::Base.use_accept_header Array(Mime[parameters[:format]] || accepts) else [format] @@ -235,7 +240,7 @@ module ActionController if @env.include? 'HTTP_CLIENT_IP' if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP']) # We don't know which came from the proxy, and which from the user - raise ActionControllerError.new(<<EOM) + raise ActionController::ActionControllerError.new(<<EOM) IP spoofing attack?! HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect} diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 27860a6207..e1d8ee3527 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -1,6 +1,6 @@ require 'digest/md5' -module ActionController # :nodoc: +module ActionDispatch # :nodoc: # Represents an HTTP response generated by a controller action. One can use # an ActionController::Response object to retrieve the current state # of the response, or customize the response. An Response object can diff --git a/actionpack/lib/action_controller/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb index 4977c79491..cec9d2e3a1 100644 --- a/actionpack/lib/action_controller/status_codes.rb +++ b/actionpack/lib/action_dispatch/http/status_codes.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch module StatusCodes #:nodoc: # Defines the standard HTTP status codes, by integer, with their # corresponding default message texts. diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_dispatch/middleware/failsafe.rb index 567581142c..7379a696aa 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_dispatch/middleware/failsafe.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch class Failsafe cattr_accessor :error_file_path self.error_file_path = Rails.public_path if defined?(Rails.public_path) diff --git a/actionpack/lib/action_controller/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index d269fe07fa..6df572268c 100644 --- a/actionpack/lib/action_controller/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch class ParamsParser ActionController::Base.param_parsers[Mime::XML] = :xml_simple ActionController::Base.param_parsers[Mime::JSON] = :json diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_dispatch/middleware/rewindable_input.rb index cedfb7fd75..725414efc4 100644 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ b/actionpack/lib/action_dispatch/middleware/rewindable_input.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch class RewindableInput class RewindableIO < ActiveSupport::BasicObject def initialize(io) diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 9434c2e05e..879d98fbdb 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -1,6 +1,6 @@ require 'rack/utils' -module ActionController +module ActionDispatch module Session class AbstractStore ENV_SESSION_KEY = 'rack.session'.freeze diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 5a728d1877..ec93f66a88 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch module Session # This cookie-based session store is the Rails default. Sessions typically # contain at most a user_id and flash message; both fit within the 4K cookie diff --git a/actionpack/lib/action_controller/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index f745715a97..8f448970d9 100644 --- a/actionpack/lib/action_controller/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -1,7 +1,7 @@ begin require_library_or_gem 'memcache' - module ActionController + module ActionDispatch module Session class MemCacheStore < AbstractStore def initialize(app, options = {}) diff --git a/actionpack/lib/action_dispatch/rack.rb b/actionpack/lib/action_dispatch/rack.rb new file mode 100644 index 0000000000..69df9dac06 --- /dev/null +++ b/actionpack/lib/action_dispatch/rack.rb @@ -0,0 +1,3 @@ +require 'action_dispatch/rack/lock' +require 'action_dispatch/rack/multipart' +require 'action_dispatch/rack/parse_query' diff --git a/actionpack/lib/action_controller/rack_ext/lock.rb b/actionpack/lib/action_dispatch/rack/lock.rb index 9bf1889065..9bf1889065 100644 --- a/actionpack/lib/action_controller/rack_ext/lock.rb +++ b/actionpack/lib/action_dispatch/rack/lock.rb diff --git a/actionpack/lib/action_controller/rack_ext/multipart.rb b/actionpack/lib/action_dispatch/rack/multipart.rb index 3b142307e9..3b142307e9 100644 --- a/actionpack/lib/action_controller/rack_ext/multipart.rb +++ b/actionpack/lib/action_dispatch/rack/multipart.rb diff --git a/actionpack/lib/action_controller/rack_ext/parse_query.rb b/actionpack/lib/action_dispatch/rack/parse_query.rb index 2f21a57770..2f21a57770 100644 --- a/actionpack/lib/action_controller/rack_ext/parse_query.rb +++ b/actionpack/lib/action_dispatch/rack/parse_query.rb diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_dispatch/utils/middleware_stack.rb index dbc2fda41e..924e3dbbc2 100644 --- a/actionpack/lib/action_controller/middleware_stack.rb +++ b/actionpack/lib/action_dispatch/utils/middleware_stack.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch class MiddlewareStack < Array class Middleware def self.new(klass, *args, &block) diff --git a/actionpack/lib/action_controller/uploaded_file.rb b/actionpack/lib/action_dispatch/utils/uploaded_file.rb index 376ba3621a..97dffa089f 100644 --- a/actionpack/lib/action_controller/uploaded_file.rb +++ b/actionpack/lib/action_dispatch/utils/uploaded_file.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch module UploadedFile def self.included(base) base.class_eval do diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_dispatch/utils/url_encoded_pair_parser.rb index 57594c4259..f2e832a977 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_dispatch/utils/url_encoded_pair_parser.rb @@ -1,4 +1,4 @@ -module ActionController +module ActionDispatch class UrlEncodedPairParser < StringScanner #:nodoc: class << self def parse_query_parameters(query_string) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 0b710bd8d9..aa06c19a48 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -31,6 +31,8 @@ rescue LoadError end end +require File.join(File.dirname(__FILE__), "action_pack") + module ActionView def self.load_all! [Base, InlineTemplate, TemplateError] @@ -38,15 +40,16 @@ module ActionView autoload :Base, 'action_view/base' autoload :Helpers, 'action_view/helpers' - autoload :InlineTemplate, 'action_view/inline_template' - autoload :Partials, 'action_view/partials' + autoload :InlineTemplate, 'action_view/template/inline' + autoload :Partials, 'action_view/render/partials' autoload :PathSet, 'action_view/paths' - autoload :Renderable, 'action_view/renderable' - autoload :RenderablePartial, 'action_view/renderable_partial' - autoload :Template, 'action_view/template' - autoload :TemplateError, 'action_view/template_error' - autoload :TemplateHandler, 'action_view/template_handler' - autoload :TemplateHandlers, 'action_view/template_handlers' + autoload :Rendering, 'action_view/render/rendering' + autoload :Renderable, 'action_view/template/renderable' + autoload :RenderablePartial, 'action_view/template/partial' + autoload :Template, 'action_view/template/template' + autoload :TemplateError, 'action_view/template/error' + autoload :TemplateHandler, 'action_view/template/handler' + autoload :TemplateHandlers, 'action_view/template/handlers' autoload :Helpers, 'action_view/helpers' end diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 1529bd3de7..2f7cfeb88e 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -160,14 +160,13 @@ module ActionView #:nodoc: # # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details. class Base - include Helpers, Partials, ::ERB::Util + include Helpers, Rendering, Partials, ::ERB::Util + extend ActiveSupport::Memoizable - attr_accessor :base_path, :assigns, :template_extension + attr_accessor :base_path, :assigns, :template_extension, :formats attr_accessor :controller - attr_writer :template_format - attr_accessor :output_buffer class << self @@ -184,9 +183,13 @@ module ActionView #:nodoc: attr_internal :request + delegate :controller_path, :to => :controller, :allow_nil => true + delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers, :flash, :logger, :action_name, :controller_name, :to => :controller + delegate :find_by_parts, :to => :view_paths + module CompiledTemplates #:nodoc: # holds compiled template code end @@ -209,7 +212,8 @@ module ActionView #:nodoc: end end - def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc: + def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: + @formats = formats || [:html] @assigns = assigns_for_first_render @assigns_added = nil @_render_stack = [] @@ -224,54 +228,6 @@ module ActionView #:nodoc: @view_paths = self.class.process_view_paths(paths) end - # Returns the result of a render that's dictated by the options hash. The primary options are: - # - # * <tt>:partial</tt> - See ActionView::Partials. - # * <tt>:update</tt> - Calls update_page with the block given. - # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. - # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. - # * <tt>:text</tt> - Renders the text passed in out. - # - # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter - # as the locals hash. - def render(options = {}, local_assigns = {}, &block) #:nodoc: - local_assigns ||= {} - - case options - when Hash - options = options.reverse_merge(:locals => {}) - if options[:layout] - _render_with_layout(options, local_assigns, &block) - elsif options[:file] - tempalte = self.view_paths.find_template(options[:file], template_format) - tempalte.render_template(self, options[:locals]) - elsif options[:partial] - render_partial(options) - elsif options[:inline] - InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals]) - elsif options[:text] - options[:text] - end - when :update - update_page(&block) - else - render_partial(:partial => options, :locals => local_assigns) - end - end - - # The format to be used when choosing between multiple templates with - # the same name but differing formats. See +Request#template_format+ - # for more details. - def template_format - if defined? @template_format - @template_format - elsif controller && controller.respond_to?(:request) - @template_format = controller.request.format.to_sym - else - @template_format = :html - end - end - # Access the current template being rendered. # Returns a ActionView::Template object. def template @@ -301,32 +257,5 @@ module ActionView #:nodoc: controller.response.content_type ||= content_type end end - - def _render_with_layout(options, local_assigns, &block) #:nodoc: - partial_layout = options.delete(:layout) - - if block_given? - begin - @_proc_for_layout = block - concat(render(options.merge(:partial => partial_layout))) - ensure - @_proc_for_layout = nil - end - else - begin - original_content_for_layout = @content_for_layout if defined?(@content_for_layout) - @content_for_layout = render(options) - - if (options[:inline] || options[:file] || options[:text]) - @cached_content_for_layout = @content_for_layout - render(:file => partial_layout, :locals => local_assigns) - else - render(options.merge(:partial => partial_layout)) - end - ensure - @content_for_layout = original_content_for_layout - end - end - end end end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 18a209dcea..99676a9c27 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -987,13 +987,13 @@ module ActionView end def render(*options_for_render) - old_format = @context && @context.template_format - @context.template_format = :html if @context + old_formats = @context && @context.formats + @context.formats = [:html] if @context Hash === options_for_render.first ? @context.render(*options_for_render) : options_for_render.first.to_s ensure - @context.template_format = old_format if @context + @context.formats = old_formats if @context end def javascript_object_for(object) diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index ee26542a07..d88296daa6 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -32,18 +32,24 @@ module ActionView #:nodoc: super(*objs.map { |obj| self.class.type_cast(obj) }) end + def find_by_parts(path, extension = nil, prefix = nil, partial = false) + template_path = path.sub(/^\//, '') + + each do |load_path| + if template = load_path.find_by_parts(template_path, extension, prefix, partial) + return template + end + end + + Template.new(path, self) + end + def find_template(original_template_path, format = nil) return original_template_path if original_template_path.respond_to?(:render) template_path = original_template_path.sub(/^\//, '') each do |load_path| - if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"]) - return template - elsif format && (template = load_path["#{template_path}.#{format}"]) - return template - elsif template = load_path["#{template_path}.#{I18n.locale}"] - return template - elsif template = load_path[template_path] + if template = load_path.find_by_parts(template_path, format) return template # Try to find html version if the format is javascript elsif format == :js && template = load_path["#{template_path}.html"] diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/render/partials.rb index 59e82b98a4..e337dcb63b 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -172,63 +172,156 @@ module ActionView module Partials extend ActiveSupport::Memoizable + def _render_partial(options = {}) #:nodoc: + options[:locals] ||= {} + + 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) + template = find_by_parts(*parts) + _render_partial_object(template, options, (object unless object == true)) + end + end + private - def render_partial(options = {}) #:nodoc: - local_assigns = options[:locals] || {} + def partial_parts(name, options) + segments = name.split("/") + parts = segments.pop.split(".") - case partial_path = options[:partial] - when String, Symbol, NilClass - if options.has_key?(:collection) - render_partial_collection(options) - else - _pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns) + 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 - when ActionView::Helpers::FormBuilder - builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '') - local_assigns.merge!(builder_partial_path.to_sym => partial_path) - render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns) - when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope - render_partial_collection(options.except(:partial).merge(:collection => partial_path)) + parts.pop if parts.size == 2 + end + + path = parts.join(".") + prefix = segments[0..-1].join("/") + prefix = prefix.blank? ? controller_path : prefix + parts = [path, formats, prefix] + parts.push options[:object] || true + 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 + end + + def _render_partial_with_layout(layout, options) + if layout + prefix = controller && !layout.include?("/") ? controller.controller_path : nil + layout = find_by_parts(layout, formats, prefix, true) + end + content = _render_partial(options) + return _render_content_with_layout(content, layout, options[:locals]) + end + + def _deprecated_ivar_assign(template) + if respond_to?(:controller) + ivar = :"@#{template.variable_name}" + object = + if controller.instance_variable_defined?(ivar) + ActiveSupport::Deprecation::DeprecatedObjectProxy.new( + controller.instance_variable_get(ivar), + "#{ivar} will no longer be implicitly assigned to #{template.variable_name}") + end + end + 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 + end + + def _render_partial_with_layout(layout, options) + if layout + prefix = controller && !layout.include?("/") ? controller.controller_path : nil + layout = find_by_parts(layout, formats, prefix, true) + end + content = _render_partial(options) + return _render_content_with_layout(content, layout, options[:locals]) + end + + def _deprecated_ivar_assign(template) + if respond_to?(:controller) + ivar = :"@#{template.variable_name}" + object = + if controller.instance_variable_defined?(ivar) + ActiveSupport::Deprecation::DeprecatedObjectProxy.new( + controller.instance_variable_get(ivar), + "#{ivar} will no longer be implicitly assigned to #{template.variable_name}") + end + end + end + + def _array_like_objects + array_like = [Array] + if defined?(ActiveRecord) + array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope) + end + array_like + end + + def _render_partial_object(template, options, object = nil) + if options.key?(:collection) + _render_partial_collection(options.delete(:collection), options, template) else - object = partial_path - render_partial( - :partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path), - :object => object, - :locals => local_assigns - ) + locals = (options[:locals] ||= {}) + object ||= locals[:object] || locals[template.variable_name] + + _set_locals(object, locals, template, options) + _render_template(template, locals) end end - def render_partial_collection(options = {}) #:nodoc: - return nil if options[:collection].blank? + def _set_locals(object, locals, template, options) + object ||= _deprecated_ivar_assign(template) + locals[:object] = locals[template.variable_name] = object + locals[options[:as]] = object if options[:as] + 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]) : '' - partial = options[:partial] - spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : '' - local_assigns = options[:locals] ? options[:locals].clone : {} - as = options[:as] + locals = (options[:locals] ||= {}) + index, @_partial_path = 0, nil + collection.map do |object| + template = passed_template || begin + _partial_path = + ActionController::RecordIdentifier.partial_path(object, controller_path) + template = _pick_partial_template(_partial_path) + end - index = 0 - options[:collection].map do |object| - _partial_path ||= partial || - ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) - template = _pick_partial_template(_partial_path) - local_assigns[template.counter_name] = index - result = template.render_partial(self, object, local_assigns.dup, as) + _set_locals(object, locals, template, options) + locals[template.counter_name] = index + index += 1 - result + _render_template(template, locals) end.join(spacer) end def _pick_partial_template(partial_path) #:nodoc: - if partial_path.include?('/') - path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}") - elsif controller - path = "#{controller.class.controller_path}/_#{partial_path}" - else - path = "_#{partial_path}" - end - - self.view_paths.find_template(path, self.template_format) + prefix = controller_path unless partial_path.include?('/') + find_by_parts(partial_path, formats, prefix, true) end memoize :_pick_partial_template end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb new file mode 100644 index 0000000000..a02c058725 --- /dev/null +++ b/actionpack/lib/action_view/render/rendering.rb @@ -0,0 +1,119 @@ +module ActionView + module Rendering + # Returns the result of a render that's dictated by the options hash. The primary options are: + # + # * <tt>:partial</tt> - See ActionView::Partials. + # * <tt>:update</tt> - Calls update_page with the block given. + # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. + # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. + # * <tt>:text</tt> - Renders the text passed in out. + # + # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter + # as the locals hash. + def render(options = {}, local_assigns = {}, &block) #:nodoc: + local_assigns ||= {} + + @exempt_from_layout = true + + case options + 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) if layout + + if file = options[:file] + template = find_by_parts(file, formats) + _render_template_with_layout(template, layout, :locals => options[:locals]) + elsif inline = options[:inline] + _render_inline(inline, layout, options) + elsif text = options[:text] + _render_text(text, layout, options) + 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) + return content unless layout + + locals ||= {} + + if controller && layout + response.layout = layout.path_without_format_and_extension if controller.respond_to?(:response) + logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger + end + + begin + original_content_for_layout = @content_for_layout if defined?(@content_for_layout) + @content_for_layout = content + + @cached_content_for_layout = @content_for_layout + _render_template(layout, locals) + ensure + @content_for_layout = original_content_for_layout + end + end + + def _render_template(template, local_assigns = {}) + template.compile(local_assigns) + + @_render_stack.push(template) + + _evaluate_assigns_and_ivars + _set_controller_content_type(template.mime_type) if template.respond_to?(:mime_type) + + result = send(template.method_name(local_assigns), local_assigns) do |*names| + if !instance_variable_defined?(:"@content_for_#{names.first}") && + instance_variable_defined?(:@_proc_for_layout) && (proc = @_proc_for_layout) + capture(*names, &proc) + elsif instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}") + instance_variable_get(ivar) + end + end + + @_render_stack.pop + result + rescue Exception => e + raise e if !template.filename || template.is_a?(InlineTemplate) + if TemplateError === e + e.sub_template_of(template) + raise e + else + raise TemplateError.new(template, assigns, e) + end + end + + def _render_inline(inline, layout, options) + content = _render_template(InlineTemplate.new(options[:inline], options[:type]), options[:locals] || {}) + layout ? _render_content_with_layout(content, layout, options[:locals]) : content + end + + def _render_text(text, layout, options) + layout ? _render_content_with_layout(text, layout, options[:locals]) : text + end + + def _render_template_with_layout(template, layout = nil, options = {}, partial = false) + if controller && logger + logger.info("Rendering #{template.path_without_extension}" + + (options[:status] ? " (#{options[:status]})" : '')) + end + + content = if partial + object = partial unless partial == true + _render_partial_object(template, options, object) + else + _render_template(template, options[:locals] || {}) + end + + return content unless layout && !template.exempt_from_layout? + _render_content_with_layout(content, layout, options[:locals] || {}) + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb deleted file mode 100644 index 3ea836fa25..0000000000 --- a/actionpack/lib/action_view/renderable_partial.rb +++ /dev/null @@ -1,47 +0,0 @@ -module ActionView - # NOTE: The template that this mixin is being included into is frozen - # so you cannot set or modify any instance variables - module RenderablePartial #:nodoc: - extend ActiveSupport::Memoizable - - def variable_name - name.sub(/\A_/, '').to_sym - end - memoize :variable_name - - def counter_name - "#{variable_name}_counter".to_sym - end - memoize :counter_name - - def render(view, local_assigns = {}) - if defined? ActionController - ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do - super - end - else - super - end - end - - def render_partial(view, object = nil, local_assigns = {}, as = nil) - object ||= local_assigns[:object] || local_assigns[variable_name] - - if object.nil? && view.respond_to?(:controller) - ivar = :"@#{variable_name}" - object = - if view.controller.instance_variable_defined?(ivar) - ActiveSupport::Deprecation::DeprecatedObjectProxy.new( - view.controller.instance_variable_get(ivar), - "#{ivar} will no longer be implicitly assigned to #{variable_name}") - end - end - - # Ensure correct object is reassigned to other accessors - local_assigns[:object] = local_assigns[variable_name] = object - local_assigns[as] = object if as - - render_template(view, local_assigns) - end - end -end diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template/error.rb index 37cb1c7c6c..37cb1c7c6c 100644 --- a/actionpack/lib/action_view/template_error.rb +++ b/actionpack/lib/action_view/template/error.rb diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template/handler.rb index 672da0ed2b..672da0ed2b 100644 --- a/actionpack/lib/action_view/template_handler.rb +++ b/actionpack/lib/action_view/template/handler.rb diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template/handlers.rb index 205f8628f0..fb85f28851 100644 --- a/actionpack/lib/action_view/template_handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -1,8 +1,8 @@ module ActionView #:nodoc: module TemplateHandlers #:nodoc: - autoload :ERB, 'action_view/template_handlers/erb' - autoload :RJS, 'action_view/template_handlers/rjs' - autoload :Builder, 'action_view/template_handlers/builder' + autoload :ERB, 'action_view/template/handlers/erb' + autoload :RJS, 'action_view/template/handlers/rjs' + autoload :Builder, 'action_view/template/handlers/builder' def self.extended(base) base.register_default_template_handler :erb, TemplateHandlers::ERB diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 788dc93326..788dc93326 100644 --- a/actionpack/lib/action_view/template_handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index e3120ba267..e3120ba267 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index 41a1fddb47..802a79b3fc 100644 --- a/actionpack/lib/action_view/template_handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -4,7 +4,7 @@ module ActionView include Compilable def compile(template) - "@template_format = :html;" + + "@formats = [:html];" + "controller.response.content_type ||= Mime::JS;" + "update_page do |page|;#{template.source}\nend" end diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/template/inline.rb index 54efa543c8..54efa543c8 100644 --- a/actionpack/lib/action_view/inline_template.rb +++ b/actionpack/lib/action_view/template/inline.rb diff --git a/actionpack/lib/action_view/template/partial.rb b/actionpack/lib/action_view/template/partial.rb new file mode 100644 index 0000000000..30dec1dc5b --- /dev/null +++ b/actionpack/lib/action_view/template/partial.rb @@ -0,0 +1,18 @@ +module ActionView + # NOTE: The template that this mixin is being included into is frozen + # so you cannot set or modify any instance variables + module RenderablePartial #:nodoc: + extend ActiveSupport::Memoizable + + def variable_name + name.sub(/\A_/, '').to_sym + end + memoize :variable_name + + def counter_name + "#{variable_name}_counter".to_sym + end + memoize :counter_name + + end +end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/template/renderable.rb index 153e14f68b..35c832aaba 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/template/renderable.rb @@ -23,28 +23,6 @@ module ActionView end memoize :method_name_without_locals - def render(view, local_assigns = {}) - compile(local_assigns) - - stack = view.instance_variable_get(:@_render_stack) - stack.push(self) - - view.send(:_evaluate_assigns_and_ivars) - view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type) - - result = view.send(method_name(local_assigns), local_assigns) do |*names| - ivar = :@_proc_for_layout - if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar)) - view.capture(*names, &proc) - elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}") - view.instance_variable_get(ivar) - end - end - - stack.pop - result - end - def method_name(local_assigns) if local_assigns && local_assigns.any? method_name = method_name_without_locals.dup @@ -55,16 +33,16 @@ module ActionView method_name.to_sym end - private - # Compile and evaluate the template's code (if necessary) - def compile(local_assigns) - render_symbol = method_name(local_assigns) + # Compile and evaluate the template's code (if necessary) + def compile(local_assigns) + render_symbol = method_name(local_assigns) - if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? - compile!(render_symbol, local_assigns) - end + if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? + compile!(render_symbol, local_assigns) end + end + private def compile!(render_symbol, local_assigns) locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template/template.rb index 1361a969a9..4b4b80d48c 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -38,7 +38,7 @@ module ActionView #:nodoc: # should not be confused with format extensions +html+, +js+, +xml+, # etc. A format must be supplied to match a formated file. +hello/index+ # will never match +hello/index.html.erb+. - def [](path) + def find_template(path) templates_in_path do |template| if template.accessible_paths.include?(path) return template @@ -47,6 +47,24 @@ module ActionView #:nodoc: nil end + def find_by_parts(name, extensions = nil, prefix = nil, partial = nil) + path = prefix ? "#{prefix}/" : "" + + name = name.split("/") + name[-1] = "_#{name[-1]}" if partial + + path << name.join("/") + + template = nil + + Array(extensions).each do |extension| + extensioned_path = extension ? "#{path}.#{extension}" : path + template = find_template(extensioned_path) || find_template(path) + break if template + end + template + end + private def templates_in_path (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| @@ -73,7 +91,7 @@ module ActionView #:nodoc: @paths.freeze end - def [](path) + def find_template(path) @paths[path] end end @@ -178,18 +196,6 @@ module ActionView #:nodoc: end memoize :method_segment - def render_template(view, local_assigns = {}) - render(view, local_assigns) - rescue Exception => e - raise e unless filename - if TemplateError === e - e.sub_template_of(self) - raise e - else - raise TemplateError.new(self, view.assigns, e) - end - end - def stale? File.mtime(filename) > mtime end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index ec337bb05b..c8f204046b 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -7,18 +7,15 @@ module ActionView @_rendered = { :template => nil, :partials => Hash.new(0) } initialize_without_template_tracking(*args) end - end - - module Renderable - alias_method :render_without_template_tracking, :render - def render(view, local_assigns = {}) - if respond_to?(:path) && !is_a?(InlineTemplate) - rendered = view.instance_variable_get(:@_rendered) - rendered[:partials][self] += 1 if is_a?(RenderablePartial) - rendered[:template] ||= self + + alias_method :_render_template_without_template_tracking, :_render_template + def _render_template(template, local_assigns = {}) + if template.respond_to?(:path) && !template.is_a?(InlineTemplate) + @_rendered[:partials][template] += 1 if template.is_a?(RenderablePartial) + @_rendered[:template] ||= template end - render_without_template_tracking(view, local_assigns) - end + _render_template_without_template_tracking(template, local_assigns) + end end class TestCase < ActiveSupport::TestCase diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 4baebcb4d1..12b60ddf3c 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -20,8 +20,7 @@ rescue LoadError end require 'action_controller' -require 'action_controller/cgi_ext' -require 'action_controller/test_process' +require 'action_controller/testing/process' require 'action_view/test_case' # Show backtraces for deprecated behavior for quicker cleanup. diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 7cd4e71aa1..6b409c9ae2 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -46,7 +46,7 @@ class DispatcherTest < Test::Unit::TestCase def test_failsafe_response Dispatcher.any_instance.expects(:dispatch).raises('b00m') - ActionController::Failsafe.any_instance.expects(:log_failsafe_exception) + ActionDispatch::Failsafe.any_instance.expects(:log_failsafe_exception) assert_nothing_raised do assert_equal [ diff --git a/actionpack/test/controller/header_test.rb b/actionpack/test/controller/header_test.rb deleted file mode 100644 index 33c14a187c..0000000000 --- a/actionpack/test/controller/header_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'abstract_unit' - -class HeaderTest < Test::Unit::TestCase - def setup - @headers = ActionController::Http::Headers.new("HTTP_CONTENT_TYPE"=>"text/plain") - end - - def test_content_type_works - assert_equal "text/plain", @headers["Content-Type"] - assert_equal "text/plain", @headers["content-type"] - assert_equal "text/plain", @headers["CONTENT_TYPE"] - assert_equal "text/plain", @headers["HTTP_CONTENT_TYPE"] - end -end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 2f5e830fba..16c16aa88d 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -55,7 +55,7 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase def test_third_party_template_library_auto_discovers_layout @controller = ThirdPartyTemplateLibraryController.new get :hello - assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout.to_s + assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout(true).to_s assert_equal 'layouts/third_party_template_library', @response.layout assert_response :success assert_equal 'Mab', @response.body @@ -64,14 +64,14 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase def test_namespaced_controllers_auto_detect_layouts @controller = ControllerNameSpace::NestedController.new get :hello - assert_equal 'layouts/controller_name_space/nested', @controller.active_layout.to_s + assert_equal 'layouts/controller_name_space/nested', @controller.active_layout(true).to_s assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body end def test_namespaced_controllers_auto_detect_layouts @controller = MultipleExtensions.new get :hello - assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout.to_s + assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout(true).to_s assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip end end @@ -83,6 +83,14 @@ class HasOwnLayoutController < LayoutTest layout 'item' end +class OnlyLayoutController < LayoutTest + layout 'item', :only => "hello" +end + +class ExceptLayoutController < LayoutTest + layout 'item', :except => "goodbye" +end + class SetsLayoutInRenderController < LayoutTest def hello render :layout => 'third_party_template_library' @@ -107,6 +115,30 @@ class LayoutSetInResponseTest < ActionController::TestCase get :hello assert_equal 'layouts/item', @response.layout end + + def test_layout_only_exception_when_included + @controller = OnlyLayoutController.new + get :hello + assert_equal 'layouts/item', @response.layout + end + + def test_layout_only_exception_when_excepted + @controller = OnlyLayoutController.new + get :goodbye + assert_equal nil, @response.layout + end + + def test_layout_except_exception_when_included + @controller = ExceptLayoutController.new + get :hello + assert_equal 'layouts/item', @response.layout + end + + def test_layout_except_exception_when_excepted + @controller = ExceptLayoutController.new + get :goodbye + assert_equal nil, @response.layout + end def test_layout_set_when_using_render @controller = SetsLayoutInRenderController.new diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index dc59180a68..948e90254d 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -437,7 +437,7 @@ class MimeControllerTest < ActionController::TestCase unless args.empty? @action = args.first[:action] end - response.body = "#{@action} - #{@template.template_format}" + response.body = "#{@action} - #{@template.formats}" end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 584b9277c4..72b33629ff 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -345,7 +345,7 @@ class TestController < ActionController::Base end def accessing_params_in_template_with_layout - render :layout => nil, :inline => "Hello: <%= params[:name] %>" + render :layout => true, :inline => "Hello: <%= params[:name] %>" end def render_with_explicit_template @@ -1231,7 +1231,7 @@ class RenderTest < ActionController::TestCase assert !@response.headers.include?('Content-Length') assert_response :no_content - ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code| + ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code| get :head_with_symbolic_status, :status => status.to_s assert_equal code, @response.response_code assert_response status @@ -1239,7 +1239,7 @@ class RenderTest < ActionController::TestCase end def test_head_with_integer_status - ActionController::StatusCodes::STATUS_CODES.each do |code, message| + ActionDispatch::StatusCodes::STATUS_CODES.each do |code, message| get :head_with_integer_status, :status => code.to_s assert_equal message, @response.message end @@ -1620,7 +1620,7 @@ class RenderingLoggingTest < ActionController::TestCase @controller.logger = MockLogger.new get :layout_test logged = @controller.logger.logged.find_all {|l| l =~ /render/i } - assert_equal "Rendering template within layouts/standard", logged[0] - assert_equal "Rendering test/hello_world", logged[1] + assert_equal "Rendering test/hello_world", logged[0] + assert_equal "Rendering template within layouts/standard", logged[1] end end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 9f6b45f065..a2a2a3ee29 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -393,7 +393,7 @@ class RescueControllerTest < ActionController::TestCase def test_rescue_dispatcher_exceptions_without_request_set @request.env['REQUEST_URI'] = '/no_way' response = RescueController.call_with_exception(@request.env, ActionController::RoutingError.new("Route not found")) - assert_kind_of ActionController::Response, response + assert_kind_of ActionDispatch::Response, response assert_equal "no way", response.body end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 5fc79baa44..b703f340a0 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -155,7 +155,7 @@ class SendFileTest < ActionController::TestCase define_method "test_default_send_#{method}_status" do @controller.options = { :stream => false } assert_nothing_raised { assert_not_nil process(method) } - assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.status + assert_equal ActionController::DEFAULT_RENDER_STATUS_CODE, @response.status end end end diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb index 95d2eb11c4..3a1a9854c3 100644 --- a/actionpack/test/controller/session/cookie_store_test.rb +++ b/actionpack/test/controller/session/cookie_store_test.rb @@ -6,7 +6,8 @@ class CookieStoreTest < ActionController::IntegrationTest SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33' DispatcherApp = ActionController::Dispatcher.new - CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp, :key => SessionKey, :secret => SessionSecret) + CookieStoreApp = ActionDispatch::Session::CookieStore.new(DispatcherApp, + :key => SessionKey, :secret => SessionSecret) Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1') @@ -49,41 +50,41 @@ class CookieStoreTest < ActionController::IntegrationTest def test_raises_argument_error_if_missing_session_key assert_raise(ArgumentError, nil.inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => nil, :secret => SessionSecret) } assert_raise(ArgumentError, ''.inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => '', :secret => SessionSecret) } end def test_raises_argument_error_if_missing_secret assert_raise(ArgumentError, nil.inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => SessionKey, :secret => nil) } assert_raise(ArgumentError, ''.inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => SessionKey, :secret => '') } end def test_raises_argument_error_if_secret_is_probably_insecure assert_raise(ArgumentError, "password".inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => SessionKey, :secret => "password") } assert_raise(ArgumentError, "secret".inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => SessionKey, :secret => "secret") } assert_raise(ArgumentError, "12345678901234567890123456789".inspect) { - ActionController::Session::CookieStore.new(nil, + ActionDispatch::Session::CookieStore.new(nil, :key => SessionKey, :secret => "12345678901234567890123456789") } end @@ -117,7 +118,7 @@ class CookieStoreTest < ActionController::IntegrationTest def test_close_raises_when_data_overflows with_test_route_set do - assert_raise(ActionController::Session::CookieStore::CookieOverflow) { + assert_raise(ActionDispatch::Session::CookieStore::CookieOverflow) { get '/raise_data_overflow' } end diff --git a/actionpack/test/controller/session/mem_cache_store_test.rb b/actionpack/test/controller/session/mem_cache_store_test.rb index eb896a344c..2e2bf79148 100644 --- a/actionpack/test/controller/session/mem_cache_store_test.rb +++ b/actionpack/test/controller/session/mem_cache_store_test.rb @@ -26,7 +26,7 @@ class MemCacheStoreTest < ActionController::IntegrationTest begin DispatcherApp = ActionController::Dispatcher.new - MemCacheStoreApp = ActionController::Session::MemCacheStore.new( + MemCacheStoreApp = ActionDispatch::Session::MemCacheStore.new( DispatcherApp, :key => '_session_id') diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 65c894c2e7..d378188b43 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -635,7 +635,7 @@ class CleanBacktraceTest < ActionController::TestCase end def test_should_clean_assertion_lines_from_backtrace - path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller") + path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller/testing") exception = ActiveSupport::TestCase::Assertion.new('message') exception.set_backtrace ["#{path}/abc", "#{path}/assertions/def"] clean_backtrace { raise exception } diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index ac84e2dfcd..1539f8f506 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -29,8 +29,8 @@ class ViewLoadPathsTest < ActionController::TestCase @controller = TestController.new # Following is needed in order to setup @controller.template object properly - @controller.send :initialize_template_class, @response @controller.send :assign_shortcuts, @request, @response + @controller.send :initialize_template_class, @response # Track the last warning. @old_behavior = ActiveSupport::Deprecation.behavior diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb new file mode 100644 index 0000000000..ec6ba494dc --- /dev/null +++ b/actionpack/test/dispatch/header_test.rb @@ -0,0 +1,16 @@ +require 'abstract_unit' + +class HeaderTest < ActiveSupport::TestCase + def setup + @headers = ActionDispatch::Http::Headers.new( + "HTTP_CONTENT_TYPE" => "text/plain" + ) + end + + test "content type" do + assert_equal "text/plain", @headers["Content-Type"] + assert_equal "text/plain", @headers["content-type"] + assert_equal "text/plain", @headers["CONTENT_TYPE"] + assert_equal "text/plain", @headers["HTTP_CONTENT_TYPE"] + end +end diff --git a/actionpack/test/controller/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 2a141697da..e5496c848b 100644 --- a/actionpack/test/controller/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -6,7 +6,7 @@ class MiddlewareStackTest < ActiveSupport::TestCase class BazMiddleware; end def setup - @stack = ActionController::MiddlewareStack.new + @stack = ActionDispatch::MiddlewareStack.new @stack.use FooMiddleware @stack.use BarMiddleware end diff --git a/actionpack/test/controller/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index c7faa621d9..2fdf4819bb 100644 --- a/actionpack/test/controller/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -1,57 +1,60 @@ require 'abstract_unit' -class MimeTypeTest < Test::Unit::TestCase - Mime::Type.register "image/png", :png - Mime::Type.register "application/pdf", :pdf +class MimeTypeTest < ActiveSupport::TestCase + Mime::Type.register "image/png", :png unless defined? Mime::PNG + Mime::Type.register "application/pdf", :pdf unless defined? Mime::PDF - def test_parse_single + test "parse single" do Mime::LOOKUP.keys.each do |mime_type| assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type) end end - def test_parse_without_q + test "parse without q" do accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,application/pdf,*/*" expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::TEXT, Mime::PDF, Mime::ALL] assert_equal expect, Mime::Type.parse(accept) end - def test_parse_with_q + test "parse with q" do accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,application/pdf,*/*; q=0.2" expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PDF, Mime::TEXT, Mime::YAML, Mime::ALL] assert_equal expect, Mime::Type.parse(accept) end - + # Accept header send with user HTTP_USER_AGENT: Sunrise/0.42j (Windows XP) - def test_parse_crappy_broken_acceptlines + test "parse crappy broken acceptlines" do accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5" expect = [Mime::HTML, Mime::XML, "image/*", Mime::TEXT, Mime::ALL] assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s } end - # Accept header send with user HTTP_USER_AGENT: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1) - def test_parse_crappy_broken_acceptlines2 + # Accept header send with user HTTP_USER_AGENT: Mozilla/4.0 + # (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1) + test "parse crappy broken acceptlines2" do accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, , pronto/1.00.00, sslvpn/1.00.00.00, */*" expect = ['image/gif', 'image/x-xbitmap', 'image/jpeg','image/pjpeg', 'application/x-shockwave-flash', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/msword', 'pronto/1.00.00', 'sslvpn/1.00.00.00', Mime::ALL ] assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s } end - - def test_custom_type - Mime::Type.register("image/gif", :gif) - assert_nothing_raised do - Mime::GIF - assert_equal Mime::GIF, Mime::SET.last + + test "custom type" do + begin + Mime::Type.register("image/gif", :gif) + assert_nothing_raised do + Mime::GIF + assert_equal Mime::GIF, Mime::SET.last + end + ensure + Mime.module_eval { remove_const :GIF if const_defined?(:GIF) } end - ensure - Mime.module_eval { remove_const :GIF if const_defined?(:GIF) } end - - def test_type_should_be_equal_to_symbol + + test "type should be equal to symbol" do assert_equal Mime::HTML, 'application/xhtml+xml' assert_equal Mime::HTML, :html end - def test_type_convenience_methods + test "type convenience methods" do # Don't test Mime::ALL, since it Mime::ALL#html? == true types = Mime::SET.to_a.map(&:to_sym).uniq - [:all] @@ -67,12 +70,12 @@ class MimeTypeTest < Test::Unit::TestCase end end - def test_mime_all_is_html + test "mime all is html" do assert Mime::ALL.all?, "Mime::ALL is not all?" assert Mime::ALL.html?, "Mime::ALL is not html?" end - def test_verifiable_mime_types + test "verifiable mime types" do all_types = Mime::SET.to_a.map(&:to_sym) all_types.uniq! # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE @@ -82,7 +85,7 @@ class MimeTypeTest < Test::Unit::TestCase assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" } end - def test_regexp_matcher + test "regexp matcher" do assert Mime::JS =~ "text/javascript" assert Mime::JS =~ "application/javascript" assert Mime::JS !~ "text/html" diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/dispatch/rack_test.rb index e458ab6738..a9a9f815da 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/dispatch/rack_test.rb @@ -1,6 +1,8 @@ require 'abstract_unit' -class BaseRackTest < Test::Unit::TestCase +# TODO: Merge these tests into RequestTest + +class BaseRackTest < ActiveSupport::TestCase def setup @env = { "HTTP_MAX_FORWARDS" => "10", @@ -43,30 +45,27 @@ class BaseRackTest < Test::Unit::TestCase "REDIRECT_STATUS" => "200", "REQUEST_METHOD" => "GET" } - @request = ActionController::Request.new(@env) + @request = ActionDispatch::Request.new(@env) # some Nokia phone browsers omit the space after the semicolon separator. # some developers have grown accustomed to using comma in cookie values. - @alt_cookie_fmt_request = ActionController::Request.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"})) + @alt_cookie_fmt_request = ActionDispatch::Request.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"})) end - def default_test; end - private - - def set_content_data(data) - @request.env['REQUEST_METHOD'] = 'POST' - @request.env['CONTENT_LENGTH'] = data.length - @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - @request.env['rack.input'] = StringIO.new(data) - end + def set_content_data(data) + @request.env['REQUEST_METHOD'] = 'POST' + @request.env['CONTENT_LENGTH'] = data.length + @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + @request.env['rack.input'] = StringIO.new(data) + end end class RackRequestTest < BaseRackTest - def test_proxy_request - assert_equal 'glu.ttono.us', @request.host_with_port + test "proxy request" do + assert_equal 'glu.ttono.us', @request.host_with_port(true) end - def test_http_host + test "http host" do @env.delete "HTTP_X_FORWARDED_HOST" @env['HTTP_HOST'] = "rubyonrails.org:8080" assert_equal "rubyonrails.org", @request.host @@ -76,19 +75,19 @@ class RackRequestTest < BaseRackTest assert_equal "www.secondhost.org", @request.host end - def test_http_host_with_default_port_overrides_server_port + test "http host with default port overrides server port" do @env.delete "HTTP_X_FORWARDED_HOST" @env['HTTP_HOST'] = "rubyonrails.org" assert_equal "rubyonrails.org", @request.host_with_port end - def test_host_with_port_defaults_to_server_name_if_no_host_headers + test "host with port defaults to server name if no host headers" do @env.delete "HTTP_X_FORWARDED_HOST" @env.delete "HTTP_HOST" assert_equal "glu.ttono.us:8007", @request.host_with_port end - def test_host_with_port_falls_back_to_server_addr_if_necessary + test "host with port falls back to server addr if necessary" do @env.delete "HTTP_X_FORWARDED_HOST" @env.delete "HTTP_HOST" @env.delete "SERVER_NAME" @@ -97,30 +96,30 @@ class RackRequestTest < BaseRackTest assert_equal "207.7.108.53:8007", @request.host_with_port end - def test_host_with_port_if_http_standard_port_is_specified + test "host with port if http standard port is specified" do @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" assert_equal "glu.ttono.us", @request.host_with_port end - def test_host_with_port_if_https_standard_port_is_specified + test "host with port if https standard port is specified" do @env['HTTP_X_FORWARDED_PROTO'] = "https" @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" assert_equal "glu.ttono.us", @request.host_with_port end - def test_host_if_ipv6_reference + test "host if ipv6 reference" do @env.delete "HTTP_X_FORWARDED_HOST" @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host end - def test_host_if_ipv6_reference_with_port + test "host if ipv6 reference with port" do @env.delete "HTTP_X_FORWARDED_HOST" @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host end - def test_cgi_environment_variables + test "cgi environment variables" do assert_equal "Basic", @request.auth_type assert_equal 0, @request.content_length assert_equal nil, @request.content_type @@ -151,7 +150,7 @@ class RackRequestTest < BaseRackTest assert_equal "lighttpd", @request.server_software end - def test_cookie_syntax_resilience + test "cookie syntax resilience" do cookies = @request.cookies assert_equal "c84ace84796670c052c6ceb2451fb0f2", cookies["_session_id"], cookies.inspect assert_equal "yes", cookies["is_admin"], cookies.inspect @@ -163,39 +162,39 @@ class RackRequestTest < BaseRackTest end class RackRequestParamsParsingTest < BaseRackTest - def test_doesnt_break_when_content_type_has_charset + test "doesnt break when content type has charset" do set_content_data 'flamenco=love' assert_equal({"flamenco"=> "love"}, @request.request_parameters) end - def test_doesnt_interpret_request_uri_as_query_string_when_missing + test "doesnt interpret request uri as query string when missing" do @request.env['REQUEST_URI'] = 'foo' assert_equal({}, @request.query_parameters) end end class RackRequestContentTypeTest < BaseRackTest - def test_html_content_type_verification + test "html content type verification" do @request.env['CONTENT_TYPE'] = Mime::HTML.to_s assert @request.content_type.verify_request? end - def test_xml_content_type_verification + test "xml content type verification" do @request.env['CONTENT_TYPE'] = Mime::XML.to_s assert !@request.content_type.verify_request? end end class RackRequestNeedsRewoundTest < BaseRackTest - def test_body_should_be_rewound + test "body should be rewound" do data = 'foo' @env['rack.input'] = StringIO.new(data) @env['CONTENT_LENGTH'] = data.length @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' # Read the request body by parsing params. - request = ActionController::Request.new(@env) + request = ActionDispatch::Request.new(@env) request.request_parameters # Should have rewound the body. @@ -206,10 +205,10 @@ end class RackResponseTest < BaseRackTest def setup super - @response = ActionController::Response.new + @response = ActionDispatch::Response.new end - def test_simple_output + test "simple output" do @response.body = "Hello, World!" @response.prepare! @@ -228,7 +227,7 @@ class RackResponseTest < BaseRackTest assert_equal ["Hello, World!"], parts end - def test_streaming_block + test "streaming block" do @response.body = Proc.new do |response, output| 5.times { |n| output.write(n) } end @@ -252,11 +251,11 @@ end class RackResponseHeadersTest < BaseRackTest def setup super - @response = ActionController::Response.new + @response = ActionDispatch::Response.new @response.status = "200 OK" end - def test_content_type + test "content type" do [204, 304].each do |c| @response.status = c.to_s assert !response_headers.has_key?("Content-Type"), "#{c} should not have Content-Type header" @@ -268,7 +267,7 @@ class RackResponseHeadersTest < BaseRackTest end end - def test_status + test "status" do assert !response_headers.has_key?('Status') end diff --git a/actionpack/test/controller/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index a3dde72c4e..a3dde72c4e 100644 --- a/actionpack/test/controller/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 054519d0d2..5b9728cc42 100644 --- a/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -215,7 +215,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest def with_muck_middleware original_middleware = ActionController::Dispatcher.middleware middleware = original_middleware.dup - middleware.insert_after ActionController::RewindableInput, MuckMiddleware + middleware.insert_after ActionDispatch::RewindableInput, MuckMiddleware ActionController::Dispatcher.middleware = middleware yield ActionController::Dispatcher.middleware = original_middleware diff --git a/actionpack/test/controller/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index a31e326ddf..a31e326ddf 100644 --- a/actionpack/test/controller/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb diff --git a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb index 89239687de..9f0535bbcc 100644 --- a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb @@ -195,7 +195,7 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest def with_muck_middleware original_middleware = ActionController::Dispatcher.middleware middleware = original_middleware.dup - middleware.insert_after ActionController::RewindableInput, MuckMiddleware + middleware.insert_after ActionDispatch::RewindableInput, MuckMiddleware ActionController::Dispatcher.middleware = middleware yield ActionController::Dispatcher.middleware = original_middleware diff --git a/actionpack/test/controller/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb index ee764e726e..ee764e726e 100644 --- a/actionpack/test/controller/request/xml_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/dispatch/request_test.rb index efe4f136f5..60f71f03ce 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -10,7 +10,7 @@ class RequestTest < ActiveSupport::TestCase ActionController::Base.relative_url_root = nil end - def test_remote_ip + test "remote ip" do assert_equal '0.0.0.0', @request.remote_ip @request.remote_addr = '1.2.3.4' @@ -82,7 +82,7 @@ class RequestTest < ActiveSupport::TestCase @request.env.delete 'HTTP_X_FORWARDED_FOR' end - def test_domains + test "domains" do @request.host = "www.rubyonrails.org" assert_equal "rubyonrails.org", @request.domain @@ -102,7 +102,7 @@ class RequestTest < ActiveSupport::TestCase assert_nil @request.domain end - def test_subdomains + test "subdomains" do @request.host = "www.rubyonrails.org" assert_equal %w( www ), @request.subdomains @@ -128,7 +128,7 @@ class RequestTest < ActiveSupport::TestCase assert_equal [], @request.subdomains end - def test_port_string + test "port string" do @request.port = 80 assert_equal "", @request.port_string @@ -136,7 +136,7 @@ class RequestTest < ActiveSupport::TestCase assert_equal ":8080", @request.port_string end - def test_request_uri + test "request uri" do @request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432' @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1" @@ -242,20 +242,20 @@ class RequestTest < ActiveSupport::TestCase assert_equal "/some/path", @request.path end - def test_host_with_default_port + test "host with default port" do @request.host = "rubyonrails.org" @request.port = 80 assert_equal "rubyonrails.org", @request.host_with_port end - def test_host_with_non_default_port + test "host with non default port" do @request.host = "rubyonrails.org" @request.port = 81 assert_equal "rubyonrails.org:81", @request.host_with_port end - def test_server_software - assert_equal nil, @request.server_software + test "server software" do + assert_equal nil, @request.server_software(true) @request.env['SERVER_SOFTWARE'] = 'Apache3.422' assert_equal 'apache', @request.server_software @@ -264,7 +264,7 @@ class RequestTest < ActiveSupport::TestCase assert_equal 'lighttpd', @request.server_software end - def test_xml_http_request + test "xml http request" do assert !@request.xml_http_request? assert !@request.xhr? @@ -277,47 +277,47 @@ class RequestTest < ActiveSupport::TestCase assert @request.xhr? end - def test_reports_ssl + test "reports ssl" do assert !@request.ssl? @request.env['HTTPS'] = 'on' assert @request.ssl? end - def test_reports_ssl_when_proxied_via_lighttpd + test "reports ssl when proxied via lighttpd" do assert !@request.ssl? @request.env['HTTP_X_FORWARDED_PROTO'] = 'https' assert @request.ssl? end - def test_symbolized_request_methods + test "symbolized request methods" do [:get, :post, :put, :delete].each do |method| self.request_method = method assert_equal method, @request.method end end - def test_invalid_http_method_raises_exception + test "invalid http method raises exception" do assert_raises(ActionController::UnknownHttpMethod) do self.request_method = :random_method @request.request_method end end - def test_allow_method_hacking_on_post + test "allow method hacking on post" do [:get, :head, :options, :put, :post, :delete].each do |method| self.request_method = method assert_equal(method == :head ? :get : method, @request.method) end end - def test_invalid_method_hacking_on_post_raises_exception + test "invalid method hacking on post raises exception" do assert_raises(ActionController::UnknownHttpMethod) do self.request_method = :_random_method @request.request_method end end - def test_restrict_method_hacking + test "restrict method hacking" do @request.instance_eval { @parameters = { :_method => 'put' } } [:get, :put, :delete].each do |method| self.request_method = method @@ -325,72 +325,74 @@ class RequestTest < ActiveSupport::TestCase end end - def test_head_masquerading_as_get + test "head masquerading as get" do self.request_method = :head assert_equal :get, @request.method assert @request.get? assert @request.head? end - def test_xml_format + test "xml format" do @request.instance_eval { @parameters = { :format => 'xml' } } assert_equal Mime::XML, @request.format end - def test_xhtml_format + test "xhtml format" do @request.instance_eval { @parameters = { :format => 'xhtml' } } assert_equal Mime::HTML, @request.format end - def test_txt_format + test "txt format" do @request.instance_eval { @parameters = { :format => 'txt' } } assert_equal Mime::TEXT, @request.format end - def test_nil_format - ActionController::Base.use_accept_header, old = - false, ActionController::Base.use_accept_header + test "nil format" do + begin + ActionController::Base.use_accept_header, old = + false, ActionController::Base.use_accept_header - @request.instance_eval { @parameters = {} } - @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" - assert @request.xhr? - assert_equal Mime::JS, @request.format + @request.instance_eval { @parameters = {} } + @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" + assert @request.xhr? + assert_equal Mime::JS, @request.format - ensure - ActionController::Base.use_accept_header = old + ensure + ActionController::Base.use_accept_header = old + end end - def test_content_type + test "content type" do @request.env["CONTENT_TYPE"] = "text/html" assert_equal Mime::HTML, @request.content_type end - def test_format_assignment_should_set_format + test "format assignment should set format" do @request.instance_eval { self.format = :txt } assert !@request.format.xml? @request.instance_eval { self.format = :xml } assert @request.format.xml? end - def test_content_no_type + test "content no type" do assert_equal nil, @request.content_type end - def test_content_type_xml + test "content type xml" do @request.env["CONTENT_TYPE"] = "application/xml" assert_equal Mime::XML, @request.content_type end - def test_content_type_with_charset + test "content type with charset" do @request.env["CONTENT_TYPE"] = "application/xml; charset=UTF-8" assert_equal Mime::XML, @request.content_type end - def test_user_agent + test "user agent" do assert_not_nil @request.user_agent end - def test_parameters + test "parameters" do @request.stubs(:request_parameters).returns({ "foo" => 1 }) @request.stubs(:query_parameters).returns({ "bar" => 2 }) diff --git a/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml b/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml new file mode 100644 index 0000000000..bbccf0913e --- /dev/null +++ b/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml @@ -0,0 +1 @@ +hello.rhtml
\ No newline at end of file diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index d41111127b..d814f84752 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' class JavaScriptHelperTest < ActionView::TestCase tests ActionView::Helpers::JavaScriptHelper - attr_accessor :template_format, :output_buffer + attr_accessor :formats, :output_buffer def setup @template = self diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index d6b86a3964..8ff29412bf 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -25,7 +25,7 @@ class Author::Nested < Author; end class PrototypeHelperBaseTest < ActionView::TestCase - attr_accessor :template_format, :output_buffer + attr_accessor :formats, :output_buffer def setup @template = self diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index c226e212b5..83de0815f4 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -153,7 +153,7 @@ module RenderTestCases # TODO: The reason for this test is unclear, improve documentation def test_render_missing_xml_partial_and_raise_missing_template - @view.template_format = :xml + @view.formats = [:xml] assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") } end diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 5e45cf65ab..74d91f129e 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -40,7 +40,7 @@ module ActiveRecord # # The example SqlBypass class is a generic SQL session store. You may # use it as a basis for high-performance database-specific stores. - class SessionStore < ActionController::Session::AbstractStore + class SessionStore < ActionDispatch::Session::AbstractStore # The default Active Record class. class Session < ActiveRecord::Base ## diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 7ebb3c48e0..62d538e2d5 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -32,6 +32,7 @@ module ActiveSupport autoload :BufferedLogger, 'active_support/buffered_logger' autoload :Cache, 'active_support/cache' autoload :Callbacks, 'active_support/callbacks' + autoload :ConcurrentHash, 'active_support/concurrent_hash' autoload :Deprecation, 'active_support/deprecation' autoload :Duration, 'active_support/duration' autoload :Gzip, 'active_support/gzip' diff --git a/activesupport/lib/active_support/concurrent_hash.rb b/activesupport/lib/active_support/concurrent_hash.rb new file mode 100644 index 0000000000..c9f9b16da3 --- /dev/null +++ b/activesupport/lib/active_support/concurrent_hash.rb @@ -0,0 +1,26 @@ +module ActiveSupport + class ConcurrentHash + def initialize(hash = {}) + @backup_cache = hash.dup + @frozen_cache = hash.dup.freeze + @mutex = Mutex.new + end + + def []=(k,v) + @mutex.synchronize { @backup_cache[k] = v } + @frozen_cache = @backup_cache.dup.freeze + end + + def [](k) + if @frozen_cache.key?(k) + @frozen_cache[k] + else + @mutex.synchronize { @backup_cache[k] } + end + end + + def empty? + @backup_cache.empty? + end + end +end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 4ffb5d9520..8e9abeaf91 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,4 +1,42 @@ module ActiveSupport + class ConcurrentHash + def initialize(hash = {}) + @backup_cache = hash.dup + @frozen_cache = hash.dup.freeze + @mutex = Mutex.new + end + + def []=(k,v) + @mutex.synchronize { @backup_cache[k] = v } + @frozen_cache = @backup_cache.dup.freeze + end + + def [](k) + if @frozen_cache.key?(k) + @frozen_cache[k] + else + @mutex.synchronize { @backup_cache[k] } + end + end + + def empty? + @backup_cache.empty? + end + end + + module SafelyMemoizable + def safely_memoize(*symbols) + symbols.each do |symbol| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{symbol}(*args) + memoized = @_memoized_#{symbol} || ::ActiveSupport::ConcurrentHash.new + memoized[args] ||= memoized_#{symbol}(*args) + end + RUBY + end + end + end + module Memoizable def self.memoized_ivar_for(symbol) "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb index 325c21397e..7985e3c8df 100644 --- a/ci/cruise_config.rb +++ b/ci/cruise_config.rb @@ -1,6 +1,6 @@ Project.configure do |project| project.build_command = 'ruby ci/ci_build.rb' -# project.email_notifier.emails = ['thewoolleyman@gmail.com'] - project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com'] + project.email_notifier.emails = ['thewoolleyman@gmail.com'] +# project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com'] project.email_notifier.from = 'thewoolleyman+railsci@gmail.com' end diff --git a/railties/lib/dispatcher.rb b/railties/lib/dispatcher.rb index 9f8b59aa3d..7f9a6221d9 100644 --- a/railties/lib/dispatcher.rb +++ b/railties/lib/dispatcher.rb @@ -20,5 +20,5 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -require 'action_controller/dispatcher' +require 'action_controller/dispatch/dispatcher' Dispatcher = ActionController::Dispatcher |