From 94911c7af7f2e1da0a23d64d2055b77bc1f6ece8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 1 Jun 2009 12:33:49 -0700 Subject: Ensure ERB source begins with the encoding comment --- .../lib/action_view/template/handlers/erb.rb | 4 +++- actionpack/lib/action_view/template/template.rb | 6 +++++- actionpack/test/fixtures/test/utf8.html.erb | 1 - actionpack/test/fixtures/test/utf8_magic.html.erb | 5 +++++ actionpack/test/template/render_test.rb | 25 ++++++++++++++++++---- 5 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 actionpack/test/fixtures/test/utf8_magic.html.erb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index d773df7d29..21272ef089 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -16,7 +16,9 @@ module ActionView self.default_format = Mime::HTML def compile(template) - ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src + magic = $1 if template.source =~ /\A(<%#.*coding:\s*(\S+)\s*-?%>)/ + erb = "#{magic}<% __in_erb_template=true %>#{template.source}" + ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end end end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index a9897258d2..e7ea42c2eb 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -53,7 +53,11 @@ module ActionView locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join code = @handler.call(self) - encoding_comment = $1 if code.sub!(/\A(#.*coding.*)\n/, '') + if code.sub!(/\A(#.*coding.*)\n/, '') + encoding_comment = $1 + elsif defined?(Encoding) && Encoding.respond_to?(:default_external) + encoding_comment = "#coding:#{Encoding.default_external}" + end source = <<-end_src def #{method_name}(local_assigns) diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb index 58cd03b439..14fe12debc 100644 --- a/actionpack/test/fixtures/test/utf8.html.erb +++ b/actionpack/test/fixtures/test/utf8.html.erb @@ -1,4 +1,3 @@ -<%# encoding: utf-8 -%> Русский текст <%= "日".encoding %> <%= @output_buffer.encoding %> diff --git a/actionpack/test/fixtures/test/utf8_magic.html.erb b/actionpack/test/fixtures/test/utf8_magic.html.erb new file mode 100644 index 0000000000..58cd03b439 --- /dev/null +++ b/actionpack/test/fixtures/test/utf8_magic.html.erb @@ -0,0 +1,5 @@ +<%# encoding: utf-8 -%> +Русский текст +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index a56d7aee75..20cd4cc1d4 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -247,10 +247,27 @@ module RenderTestCases end if '1.9'.respond_to?(:force_encoding) - def test_render_utf8_template - result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result - assert_equal Encoding::UTF_8, result.encoding + def test_render_utf8_template_with_magic_comment + with_external_encoding Encoding::ASCII_8BIT do + result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal Encoding::UTF_8, result.encoding + end + end + + def test_render_utf8_template_with_default_external_encoding + with_external_encoding Encoding::UTF_8 do + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal Encoding::UTF_8, result.encoding + end + end + + def with_external_encoding(encoding) + old, Encoding.default_external = Encoding.default_external, encoding + yield + ensure + Encoding.default_external = old end end end -- cgit v1.2.3 From 196f780e30fcece25e4d09c12f9b9f7374ebed29 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 1 Jun 2009 15:40:11 -0700 Subject: Get all the callback tests to work on new base --- .../lib/action_controller/abstract/callbacks.rb | 6 ++ actionpack/test/controller/filters_test.rb | 68 +++++++++++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index c6d3413c30..affe053bac 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -26,6 +26,12 @@ module AbstractController end end + def skip_filter(*names, &blk) + skip_before_filter(*names, &blk) + skip_after_filter(*names, &blk) + skip_around_filter(*names, &blk) + end + [:before, :after, :around].each do |filter| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{filter}_filter(*names, &blk) diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 5e28ef6007..df0a9dbfb0 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -231,24 +231,29 @@ class FilterTest < ActionController::TestCase end class ConditionalParentOfConditionalSkippingController < ConditionalFilterController - before_filter :conditional_in_parent, :only => [:show, :another_action] - after_filter :conditional_in_parent, :only => [:show, :another_action] + before_filter :conditional_in_parent_before, :only => [:show, :another_action] + after_filter :conditional_in_parent_after, :only => [:show, :another_action] private - def conditional_in_parent + def conditional_in_parent_before @ran_filter ||= [] - @ran_filter << 'conditional_in_parent' + @ran_filter << 'conditional_in_parent_before' + end + + def conditional_in_parent_after + @ran_filter ||= [] + @ran_filter << 'conditional_in_parent_after' end end class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent, :only => :another_action - skip_after_filter :conditional_in_parent, :only => :another_action + skip_before_filter :conditional_in_parent_before, :only => :another_action + skip_after_filter :conditional_in_parent_after, :only => :another_action end class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent, :only => :show + skip_before_filter :conditional_in_parent_before, :only => :show end class ProcController < PrependingController @@ -593,11 +598,22 @@ class FilterTest < ActionController::TestCase assert_equal "before and after", assigns["execution_log"] end - def test_prepending_and_appending_around_filter - controller = test_process(MixedFilterController) - assert_equal " before aroundfilter before procfilter before appended aroundfilter " + - " after appended aroundfilter after aroundfilter after procfilter ", - MixedFilterController.execution_log + for_tag(:old_base) do + def test_prepending_and_appending_around_filter + controller = test_process(MixedFilterController) + assert_equal " before aroundfilter before procfilter before appended aroundfilter " + + " after appended aroundfilter after aroundfilter after procfilter ", + MixedFilterController.execution_log + end + end + + for_tag(:new_base) do + def test_prepending_and_appending_around_filter + controller = test_process(MixedFilterController) + assert_equal " before aroundfilter before procfilter before appended aroundfilter " + + " after appended aroundfilter after procfilter after aroundfilter ", + MixedFilterController.execution_log + end end def test_rendering_breaks_filtering_chain @@ -658,18 +674,18 @@ class FilterTest < ActionController::TestCase def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional test_process(ChildOfConditionalParentController) - assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter'] + assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(ChildOfConditionalParentController, 'another_action') assert_nil assigns['ran_filter'] end def test_condition_skipping_of_filters_when_siblings_also_have_conditions test_process(ChildOfConditionalParentController) - assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter'], "1" + assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(AnotherChildOfConditionalParentController) - assert_equal nil, assigns['ran_filter'] + assert_equal %w( conditional_in_parent_after ), assigns['ran_filter'] test_process(ChildOfConditionalParentController) - assert_equal %w( conditional_in_parent conditional_in_parent ), assigns['ran_filter'] + assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] end def test_changing_the_requirements @@ -823,7 +839,9 @@ class ControllerWithAllTypesOfFilters < PostsController end class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters + $vbf = true skip_filter :around_again + $vbf = false skip_filter :after end @@ -886,9 +904,18 @@ class YieldingAroundFiltersTest < ActionController::TestCase end end - def test_filter_order_with_all_filter_types - test_process(ControllerWithAllTypesOfFilters,'no_raise') - assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after', assigns['ran_filter'].join(' ') + for_tag(:old_base) do + def test_filter_order_with_all_filter_types + test_process(ControllerWithAllTypesOfFilters,'no_raise') + assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after', assigns['ran_filter'].join(' ') + end + end + + for_tag(:new_base) do + def test_filter_order_with_all_filter_types + test_process(ControllerWithAllTypesOfFilters,'no_raise') + assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ') + end end def test_filter_order_with_skip_filter_method @@ -901,7 +928,6 @@ class YieldingAroundFiltersTest < ActionController::TestCase response = test_process(controller, 'fail_1') assert_equal ' ', response.body assert_equal 1, controller.instance_variable_get(:@try) - assert controller.instance_variable_get(:@before_filter_chain_aborted) end def test_second_filter_in_multiple_before_filter_chain_halts @@ -909,7 +935,6 @@ class YieldingAroundFiltersTest < ActionController::TestCase response = test_process(controller, 'fail_2') assert_equal ' ', response.body assert_equal 2, controller.instance_variable_get(:@try) - assert controller.instance_variable_get(:@before_filter_chain_aborted) end def test_last_filter_in_multiple_before_filter_chain_halts @@ -917,7 +942,6 @@ class YieldingAroundFiltersTest < ActionController::TestCase response = test_process(controller, 'fail_3') assert_equal ' ', response.body assert_equal 3, controller.instance_variable_get(:@try) - assert controller.instance_variable_get(:@before_filter_chain_aborted) end protected -- cgit v1.2.3 From 971e2438d98326c994ec6d3ef8e37b7e868ed6e2 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 2 Jun 2009 19:00:59 -0700 Subject: Simplify callbacks to use less metaprogramming --- .../lib/action_controller/abstract/callbacks.rb | 30 +++++++++++----------- .../test/abstract_controller/callbacks_test.rb | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index affe053bac..dd4213e847 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -32,32 +32,32 @@ module AbstractController skip_around_filter(*names, &blk) end + def _insert_callbacks(names, block) + options = names.last.is_a?(Hash) ? names.pop : {} + _normalize_callback_options(options) + names.push(block) if block + names.each do |name| + yield name, options + end + end + [:before, :after, :around].each do |filter| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - process_action_callback(:#{filter}, name, options) + _insert_callbacks(names, blk) do |name, options| + _set_callback(:process_action, :#{filter}, name, options) end end def prepend_#{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - process_action_callback(:#{filter}, name, options.merge(:prepend => true)) + _insert_callbacks(names, blk) do |name, options| + _set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) end end def skip_#{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - skip_process_action_callback(:#{filter}, name, options) + _insert_callbacks(names, blk) do |name, options| + _skip_callback(:process_action, :#{filter}, name, options) end end diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract_controller/callbacks_test.rb index 1de60868c3..826e76892e 100644 --- a/actionpack/test/abstract_controller/callbacks_test.rb +++ b/actionpack/test/abstract_controller/callbacks_test.rb @@ -8,7 +8,7 @@ module AbstractController end class Callback1 < ControllerWithCallbacks - process_action_callback :before, :first + _set_callback :process_action, :before, :first def first @text = "Hello world" -- cgit v1.2.3 From 1126a85aed576402d978e6f76eb393b6baaa9541 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 2 Jun 2009 21:41:31 -0700 Subject: Further cleaning up new callbacks --- actionpack/lib/action_controller/abstract/callbacks.rb | 6 +++--- actionpack/test/abstract_controller/callbacks_test.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index dd4213e847..fd7e5ebfda 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -45,19 +45,19 @@ module AbstractController class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| - _set_callback(:process_action, :#{filter}, name, options) + set_callback(:process_action, :#{filter}, name, options) end end def prepend_#{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| - _set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) + set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) end end def skip_#{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| - _skip_callback(:process_action, :#{filter}, name, options) + skip_callback(:process_action, :#{filter}, name, options) end end diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract_controller/callbacks_test.rb index 826e76892e..32450257f0 100644 --- a/actionpack/test/abstract_controller/callbacks_test.rb +++ b/actionpack/test/abstract_controller/callbacks_test.rb @@ -8,7 +8,7 @@ module AbstractController end class Callback1 < ControllerWithCallbacks - _set_callback :process_action, :before, :first + set_callback :process_action, :before, :first def first @text = "Hello world" -- cgit v1.2.3 From 6b4da2ecacc28d7e50aa0e0123b5f4564d6e92c2 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 2 Jun 2009 21:43:54 -0700 Subject: Add explicit requirement in a few cases it was missing. TODO: Come up with the minimal core and remove all of these --- actionpack/lib/action_view/helpers/form_helper.rb | 1 + actionpack/lib/action_view/helpers/form_tag_helper.rb | 1 + actionpack/lib/action_view/helpers/prototype_helper.rb | 1 + 3 files changed, 3 insertions(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index beef661a37..6668482370 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -3,6 +3,7 @@ require 'action_view/helpers/date_helper' require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/hash/slice' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index c96b1fc8d2..e44ad80068 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -1,5 +1,6 @@ require 'cgi' require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/object/returning' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index c0f5df3468..22fcf9d29f 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1,6 +1,7 @@ require 'set' require 'active_support/json' require 'active_support/core_ext/object/extending' +require 'active_support/core_ext/object/returning' module ActionView module Helpers -- cgit v1.2.3 From d6e4113c83a9d55be6f2af247da2cecaa855f43b Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 2 Jun 2009 22:06:21 -0700 Subject: YAGNI Method references. Deprecated --- actionpack/test/controller/filters_test.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index df0a9dbfb0..b930ff4997 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -876,10 +876,12 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert_raise(After) { test_process(controller,'raises_after') } end - def test_with_method - controller = ControllerWithFilterMethod - assert_nothing_raised { test_process(controller,'no_raise') } - assert_raise(After) { test_process(controller,'raises_after') } + for_tag(:old_base) do + def test_with_method + controller = ControllerWithFilterMethod + assert_nothing_raised { test_process(controller,'no_raise') } + assert_raise(After) { test_process(controller,'raises_after') } + end end def test_with_proc -- cgit v1.2.3 From 86fc43fd584726ba81b1a3b3e0a6f6000c1f4cc8 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 8 Jun 2009 13:33:18 -0700 Subject: ActionPack components should no longer have undeclared dependencies. * Tests can be run in isolation * Dependencies added * A few tests modified to avoid depending on AS deps not depended on my files they were testing --- actionpack/Rakefile | 8 +++++ .../lib/action_controller/routing/resources.rb | 4 +-- .../middleware/session/mem_cache_store.rb | 1 + .../action_dispatch/middleware/show_exceptions.rb | 2 ++ .../action_view/helpers/active_record_helper.rb | 2 ++ .../lib/action_view/helpers/benchmark_helper.rb | 2 +- actionpack/lib/action_view/helpers/url_helper.rb | 6 ++-- actionpack/lib/action_view/template/error.rb | 2 ++ actionpack/test/controller/helper_test.rb | 6 ++-- actionpack/test/controller/routing_test.rb | 8 ++--- actionpack/test/dispatch/mime_type_test.rb | 4 +-- .../test/fixtures/test/_local_inspector.html.erb | 2 +- actionpack/test/new_base/abstract_unit.rb | 13 +++++-- actionpack/test/template/erb_util_test.rb | 2 +- actionpack/test/template/number_helper_test.rb | 42 +++++++++++++++------- actionpack/test/template/render_test.rb | 2 +- 16 files changed, 72 insertions(+), 34 deletions(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 5f5614e58f..d6aa0c0b87 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -35,6 +35,14 @@ Rake::TestTask.new(:test_action_pack) do |t| t.verbose = true #t.warning = true end + +task :test_action_pack_isolated do + test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort + test_lib = test_lib_dirs.map { |lib| "-I#{lib}" }.join(' ') + test_files.map! { |f| "ruby #{test_lib} #{f}" } + exec test_files.join(" && ") +end + task :isolated_test do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) Dir.glob("test/{controller,dispatch,template}/**/*_test.rb").all? do |file| diff --git a/actionpack/lib/action_controller/routing/resources.rb b/actionpack/lib/action_controller/routing/resources.rb index 05c782d226..2dee0a3d87 100644 --- a/actionpack/lib/action_controller/routing/resources.rb +++ b/actionpack/lib/action_controller/routing/resources.rb @@ -150,9 +150,9 @@ module ActionController end if only - @allowed_actions[:only] = Array(only).map(&:to_sym) + @allowed_actions[:only] = Array(only).map {|a| a.to_sym } elsif except - @allowed_actions[:except] = Array(except).map(&:to_sym) + @allowed_actions[:except] = Array(except).map {|a| a.to_sym } end end diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index 8f448970d9..1d9efc2b36 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -1,3 +1,4 @@ +require "active_support/core_ext/kernel/requires" begin require_library_or_gem 'memcache' diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index bfff307669..036deec6d2 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/exception" + module ActionDispatch class ShowExceptions include StatusCodes diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index b4b9f6e34b..8b70200654 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -1,6 +1,8 @@ require 'cgi' require 'action_view/helpers/form_helper' require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/kernel/reporting' module ActionView class Base diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb index 61c2cecb04..c13a069a35 100644 --- a/actionpack/lib/action_view/helpers/benchmark_helper.rb +++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb @@ -1,4 +1,4 @@ -require 'benchmark' +require 'active_support/core_ext/benchmark' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 36e0a78e93..a861102166 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -221,9 +221,9 @@ module ActionView html_options = args.second concat(link_to(capture(&block), options, html_options)) else - name = args.first - options = args.second || {} - html_options = args.third + name = args[0] + options = args[1] || {} + html_options = args[2] url = url_for(options) diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index a06e80b294..6e5093c5bd 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/enumerable" + module ActionView # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a # bunch of intimate details and uses it to report a very precise exception message. diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 5b9feb3630..342cbfbcd3 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -127,7 +127,7 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = AllHelpersController.master_helper_module.instance_methods.map(&:to_s) + methods = AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} # abc_helper.rb assert methods.include?('bare_a') @@ -171,11 +171,11 @@ class HelperTest < Test::Unit::TestCase private def expected_helper_methods - TestHelper.instance_methods.map(&:to_s) + TestHelper.instance_methods.map {|m| m.to_s } end def master_helper_methods - @controller_class.master_helper_module.instance_methods.map(&:to_s) + @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } end def missing_methods diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 6b08a04b10..c2acc03a1b 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1623,13 +1623,13 @@ class RouteSetTest < Test::Unit::TestCase set.draw { |m| m.connect ':controller/:action/:id' } path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map(&:to_s).sort + assert_equal %w(that this), extras.map { |e| e.to_s }.sort end def test_extra_keys set.draw { |m| m.connect ':controller/:action/:id' } extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map(&:to_s).sort + assert_equal %w(that this), extras.map { |e| e.to_s }.sort end def test_generate_extras_not_first @@ -1639,7 +1639,7 @@ class RouteSetTest < Test::Unit::TestCase end path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map(&:to_s).sort + assert_equal %w(that this), extras.map { |e| e.to_s }.sort end def test_generate_not_first @@ -1656,7 +1656,7 @@ class RouteSetTest < Test::Unit::TestCase map.connect ':controller/:action/:id' end extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map(&:to_s).sort + assert_equal %w(that this), extras.map { |e| e.to_s }.sort end def test_draw diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 2fdf4819bb..27bdd10ee5 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -56,7 +56,7 @@ class MimeTypeTest < ActiveSupport::TestCase 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] + types = Mime::SET.to_a.map{|m| m.to_sym }.uniq - [:all] # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) } @@ -76,7 +76,7 @@ class MimeTypeTest < ActiveSupport::TestCase end test "verifiable mime types" do - all_types = Mime::SET.to_a.map(&:to_sym) + all_types = Mime::SET.to_a.map{|m| m.to_sym} all_types.uniq! # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE all_types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) } diff --git a/actionpack/test/fixtures/test/_local_inspector.html.erb b/actionpack/test/fixtures/test/_local_inspector.html.erb index c5a6e3e5bc..e6765c0882 100644 --- a/actionpack/test/fixtures/test/_local_inspector.html.erb +++ b/actionpack/test/fixtures/test/_local_inspector.html.erb @@ -1 +1 @@ -<%= local_assigns.keys.map(&:to_s).sort.join(",") -%> \ No newline at end of file +<%= local_assigns.keys.map {|k| k.to_s }.sort.join(",") -%> \ No newline at end of file diff --git a/actionpack/test/new_base/abstract_unit.rb b/actionpack/test/new_base/abstract_unit.rb index e6690d41d9..7d72f8393b 100644 --- a/actionpack/test/new_base/abstract_unit.rb +++ b/actionpack/test/new_base/abstract_unit.rb @@ -11,9 +11,6 @@ $stderr.puts "Running old tests on new_base" require 'test/unit' require 'active_support' -# TODO : Revisit requiring all the core extensions here -require 'active_support/core_ext' - require 'active_support/test_case' require 'action_controller/abstract' require 'action_controller/new_base' @@ -45,6 +42,16 @@ ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), '../fixtures') +module ActionView + class TestCase + setup do + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + end + end + end +end + module ActionController Base.session = { :key => '_testing_session', diff --git a/actionpack/test/template/erb_util_test.rb b/actionpack/test/template/erb_util_test.rb index c8c986f218..49f51c50c5 100644 --- a/actionpack/test/template/erb_util_test.rb +++ b/actionpack/test/template/erb_util_test.rb @@ -16,7 +16,7 @@ class ErbUtilTest < Test::Unit::TestCase end def test_rest_in_ascii - (0..127).to_a.map(&:chr).each do |chr| + (0..127).to_a.map {|int| int.chr }.each do |chr| next if %w(& " < >).include?(chr) assert_equal chr, html_escape(chr) end diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index b6542ef29d..57b740032e 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -3,6 +3,22 @@ require 'abstract_unit' class NumberHelperTest < ActionView::TestCase tests ActionView::Helpers::NumberHelper + def kilobytes(number) + number * 1024 + end + + def megabytes(number) + kilobytes(number) * 1024 + end + + def gigabytes(number) + megabytes(number) * 1024 + end + + def terabytes(number) + gigabytes(number) * 1024 + end + def test_number_to_phone assert_equal("555-1234", number_to_phone(5551234)) assert_equal("800-555-1212", number_to_phone(8005551212)) @@ -96,16 +112,16 @@ class NumberHelperTest < ActionView::TestCase assert_equal '1.2 MB', number_to_human_size(1234567) assert_equal '1.1 GB', number_to_human_size(1234567890) assert_equal '1.1 TB', number_to_human_size(1234567890123) - assert_equal '1025 TB', number_to_human_size(1025.terabytes) - assert_equal '444 KB', number_to_human_size(444.kilobytes) - assert_equal '1023 MB', number_to_human_size(1023.megabytes) - assert_equal '3 TB', number_to_human_size(3.terabytes) + assert_equal '1025 TB', number_to_human_size(terabytes(1025)) + assert_equal '444 KB', number_to_human_size(kilobytes(444)) + assert_equal '1023 MB', number_to_human_size(megabytes(1023)) + assert_equal '3 TB', number_to_human_size(terabytes(3)) assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2) assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4) assert_equal("123 Bytes", number_to_human_size("123")) - assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2) - assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) - assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) + assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2) + assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4) + assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4) assert_equal '1 Byte', number_to_human_size(1.1) assert_equal '10 Bytes', number_to_human_size(10) #assert_nil number_to_human_size('x') # fails due to API consolidation @@ -115,9 +131,9 @@ class NumberHelperTest < ActionView::TestCase def test_number_to_human_size_with_options_hash assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2) assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4) - assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2) - assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) - assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) + assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2) + assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4) + assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4) assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 0) assert_equal '500 MB', number_to_human_size(524288000, :precision=>0) assert_equal '40 KB', number_to_human_size(41010, :precision => 0) @@ -125,8 +141,8 @@ class NumberHelperTest < ActionView::TestCase end def test_number_to_human_size_with_custom_delimiter_and_separator - assert_equal '1,01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2, :separator => ',') - assert_equal '1,01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4, :separator => ',') - assert_equal '1.000,1 TB', number_to_human_size(1000.1.terabytes, :delimiter => '.', :separator => ',') + assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :separator => ',') + assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4, :separator => ',') + assert_equal '1.000,1 TB', number_to_human_size(terabytes(1000.1), :delimiter => '.', :separator => ',') end end diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 20cd4cc1d4..2ed11aa3c0 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -13,7 +13,7 @@ module RenderTestCases I18n.backend.store_translations 'pt-BR', {} # Ensure original are still the same since we are reindexing view paths - assert_equal ORIGINAL_LOCALES, I18n.available_locales.map(&:to_s).sort + assert_equal ORIGINAL_LOCALES, I18n.available_locales.map {|l| l.to_s }.sort end def test_render_file -- cgit v1.2.3 From a6bf6f275c4fb25e5521faa148d2d2ea63a064b2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 14:08:04 -0700 Subject: Use as_json instead of rails_to_json --- actionpack/lib/action_view/helpers/prototype_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 22fcf9d29f..4c2fcb3bd0 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1176,7 +1176,7 @@ module ActionView class JavaScriptVariableProxy < JavaScriptProxy #:nodoc: def initialize(generator, variable) - @variable = variable + @variable = ActiveSupport::JSON::Variable.new(variable) @empty = true # only record lines if we have to. gets rid of unnecessary linebreaks super(generator) end @@ -1187,7 +1187,7 @@ module ActionView true end - def rails_to_json(*) + def as_json(options = nil) @variable end -- cgit v1.2.3 From c1fcabb3505dd1595142e394d2dca71d62acf77c Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 8 Jun 2009 14:26:22 -0700 Subject: Remove duplicate --- actionpack/Rakefile | 7 ------- 1 file changed, 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index d6aa0c0b87..074bc90ca9 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -36,13 +36,6 @@ Rake::TestTask.new(:test_action_pack) do |t| #t.warning = true end -task :test_action_pack_isolated do - test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort - test_lib = test_lib_dirs.map { |lib| "-I#{lib}" }.join(' ') - test_files.map! { |f| "ruby #{test_lib} #{f}" } - exec test_files.join(" && ") -end - task :isolated_test do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) Dir.glob("test/{controller,dispatch,template}/**/*_test.rb").all? do |file| -- cgit v1.2.3 From d3078b540ed154b6ea245de40e8de8bba642cd02 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 15:04:26 -0700 Subject: Explicitly qualify constants in BasicObjects --- actionpack/lib/action_view/helpers/prototype_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 4c2fcb3bd0..624b537ad2 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1176,7 +1176,7 @@ module ActionView class JavaScriptVariableProxy < JavaScriptProxy #:nodoc: def initialize(generator, variable) - @variable = ActiveSupport::JSON::Variable.new(variable) + @variable = ::ActiveSupport::JSON::Variable.new(variable) @empty = true # only record lines if we have to. gets rid of unnecessary linebreaks super(generator) end -- cgit v1.2.3 From a470bb36128cfdddd888492b7ac972ee582f6acb Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 8 Jun 2009 14:28:36 -0700 Subject: Remove extraneous response_obj --- actionpack/lib/action_controller/abstract/base.rb | 5 ----- 1 file changed, 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index 87083a4d79..306f3d2ccb 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -13,7 +13,6 @@ module AbstractController class Base attr_internal :response_body - attr_internal :response_obj attr_internal :action_name class << self @@ -63,10 +62,6 @@ module AbstractController abstract! - def initialize - self.response_obj = {} - end - def process(action) @_action_name = action_name = action.to_s -- cgit v1.2.3 From 38b608ecab2441cd0c4e75bc08bdf57fcf85dd71 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 8 Jun 2009 16:14:38 -0700 Subject: Writing comments to AbstractController --- actionpack/lib/action_controller/abstract/base.rb | 105 +++++++++++++++++---- .../lib/action_controller/abstract/benchmarker.rb | 10 ++ .../lib/action_controller/abstract/callbacks.rb | 60 ++++++++++-- actionpack/lib/action_controller/new_base/base.rb | 2 +- .../abstract_controller_test.rb | 24 ++--- .../test/abstract_controller/callbacks_test.rb | 30 +++--- actionpack/test/abstract_controller/helper_test.rb | 2 +- .../test/abstract_controller/layouts_test.rb | 30 +++--- 8 files changed, 191 insertions(+), 72 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index 306f3d2ccb..d2cf4b1aad 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -17,36 +17,58 @@ module AbstractController class << self attr_reader :abstract + alias_method :abstract?, :abstract + # Define a controller as abstract. See internal_methods for more + # details. def abstract! @abstract = true end - alias_method :abstract?, :abstract - def inherited(klass) - ::AbstractController::Base.subclasses << klass.to_s + ::AbstractController::Base.descendants << klass.to_s super end - def subclasses - @subclasses ||= [] + # A list of all descendents of AbstractController::Base. This is + # useful for initializers which need to add behavior to all controllers. + def descendants + @descendants ||= [] end + # A list of all internal methods for a controller. This finds the first + # abstract superclass of a controller, and gets a list of all public + # instance methods on that abstract class. Public instance methods of + # a controller would normally be considered action methods, so we + # are removing those methods on classes declared as abstract + # (ActionController::Http and ActionController::Base are defined + # as abstract) def internal_methods controller = self controller = controller.superclass until controller.abstract? controller.public_instance_methods(true) end - def process(action) - new.process(action.to_s) - end - + # The list of hidden actions to an empty Array. Defaults to an + # empty Array. This can be modified by other modules or subclasses + # to specify particular actions as hidden. + # + # ==== Returns + # Array[String]:: An array of method names that should not be + # considered actions. def hidden_actions [] end + # A list of method names that should be considered actions. This + # includes all public instance methods on a controller, less + # any internal methods (see #internal_methods), adding back in + # any methods that are internal, but still exist on the class + # itself. Finally, #hidden_actions are removed. + # + # ==== Returns + # Array[String]:: A list of all methods that should be considered + # actions. def action_methods @action_methods ||= # All public instance methods of this class, including ancestors @@ -62,6 +84,14 @@ module AbstractController abstract! + # Calls the action going through the entire action dispatch stack. + # + # The actual method that is called is determined by calling + # #method_for_action. If no method can handle the action, then an + # ActionNotFound error is raised. + # + # ==== Returns + # self def process(action) @_action_name = action_name = action.to_s @@ -74,33 +104,68 @@ module AbstractController end private + # See AbstractController::Base.action_methods def action_methods self.class.action_methods end - def action_method?(action) - action_methods.include?(action) + # Returns true if the name can be considered an action. This can + # be overridden in subclasses to modify the semantics of what + # can be considered an action. + # + # ==== Parameters + # name:: The name of an action to be tested + # + # ==== Returns + # TrueClass, FalseClass + def action_method?(name) + action_methods.include?(name) end - # It is possible for respond_to?(action_name) to be false and - # respond_to?(:action_missing) to be false if respond_to_action? - # is overridden in a subclass. For instance, ActionController::Base - # overrides it to include the case where a template matching the - # action_name is found. + # Call the action. Override this in a subclass to modify the + # behavior around processing an action. This, and not #process, + # is the intended way to override action dispatching. def process_action(method_name) send_action(method_name) end + # Actually call the method associated with the action. Override + # this method if you wish to change how action methods are called, + # not to add additional behavior around it. For example, you would + # override #send_action if you want to inject arguments into the + # method. alias send_action send + # If the action name was not found, but a method called "action_missing" + # was found, #method_for_action will return "_handle_action_missing". + # This method calls #action_missing with the current action name. def _handle_action_missing action_missing(@_action_name) end - # Override this to change the conditions that will raise an - # ActionNotFound error. If you accept a difference case, - # you must handle it by also overriding process_action and - # handling the case. + # Takes an action name and returns the name of the method that will + # handle the action. In normal cases, this method returns the same + # name as it receives. By default, if #method_for_action receives + # a name that is not an action, it will look for an #action_missing + # method and return "_handle_action_missing" if one is found. + # + # Subclasses may override this method to add additional conditions + # that should be considered an action. For instance, an HTTP controller + # with a template matching the action name is considered to exist. + # + # If you override this method to handle additional cases, you may + # also provide a method (like _handle_method_missing) to handle + # the case. + # + # If none of these conditions are true, and method_for_action + # returns nil, an ActionNotFound exception will be raised. + # + # ==== Parameters + # action_name:: An action name to find a method name for + # + # ==== Returns + # String:: The name of the method that handles the action + # nil:: No method name could be found. Raise ActionNotFound. def method_for_action(action_name) if action_method?(action_name) then action_name elsif respond_to?(:action_missing, true) then "_handle_action_missing" diff --git a/actionpack/lib/action_controller/abstract/benchmarker.rb b/actionpack/lib/action_controller/abstract/benchmarker.rb index 07294cede3..58e9564c2f 100644 --- a/actionpack/lib/action_controller/abstract/benchmarker.rb +++ b/actionpack/lib/action_controller/abstract/benchmarker.rb @@ -5,6 +5,16 @@ module AbstractController include Logger module ClassMethods + # Execute the passed in block, timing the duration of the block in ms. + # + # ==== Parameters + # title<#to_s>:: The title of block to benchmark + # log_level:: A valid log level. Defaults to Logger::DEBUG + # use_silence:: Whether or not to silence the + # logger for the duration of the block. + # + # ==== Returns + # Object:: The result of the block def benchmark(title, log_level = ::Logger::DEBUG, use_silence = true) if logger && logger.level >= log_level result = nil diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index fd7e5ebfda..0d5161c80e 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -2,12 +2,17 @@ module AbstractController module Callbacks extend ActiveSupport::Concern + # Uses ActiveSupport::NewCallbacks as the base functionality. For + # more details on the whole callback system, read the documentation + # for ActiveSupport::NewCallbacks. include ActiveSupport::NewCallbacks included do define_callbacks :process_action, "response_body" end + # Override AbstractController::Base's process_action to run the + # process_action callbacks around the normal behavior. def process_action(method_name) _run_process_action_callbacks(method_name) do super @@ -15,6 +20,17 @@ module AbstractController end module ClassMethods + # If :only or :accept are used, convert the options into the + # primitive form (:per_key) used by ActiveSupport::Callbacks. + # The basic idea is that :only => :index gets converted to + # :if => proc {|c| c.action_name == "index" }, but that the + # proc is only evaluated once per action for the lifetime of + # a Rails process. + # + # ==== Options + # :only<#to_s>:: The callback should be run only for this action + # :except<#to_s>:: The callback should be run for all actions + # except this action def _normalize_callback_options(options) if only = options[:only] only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ") @@ -26,41 +42,69 @@ module AbstractController end end + # Skip before, after, and around filters matching any of the names + # + # ==== Parameters + # *names:: A list of valid names that could be used for + # callbacks. Note that skipping uses Ruby equality, so it's + # impossible to skip a callback defined using an anonymous proc + # using #skip_filter def skip_filter(*names, &blk) - skip_before_filter(*names, &blk) - skip_after_filter(*names, &blk) - skip_around_filter(*names, &blk) + skip_before_filter(*names) + skip_after_filter(*names) + skip_around_filter(*names) end - def _insert_callbacks(names, block) - options = names.last.is_a?(Hash) ? names.pop : {} + # Take callback names and an optional callback proc, normalize them, + # then call the block with each callback. This allows us to abstract + # the normalization across several methods that use it. + # + # ==== Parameters + # callbacks:: A list of callbacks, with an optional + # options hash as the last parameter. + # block:: A proc that should be added to the callbacks. + # + # ==== Block Parameters + # name:: The callback to be added + # options:: A list of options to be used when adding the callback + def _insert_callbacks(callbacks, block) + options = callbacks.last.is_a?(Hash) ? callbacks.pop : {} _normalize_callback_options(options) - names.push(block) if block - names.each do |name| - yield name, options + callbacks.push(block) if block + callbacks.each do |callback| + yield callback, options end end + # set up before_filter, prepend_before_filter, skip_before_filter, etc. + # for each of before, after, and around. [:before, :after, :around].each do |filter| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + # Append a before, after or around filter. See _insert_callbacks + # for details on the allowed parameters. def #{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| set_callback(:process_action, :#{filter}, name, options) end end + # Prepend a before, after or around filter. See _insert_callbacks + # for details on the allowed parameters. def prepend_#{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) end end + # Skip a before, after or around filter. See _insert_callbacks + # for details on the allowed parameters. def skip_#{filter}_filter(*names, &blk) _insert_callbacks(names, blk) do |name, options| skip_callback(:process_action, :#{filter}, name, options) end end + # *_filter is the same as append_*_filter alias_method :append_#{filter}_filter, :#{filter}_filter RUBY_EVAL end diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb index d7b65d37fa..e8fc153578 100644 --- a/actionpack/lib/action_controller/new_base/base.rb +++ b/actionpack/lib/action_controller/new_base/base.rb @@ -39,7 +39,7 @@ module ActionController # TODO: Extract into its own module # This should be moved together with other normalizing behavior module ImplicitRender - def send_action(method_name) + def send_action(*) ret = super default_render unless performed? ret diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index 9c028e7d1e..c7eaaeb4ba 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -19,7 +19,7 @@ module AbstractController class TestBasic < ActiveSupport::TestCase test "dispatching works" do - result = Me.process(:index) + result = Me.new.process(:index) assert_equal "Hello world", result.response_body end end @@ -68,27 +68,27 @@ module AbstractController class TestRenderer < ActiveSupport::TestCase test "rendering templates works" do - result = Me2.process(:index) + result = Me2.new.process(:index) assert_equal "Hello from index.erb", result.response_body end test "rendering passes ivars to the view" do - result = Me2.process(:action_with_ivars) + result = Me2.new.process(:action_with_ivars) assert_equal "Hello from index_with_ivars.erb", result.response_body end test "rendering with no template name" do - result = Me2.process(:naked_render) + result = Me2.new.process(:naked_render) assert_equal "Hello from naked_render.erb", result.response_body end test "rendering to a rack body" do - result = Me2.process(:rendering_to_body) + result = Me2.new.process(:rendering_to_body) assert_equal "Hello from naked_render.erb", result.response_body end test "rendering to a string" do - result = Me2.process(:rendering_to_string) + result = Me2.new.process(:rendering_to_string) assert_equal "Hello from naked_render.erb", result.response_body end end @@ -120,12 +120,12 @@ module AbstractController class TestPrefixedViews < ActiveSupport::TestCase test "templates are located inside their 'prefix' folder" do - result = Me3.process(:index) + result = Me3.new.process(:index) assert_equal "Hello from me3/index.erb", result.response_body end test "templates included their format" do - result = Me3.process(:formatted) + result = Me3.new.process(:formatted) assert_equal "Hello from me3/formatted.html.erb", result.response_body end end @@ -173,7 +173,7 @@ module AbstractController class TestLayouts < ActiveSupport::TestCase test "layouts are included" do - result = Me4.process(:index) + result = Me4.new.process(:index) assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", result.response_body end end @@ -210,7 +210,7 @@ module AbstractController class TestRespondToAction < ActiveSupport::TestCase def assert_dispatch(klass, body = "success", action = :index) - response = klass.process(action).response_body + response = klass.new.process(action).response_body assert_equal body, response end @@ -219,7 +219,7 @@ module AbstractController end test "raises ActionNotFound when method does not exist and action_missing is not defined" do - assert_raise(ActionNotFound) { DefaultRespondToActionController.process(:fail) } + assert_raise(ActionNotFound) { DefaultRespondToActionController.new.process(:fail) } end test "dispatches to action_missing when method does not exist and action_missing is defined" do @@ -231,7 +231,7 @@ module AbstractController end test "raises ActionNotFound if method is defined but respond_to_action? returns false" do - assert_raise(ActionNotFound) { RespondToActionController.process(:fail) } + assert_raise(ActionNotFound) { RespondToActionController.new.process(:fail) } end end diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract_controller/callbacks_test.rb index 32450257f0..817f60f7d1 100644 --- a/actionpack/test/abstract_controller/callbacks_test.rb +++ b/actionpack/test/abstract_controller/callbacks_test.rb @@ -21,7 +21,7 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "basic callbacks work" do - result = Callback1.process(:index) + result = Callback1.new.process(:index) assert_equal "Hello world", result.response_body end end @@ -52,17 +52,17 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "before_filter works" do - result = Callback2.process(:index) + result = Callback2.new.process(:index) assert_equal "Hello world", result.response_body end test "after_filter works" do - result = Callback2.process(:index) + result = Callback2.new.process(:index) assert_equal "Goodbye", result.instance_variable_get("@second") end test "around_filter works" do - result = Callback2.process(:index) + result = Callback2.new.process(:index) assert_equal "FIRSTSECOND", result.instance_variable_get("@aroundz") end end @@ -83,12 +83,12 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "before_filter works with procs" do - result = Callback3.process(:index) + result = Callback3.new.process(:index) assert_equal "Hello world", result.response_body end test "after_filter works with procs" do - result = Callback3.process(:index) + result = Callback3.new.process(:index) assert_equal "Goodbye", result.instance_variable_get("@second") end end @@ -118,17 +118,17 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "when :only is specified, a before filter is triggered on that action" do - result = CallbacksWithConditions.process(:index) + result = CallbacksWithConditions.new.process(:index) assert_equal "Hello, World", result.response_body end test "when :only is specified, a before filter is not triggered on other actions" do - result = CallbacksWithConditions.process(:sekrit_data) + result = CallbacksWithConditions.new.process(:sekrit_data) assert_equal "true", result.response_body end test "when :except is specified, an after filter is not triggered on that action" do - result = CallbacksWithConditions.process(:index) + result = CallbacksWithConditions.new.process(:index) assert_nil result.instance_variable_get("@authenticated") end end @@ -158,17 +158,17 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "when :only is specified with an array, a before filter is triggered on that action" do - result = CallbacksWithArrayConditions.process(:index) + result = CallbacksWithArrayConditions.new.process(:index) assert_equal "Hello, World", result.response_body end test "when :only is specified with an array, a before filter is not triggered on other actions" do - result = CallbacksWithArrayConditions.process(:sekrit_data) + result = CallbacksWithArrayConditions.new.process(:sekrit_data) assert_equal "true", result.response_body end test "when :except is specified with an array, an after filter is not triggered on that action" do - result = CallbacksWithArrayConditions.process(:index) + result = CallbacksWithArrayConditions.new.process(:index) assert_nil result.instance_variable_get("@authenticated") end end @@ -183,12 +183,12 @@ module AbstractController class TestCallbacks < ActiveSupport::TestCase test "when a callback is modified in a child with :only, it works for the :only action" do - result = ChangedConditions.process(:index) + result = ChangedConditions.new.process(:index) assert_equal "Hello world", result.response_body end test "when a callback is modified in a child with :only, it does not work for other actions" do - result = ChangedConditions.process(:not_index) + result = ChangedConditions.new.process(:not_index) assert_equal "", result.response_body end end @@ -207,7 +207,7 @@ module AbstractController class TestHalting < ActiveSupport::TestCase test "when a callback sets the response body, the action should not be invoked" do - result = SetsResponseBody.process(:index) + result = SetsResponseBody.new.process(:index) assert_equal "Success", result.response_body end end diff --git a/actionpack/test/abstract_controller/helper_test.rb b/actionpack/test/abstract_controller/helper_test.rb index f91aefe606..0a2535f834 100644 --- a/actionpack/test/abstract_controller/helper_test.rb +++ b/actionpack/test/abstract_controller/helper_test.rb @@ -34,7 +34,7 @@ module AbstractController class TestHelpers < ActiveSupport::TestCase def test_helpers - result = MyHelpers1.process(:index) + result = MyHelpers1.new.process(:index) assert_equal "Hello World : Included", result.response_body end end diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index d3440c3de0..4b66f063f3 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -142,7 +142,7 @@ module AbstractControllerTests end # TODO Move to bootloader - AbstractController::Base.subclasses.each do |klass| + AbstractController::Base.descendants.each do |klass| klass = klass.constantize next unless klass < AbstractController::Layouts klass.class_eval do @@ -152,70 +152,70 @@ module AbstractControllerTests class TestBase < ActiveSupport::TestCase test "when no layout is specified, and no default is available, render without a layout" do - result = Blank.process(:index) + result = Blank.new.process(:index) assert_equal "Hello blank!", result.response_body end test "when layout is specified as a string, render with that layout" do - result = WithString.process(:index) + result = WithString.new.process(:index) assert_equal "With String Hello string!", result.response_body end test "when layout is specified as a string, but the layout is missing, raise an exception" do - assert_raises(ActionView::MissingTemplate) { WithMissingLayout.process(:index) } + assert_raises(ActionView::MissingTemplate) { WithMissingLayout.new.process(:index) } end test "when layout is specified as false, do not use a layout" do - result = WithFalseLayout.process(:index) + result = WithFalseLayout.new.process(:index) assert_equal "Hello false!", result.response_body end test "when layout is specified as nil, do not use a layout" do - result = WithNilLayout.process(:index) + result = WithNilLayout.new.process(:index) assert_equal "Hello nil!", result.response_body end test "when layout is specified as a symbol, call the requested method and use the layout returned" do - result = WithSymbol.process(:index) + result = WithSymbol.new.process(:index) assert_equal "OMGHI2U Hello symbol!", result.response_body end test "when layout is specified as a symbol and the method returns nil, don't use a layout" do - result = WithSymbolReturningNil.process(:index) + result = WithSymbolReturningNil.new.process(:index) assert_equal "Hello nilz!", result.response_body end test "when the layout is specified as a symbol and the method doesn't exist, raise an exception" do - assert_raises(NoMethodError, /:nilz/) { WithSymbolAndNoMethod.process(:index) } + assert_raises(NoMethodError, /:nilz/) { WithSymbolAndNoMethod.new.process(:index) } end test "when the layout is specified as a symbol and the method returns something besides a string/false/nil, raise an exception" do - assert_raises(ArgumentError) { WithSymbolReturningObj.process(:index) } + assert_raises(ArgumentError) { WithSymbolReturningObj.new.process(:index) } end test "when a child controller does not have a layout, use the parent controller layout" do - result = WithStringChild.process(:index) + result = WithStringChild.new.process(:index) assert_equal "With String Hello string!", result.response_body end test "when a child controller has specified a layout, use that layout and not the parent controller layout" do - result = WithStringOverriddenChild.process(:index) + result = WithStringOverriddenChild.new.process(:index) assert_equal "With Override Hello string!", result.response_body end test "when a child controller has an implied layout, use that layout and not the parent controller layout" do - result = WithStringImpliedChild.process(:index) + result = WithStringImpliedChild.new.process(:index) assert_equal "With Implied Hello string!", result.response_body end test "when a child controller specifies layout nil, do not use the parent layout" do - result = WithNilChild.process(:index) + result = WithNilChild.new.process(:index) assert_equal "Hello string!", result.response_body end test "when a grandchild has no layout specified, the child has an implied layout, and the " \ "parent has specified a layout, use the child controller layout" do - result = WithChildOfImplied.process(:index) + result = WithChildOfImplied.new.process(:index) assert_equal "With Implied Hello string!", result.response_body end -- cgit v1.2.3 From be371993b7c0bb5110a38d4c3bade116b66a904a Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 8 Jun 2009 17:41:08 -0700 Subject: More documentation work on AbstractController This is still not completely done and there might be pointers to things that don't exist yet. --- actionpack/lib/action_controller/abstract.rb | 18 +++--- actionpack/lib/action_controller/abstract/base.rb | 9 --- .../lib/action_controller/abstract/exceptions.rb | 9 +++ .../lib/action_controller/abstract/renderer.rb | 69 +++++++++++++++++++--- 4 files changed, 80 insertions(+), 25 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract.rb b/actionpack/lib/action_controller/abstract.rb index f46b91627f..d0eba253b8 100644 --- a/actionpack/lib/action_controller/abstract.rb +++ b/actionpack/lib/action_controller/abstract.rb @@ -2,13 +2,15 @@ require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/delegation" module AbstractController - autoload :Base, "action_controller/abstract/base" - autoload :Benchmarker, "action_controller/abstract/benchmarker" - autoload :Callbacks, "action_controller/abstract/callbacks" - autoload :Helpers, "action_controller/abstract/helpers" - autoload :Layouts, "action_controller/abstract/layouts" - autoload :Logger, "action_controller/abstract/logger" - autoload :Renderer, "action_controller/abstract/renderer" + autoload :Base, "action_controller/abstract/base" + autoload :Benchmarker, "action_controller/abstract/benchmarker" + autoload :Callbacks, "action_controller/abstract/callbacks" + autoload :Helpers, "action_controller/abstract/helpers" + autoload :Layouts, "action_controller/abstract/layouts" + autoload :Logger, "action_controller/abstract/logger" + autoload :Renderer, "action_controller/abstract/renderer" # === Exceptions - autoload :ActionNotFound, "action_controller/abstract/exceptions" + autoload :ActionNotFound, "action_controller/abstract/exceptions" + autoload :DoubleRenderError, "action_controller/abstract/exceptions" + autoload :Error, "action_controller/abstract/exceptions" end diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index d2cf4b1aad..c3daef8759 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -1,15 +1,6 @@ require 'active_support/core_ext/module/attr_internal' module AbstractController - class Error < StandardError; end - - class DoubleRenderError < Error - 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 Base attr_internal :response_body diff --git a/actionpack/lib/action_controller/abstract/exceptions.rb b/actionpack/lib/action_controller/abstract/exceptions.rb index 2f6c55f068..b671516de1 100644 --- a/actionpack/lib/action_controller/abstract/exceptions.rb +++ b/actionpack/lib/action_controller/abstract/exceptions.rb @@ -1,3 +1,12 @@ module AbstractController + class Error < StandardError; end class ActionNotFound < StandardError; end + + class DoubleRenderError < Error + 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 end diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index dd58c7cb64..28bdda4a75 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -14,10 +14,29 @@ module AbstractController self._view_paths ||= ActionView::PathSet.new end + # An instance of a view class. The default view class is ActionView::Base + # + # The view class must have the following methods: + # initialize[paths, assigns_for_first_render, controller] + # paths:: A list of resolvers to look for templates in + # controller A controller + # _render_partial_from_controller[options] + # options:: see _render_partial in ActionView::Base + # _render_template_from_controller[template, layout, options, partial] + # template:: The template to render + # layout:: The layout to render around the template + # options:: See _render_template_with_layout in ActionView::Base + # partial:: Whether or not the template to render is a partial + # _partial:: If a partial, rather than a template, was rendered, return + # the partial. + # + # Override this method in a to change the default behavior. def _action_view @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self) end + # Mostly abstracts the fact that calling render twice is a DoubleRenderError. + # Delegates render_to_body and sticks the result in self.response_body. def render(*args) if response_body raise AbstractController::DoubleRenderError, "OMG" @@ -27,9 +46,10 @@ module AbstractController end # Raw rendering of a template to a Rack-compatible body. - # ==== - # @option _prefix The template's path prefix - # @option _layout The relative path to the layout template to use + # + # ==== Options + # _partial_object:: The object that is being rendered. If this + # exists, we are in the special case of rendering an object as a partial. # # :api: plugin def render_to_body(options = {}) @@ -42,21 +62,27 @@ module AbstractController end end - # Raw rendering of a template to a string. - # ==== - # @option _prefix The template's path prefix - # @option _layout The relative path to the layout template to use + # Raw rendering of a template to a string. Just convert the results of + # render_to_body into a String. # # :api: plugin def render_to_string(options = {}) AbstractController::Renderer.body_to_s(render_to_body(options)) end + # Renders the template from an object. + # + # ==== Options + # _template:: The template to render + # _layout:: The layout to wrap the template in (optional) + # _partial:: Whether or not the template to be rendered is a partial def _render_template(options) _action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial]) end - def view_paths() + # The list of view paths for this controller. See ActionView::ViewPathSet for + # more details about writing custom view paths. + def view_paths _view_paths end @@ -73,6 +99,15 @@ module AbstractController end private + # Take in a set of options and determine the template to render + # + # ==== Options + # _template:: If this is provided, the search is over + # _template_name<#to_s>:: The name of the template to look up. Otherwise, + # use the current action name. + # _prefix:: The prefix to look inside of. In a file system, this corresponds + # to a directory. + # _partial:: Whether or not the file to look up is a partial def _determine_template(options) name = (options[:_template_name] || action_name).to_s @@ -82,18 +117,36 @@ module AbstractController end module ClassMethods + # Append a path to the list of view paths for this controller. + # + # ==== Parameters + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path + # (see ActionView::ViewPathSet for more information) def append_view_path(path) self.view_paths << path end + # Prepend a path to the list of view paths for this controller. + # + # ==== Parameters + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path + # (see ActionView::ViewPathSet for more information) def prepend_view_path(path) self.view_paths.unshift(path) end + # A list of all of the default view paths for this controller. def view_paths self._view_paths end + # Set the view paths. + # + # ==== Parameters + # paths:: If a ViewPathSet is provided, use that; + # otherwise, process the parameter into a ViewPathSet. def view_paths=(paths) self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths) -- cgit v1.2.3 From 23f3b567e2983a295937b40e142f965188f7c83c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:35:30 -0700 Subject: Uses Hash#symbolize_keys --- actionpack/lib/action_view/helpers/url_helper.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index a861102166..de864f453c 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/javascript_helper' +require 'active_support/core_ext/hash/keys' module ActionView module Helpers #:nodoc: -- cgit v1.2.3 From f68cc639f57a9fc261a2e432d1fdd749146d689d Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 26 May 2009 15:06:09 -0500 Subject: A test to show that http_authentication needs to fail authentication if the password procedure returns nil. Also includes a fix to validate_digest_response to fail validation if the password procedure returns nil. Signed-off-by: Michael Koziarski --- .../lib/action_controller/base/http_authentication.rb | 4 +++- .../test/controller/http_digest_authentication_test.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb index 2893290efb..2519f55269 100644 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ b/actionpack/lib/action_controller/base/http_authentication.rb @@ -185,7 +185,7 @@ module ActionController request.env['REDIRECT_X_HTTP_AUTHORIZATION'] end - # Raises error unless the request credentials response value matches the expected value. + # Returns false unless the request credentials response value matches the expected value. # First try the password as a ha1 digest password. If this fails, then try it as a plain # text password. def validate_digest_response(request, realm, &password_procedure) @@ -194,6 +194,8 @@ module ActionController if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] password = password_procedure.call(credentials[:username]) + return false unless password + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] [true, false].any? do |password_is_ha1| diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 15a11395bb..58f3b88075 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -76,6 +76,15 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'SuperSecret', credentials[:realm] end + test "authentication request with nil credentials" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil) + get :index + + assert_response :unauthorized + assert_equal "HTTP Digest: Access denied.\n", @response.body, "Authentication didn't fail for request" + assert_not_equal 'Hello Secret', @response.body, "Authentication didn't fail for request" + end + test "authentication request with invalid password" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'foo') get :display @@ -168,6 +177,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "validate_digest_response should fail with nil returning password_procedure" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil) + assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret"){nil} + end + private def encode_credentials(options) -- cgit v1.2.3 From 99803b7cdba13345d267127e14809fc389a0e0c2 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 9 Jun 2009 20:10:21 +1200 Subject: Pull autoload fix from 2-3-stable --- actionpack/lib/action_view/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 693ab7c2e0..97fa2d80e9 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -11,7 +11,7 @@ module ActionView #:nodoc: autoload :FormHelper, 'action_view/helpers/form_helper' autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper' autoload :FormTagHelper, 'action_view/helpers/form_tag_helper' - autoload :JavascriptHelper, 'action_view/helpers/javascript_helper' + autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper' autoload :NumberHelper, 'action_view/helpers/number_helper' autoload :PrototypeHelper, 'action_view/helpers/prototype_helper' autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper' -- cgit v1.2.3 From a14df8c9b26b489f1db8fba64c72da3f20af7fcd Mon Sep 17 00:00:00 2001 From: David Stevenson Date: Wed, 11 Mar 2009 09:28:56 -0700 Subject: Made label target radio button tags with values. Radio button now respects inherited :index options when generating id. Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/form_helper.rb | 26 +++++++++++++++++------ actionpack/test/template/form_helper_test.rb | 19 +++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6668482370..8ecec87b10 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -495,7 +495,8 @@ module ActionView # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged - # onto the HTML as an HTML element attribute as in the example shown. + # onto the HTML as an HTML element attribute as in the example shown, except for the :value option, which is designed to + # target labels for radio_button tags (where the value is used in the ID of the input tag). # # ==== Examples # label(:post, :title) @@ -507,6 +508,9 @@ module ActionView # label(:post, :title, "A short title", :class => "title_label") # # => # + # label(:post, :privacy, "Public Post", :value => "public") + # # => + # def label(object_name, method, text = nil, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options) end @@ -729,8 +733,9 @@ module ActionView def to_label_tag(text = nil, options = {}) options = options.stringify_keys + tag_value = options.delete("value") name_and_id = options.dup - add_default_name_and_id(name_and_id) + add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options["for"] ||= name_and_id["id"] content = (text.blank? ? nil : text.to_s) || method_name.humanize @@ -762,11 +767,7 @@ module ActionView checked = self.class.radio_button_checked?(value(object), tag_value) end options["checked"] = "checked" if checked - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase - options["id"] ||= defined?(@auto_index) ? - "#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" : - "#{tag_id}_#{pretty_tag_value}" - add_default_name_and_id(options) + add_default_name_and_id_for_value(tag_value, options) tag("input", options) end @@ -867,6 +868,17 @@ module ActionView end private + def add_default_name_and_id_for_value(tag_value, options) + if tag_value + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + specified_id = options["id"] + add_default_name_and_id(options) + options["id"] += "_#{pretty_tag_value}" unless specified_id + else + add_default_name_and_id(options) + end + end + def add_default_name_and_id(options) if options.has_key?("index") options["name"] ||= tag_name_with_index(options["index"]) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 104649deac..947668ccc6 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -99,6 +99,11 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal('', label(:post, :title, nil, "for" => "my_for")) end + def test_label_for_radio_buttons_with_value + assert_dom_equal('', label("post", "title", "The title goes here", :value => "great_title")) + assert_dom_equal('', label("post", "title", "The title goes here", :value => "great title")) + end + def test_text_field assert_dom_equal( '', text_field("post", "title") @@ -532,6 +537,20 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_radio_button + form_for(:post, @post) do |f| + f.fields_for(:comment, @post, :index => 5) do |c| + concat c.radio_button(:title, "hello") + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_with_auto_index_on_both form_for("post[]", @post) do |f| f.fields_for("comment[]", @post) do |c| -- cgit v1.2.3 From a94e7d7897a300a95d5d5a00c5efc573b42bcb58 Mon Sep 17 00:00:00 2001 From: Stephen Anderson Date: Tue, 6 Jan 2009 11:28:40 -0600 Subject: Sanitized the id generated by text_area_tag helper method. text_area_tag('item[description]') should return: instead of: The old id was causing HTML validation failures. Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 2 +- actionpack/test/template/form_tag_helper_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index e44ad80068..8ab78e7bc6 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -258,7 +258,7 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end - content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys) + content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a check box form input tag. diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 5ca4d4d6ea..09d199b75d 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -154,6 +154,11 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_text_area_tag_id_sanitized + input_elem = root_elem(text_area_tag("item[][description]")) + assert_match VALID_HTML_ID, input_elem['id'] + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() -- cgit v1.2.3 From acb244778587ff400f5b1d54e27028a2dae91101 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 14:50:01 -0700 Subject: Write documentation for AbstractController::Helpers --- .../lib/action_controller/abstract/helpers.rb | 52 ++++++++++++++-------- .../lib/action_controller/abstract/renderer.rb | 3 ++ .../lib/action_controller/new_base/helpers.rb | 4 +- actionpack/test/controller/helper_test.rb | 19 ++++++-- 4 files changed, 55 insertions(+), 23 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 0a2776de9c..6b73f887c1 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -5,33 +5,26 @@ module AbstractController include Renderer included do - extlib_inheritable_accessor :master_helper_module - self.master_helper_module = Module.new + extlib_inheritable_accessor(:_helpers) { Module.new } end + # Override AbstractController::Renderer's _action_view to include the + # helper module for this class into its helpers module. def _action_view - @_action_view ||= begin - av = super - av.helpers.send(:include, master_helper_module) - av - end + @_action_view ||= super.tap { |av| av.helpers.include(_helpers) } end module ClassMethods + # When a class is inherited, wrap its helper module in a new module. + # This ensures that the parent class's module can be changed + # independently of the child class's. def inherited(klass) - klass.master_helper_module = Module.new - klass.master_helper_module.__send__ :include, master_helper_module + helpers = _helpers + klass._helpers = Module.new { include helpers } super end - # Makes all the (instance) methods in the helper module available to templates rendered through this controller. - # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules - # available to the templates. - def add_template_helper(mod) - master_helper_module.module_eval { include mod } - end - # Declare a controller method as a helper. For example, the following # makes the +current_user+ controller method available to the view: # class ApplicationController < ActionController::Base @@ -48,9 +41,13 @@ module AbstractController # # In a view: # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> + # + # ==== Parameters + # meths:: The name of a method on the controller + # to be made available on the view. def helper_method(*meths) meths.flatten.each do |meth| - master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def #{meth}(*args, &blk) controller.send(%(#{meth}), *args, &blk) end @@ -58,6 +55,14 @@ module AbstractController end end + # Make a number of helper modules part of this class' default + # helpers. + # + # ==== Parameters + # *args:: Modules to be included + # block:: Evalulate the block in the context + # of the helper module. Any methods defined in the block + # will be helpers. def helper(*args, &block) args.flatten.each do |arg| case arg @@ -65,7 +70,18 @@ module AbstractController add_template_helper(arg) end end - master_helper_module.module_eval(&block) if block_given? + _helpers.module_eval(&block) if block_given? + end + + private + # Makes all the (instance) methods in the helper module available to templates + # rendered through this controller. + # + # ==== Parameters + # mod:: The module to include into the current helper module + # for the class + def add_template_helper(mod) + _helpers.module_eval { include mod } end end end diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index 28bdda4a75..611d3a16ce 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -29,6 +29,9 @@ module AbstractController # partial:: Whether or not the template to render is a partial # _partial:: If a partial, rather than a template, was rendered, return # the partial. + # helpers:: A module containing the helpers to be used in the view. This + # module should respond_to include. + # controller:: The controller that initialized the ActionView # # Override this method in a to change the default behavior. def _action_view diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index e8000be87b..8925dd3969 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -83,7 +83,7 @@ module ActionController end # Evaluate block in template class if given. - master_helper_module.module_eval(&block) if block_given? + _helpers.module_eval(&block) if block_given? end # Declares helper accessors for controller attributes. For example, the @@ -99,7 +99,7 @@ module ActionController def helpers unless @helper_proxy @helper_proxy = ActionView::Base.new - @helper_proxy.extend master_helper_module + @helper_proxy.extend _helpers else @helper_proxy end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 342cbfbcd3..515c4c9f52 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -127,7 +127,11 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} + methods = if defined?(ActionController::Http) + AllHelpersController._helpers.instance_methods.map {|m| m.to_s} + else + AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} + end # abc_helper.rb assert methods.include?('bare_a') @@ -143,7 +147,12 @@ class HelperTest < Test::Unit::TestCase @controller_class.helpers_dir = File.dirname(__FILE__) + '/../fixtures/alternate_helpers' # Reload helpers - @controller_class.master_helper_module = Module.new + if defined?(ActionController::Http) + @controller_class._helpers = Module.new + else + @controller_class.master_helper_module = Module.new + end + @controller_class.helper :all # helpers/abc_helper.rb should not be included @@ -175,7 +184,11 @@ class HelperTest < Test::Unit::TestCase end def master_helper_methods - @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } + if defined?(ActionController::Http) + @controller_class._helpers.instance_methods.map {|m| m.to_s } + else + @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } + end end def missing_methods -- cgit v1.2.3 From f35f47b8c0bbb181352e9c957f02693cb1801b76 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 16:46:42 -0700 Subject: Cleaning up and documenting AbstractController::Layouts --- .../lib/action_controller/abstract/layouts.rb | 114 ++++++++++---- .../action_controller/new_base/compatibility.rb | 5 +- .../lib/action_controller/new_base/layouts.rb | 171 ++++++++++++++++++++- .../abstract_controller_test.rb | 2 +- .../test/abstract_controller/layouts_test.rb | 6 +- 5 files changed, 255 insertions(+), 43 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 273063f74b..9ff8e9beb1 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -5,16 +5,26 @@ module AbstractController include Renderer included do - extlib_inheritable_accessor :_layout_conditions - self._layout_conditions = {} + extlib_inheritable_accessor(:_layout_conditions) { Hash.new } end module ClassMethods + # Specify the layout to use for this class. + # + # If the specified layout is a: + # String:: the String is the template name + # Symbol:: call the method specified by the symbol, which will return + # the template name + # false:: There is no layout + # true:: raise an ArgumentError + # + # ==== Parameters + # layout:: The layout to use. + # + # ==== Options (conditions) + # :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to. + # :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one def layout(layout, conditions = {}) - unless [String, Symbol, FalseClass, NilClass].include?(layout.class) - raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" - end - conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} } self._layout_conditions = conditions @@ -22,6 +32,11 @@ module AbstractController _write_layout_method end + # If no layout is supplied, look for a template named the return + # value of this method. + # + # ==== Returns + # String:: A template name def _implied_layout_name name.underscore end @@ -29,23 +44,31 @@ module AbstractController # Takes the specified layout and creates a _layout method to be called # by _default_layout # - # If the specified layout is a: - # String:: return the string - # Symbol:: call the method specified by the symbol - # false:: return nil - # none:: If a layout is found in the view paths with the controller's - # name, return that string. Otherwise, use the superclass' - # layout (which might also be implied) + # If there is no explicit layout specified: + # If a layout is found in the view paths with the controller's + # name, return that string. Otherwise, use the superclass' + # layout (which might also be implied) def _write_layout_method case @_layout when String self.class_eval %{def _layout(details) #{@_layout.inspect} end} when Symbol - self.class_eval %{def _layout(details) #{@_layout} end} + self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + def _layout(details) + #{@_layout}.tap do |layout| + unless layout.is_a?(String) || !layout + raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \ + "should have returned a String, false, or nil" + end + end + end + ruby_eval when false self.class_eval %{def _layout(details) end} - else - self.class_eval %{ + when true + raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" + when nil + self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def _layout(details) if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts") "#{_implied_layout_name}" @@ -53,33 +76,54 @@ module AbstractController super end end - } + ruby_eval end end end private - # This will be overwritten - def _layout(details) - end + # This will be overwritten by _write_layout_method + def _layout(details) end - # :api: plugin - # ==== - # Override this to mutate the inbound layout name + # Determine the layout for a given name and details. + # + # ==== Parameters + # name:: The name of the template + # details Object}>:: A list of details to restrict + # the lookup to. By default, layout lookup is limited to the + # formats specified for the current request. def _layout_for_name(name, details = {:formats => formats}) - unless [String, FalseClass, NilClass].include?(name.class) - raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}" - end - - name && view_paths.find_by_parts(name, details, _layout_prefix(name)) + name && _find_by_parts(name, details) end - # TODO: Decide if this is the best hook point for the feature - def _layout_prefix(name) - "layouts" + # Take in the name and details and find a Template. + # + # ==== Parameters + # name:: The name of the template to retrieve + # details:: A list of details to restrict the search by. This + # might include details like the format or locale of the template. + # + # ==== Returns + # Template:: A template object matching the name and details + def _find_by_parts(name, details) + # TODO: Make prefix actually part of details in ViewPath#find_by_parts + prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" + view_paths.find_by_parts(name, details, prefix) end - def _default_layout(require_layout = false, details = {:formats => formats}) + # Returns the default layout for this controller and a given set of details. + # Optionally raises an exception if the layout could not be found. + # + # ==== Parameters + # details:: A list of details to restrict the search by. This + # might include details like the format or locale of the template. + # require_layout:: If this is true, raise an ArgumentError + # with details about the fact that the exception could not be + # found (defaults to false) + # + # ==== Returns + # Template:: The template object for the default layout (or nil) + def _default_layout(details, require_layout = false) if require_layout && _action_has_layout? && !_layout(details) raise ArgumentError, "There was no default layout for #{self.class} in #{view_paths.inspect}" @@ -93,6 +137,12 @@ module AbstractController end end + # Determines whether the current action has a layout by checking the + # action name against the :only and :except conditions set on the + # layout. + # + # ==== Returns + # Boolean:: True if the action has a layout, false otherwise. def _action_has_layout? conditions = _layout_conditions if only = conditions[:only] diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb index f278c2da14..29ba43a879 100644 --- a/actionpack/lib/action_controller/new_base/compatibility.rb +++ b/actionpack/lib/action_controller/new_base/compatibility.rb @@ -114,8 +114,9 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _layout_prefix(name) - super unless name =~ /\blayouts/ + def _find_by_parts(name, details) + details[:prefix] = nil if name =~ /\blayouts/ + super end def performed? diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb index 0ff71587d6..ace4b148c9 100644 --- a/actionpack/lib/action_controller/new_base/layouts.rb +++ b/actionpack/lib/action_controller/new_base/layouts.rb @@ -1,4 +1,163 @@ module ActionController + # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in + # repeated setups. The inclusion pattern has pages that look like this: + # + # <%= render "shared/header" %> + # Hello World + # <%= render "shared/footer" %> + # + # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose + # and if you ever want to change the structure of these two includes, you'll have to change all the templates. + # + # With layouts, you can flip it around and have the common structure know where to insert changing content. This means + # that the header and footer are only mentioned in one place, like this: + # + # // The header part of this layout + # <%= yield %> + # // The footer part of this layout + # + # And then you have content pages that look like this: + # + # hello world + # + # At rendering time, the content page is computed and then inserted in the layout, like this: + # + # // The header part of this layout + # hello world + # // The footer part of this layout + # + # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance + # variable. The preferred notation now is to use yield, as documented above. + # + # == Accessing shared variables + # + # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with + # references that won't materialize before rendering time: + # + #

<%= @page_title %>

+ # <%= yield %> + # + # ...and content pages that fulfill these references _at_ rendering time: + # + # <% @page_title = "Welcome" %> + # Off-world colonies offers you a chance to start a new life + # + # The result after rendering is: + # + #

Welcome

+ # Off-world colonies offers you a chance to start a new life + # + # == Layout assignment + # + # You can either specify a layout declaratively (using the #layout class method) or give + # it the same name as your controller, and place it in app/views/layouts. + # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance. + # + # For instance, if you have PostsController and a template named app/views/layouts/posts.html.erb, + # that template will be used for all actions in PostsController and controllers inheriting + # from PostsController. + # + # If you use a module, for instance Weblog::PostsController, you will need a template named + # app/views/layouts/weblog/posts.html.erb. + # + # Since all your controllers inherit from ApplicationController, they will use + # app/views/layouts/application.html.erb if no other layout is specified + # or provided. + # + # == Inheritance Examples + # + # class BankController < ActionController::Base + # layout "bank_standard" + # + # class InformationController < BankController + # + # class TellerController < BankController + # # teller.html.erb exists + # + # class TillController < TellerController + # + # class VaultController < BankController + # layout :access_level_layout + # + # class EmployeeController < BankController + # layout nil + # + # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites + # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. + # + # The TellerController uses +teller.html.erb+, and TillController inherits that layout and + # uses it as well. + # + # == Types of layouts + # + # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes + # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can + # be done either by specifying a method reference as a symbol or using an inline method (as a proc). + # + # The method reference is the preferred approach to variable layouts and is used like this: + # + # class WeblogController < ActionController::Base + # layout :writers_and_readers + # + # def index + # # fetching posts + # end + # + # private + # def writers_and_readers + # logged_in? ? "writer_layout" : "reader_layout" + # end + # + # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing + # is logged in or not. + # + # If you want to use an inline method, such as a proc, do something like this: + # + # class WeblogController < ActionController::Base + # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } + # + # Of course, the most common way of specifying a layout is still just as a plain template name: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. + # Otherwise, it will be looked up relative to the template root. + # + # == Conditional layouts + # + # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering + # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The + # :only and :except options can be passed to the layout call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard", :except => :rss + # + # # ... + # + # end + # + # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout + # around the rendered view. + # + # Both the :only and :except condition can accept an arbitrary number of method references, so + # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. + # + # == Using a different layout in the action render call + # + # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. + # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. + # You can do this by passing a :layout option to the render call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # def help + # render :action => "help", :layout => "help" + # end + # end + # + # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. module Layouts extend ActiveSupport::Concern @@ -6,6 +165,7 @@ module ActionController include AbstractController::Layouts module ClassMethods + # If no layout is provided, look for a layout with this name. def _implied_layout_name controller_path end @@ -14,16 +174,17 @@ module ActionController private def _determine_template(options) super - if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout) - options[:_layout] = _layout_for_option(options.key?(:layout) ? options[:layout] : :none, options[:_template].details) - end + + return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout) + layout = options.key?(:layout) ? options[:layout] : :none + options[:_layout] = _layout_for_option(layout, options[:_template].details) end def _layout_for_option(name, details) case name when String then _layout_for_name(name, details) - when true then _default_layout(true, details) - when :none then _default_layout(false, details) + when true then _default_layout(details, true) + when :none then _default_layout(details, false) when false, nil then nil else raise ArgumentError, diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index c7eaaeb4ba..05b55216c8 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -154,7 +154,7 @@ module AbstractController end def render_to_body(options = {}) - options[:_layout] = options[:layout] || _default_layout + options[:_layout] = options[:layout] || _default_layout({}) super end end diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 4b66f063f3..64c435abb7 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -25,7 +25,7 @@ module AbstractControllerTests def controller_path() self.class.controller_path end def render_to_body(options) - options[:_layout] = _default_layout + options[:_layout] = _default_layout({}) super end end @@ -221,11 +221,11 @@ module AbstractControllerTests test "raises an exception when specifying layout true" do assert_raises ArgumentError do - Object.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + Object.class_eval do class ::BadOmgFailLolLayout < AbstractControllerTests::Layouts::Base layout true end - RUBY_EVAL + end end end end -- cgit v1.2.3 From b6fde6b4801fae26cdd0e790f6bfd06e7afe9941 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 9 Jun 2009 17:03:02 -0700 Subject: Clean up AbstractController::Logger and write documentation --- actionpack/lib/action_controller/abstract/logger.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb index d6fa843485..b960e152e3 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/action_controller/abstract/logger.rb @@ -5,6 +5,13 @@ module AbstractController module Logger extend ActiveSupport::Concern + # A class that allows you to defer expensive processing + # until the logger actually tries to log. Otherwise, you are + # forced to do the processing in advance, and send the + # entire processed String to the logger, which might + # just discard the String if the log level is too low. + # + # TODO: Require that Rails loggers accept a block. class DelayedLog def initialize(&blk) @blk = blk @@ -20,8 +27,10 @@ module AbstractController cattr_accessor :logger end - def process(action) - ret = super + # Override process_action in the AbstractController::Base + # to log details about the method. + def process_action(action) + super if logger log = DelayedLog.new do @@ -32,10 +41,9 @@ module AbstractController logger.info(log) end - - ret end + private def request_origin # this *needs* to be cached! # otherwise you'd get different results if calling it more than once -- cgit v1.2.3 From 4fc0778123fc775d106f6bc8a4a2a362bf0047ed Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 12:12:15 -0700 Subject: Simplify helper use of ActiveSupport::Dependencies, and use super better for in #helpers --- .../lib/action_controller/new_base/helpers.rb | 83 +++++++++------------- 1 file changed, 34 insertions(+), 49 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 8925dd3969..60d1a6f775 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -56,34 +56,7 @@ module ActionController # helper(:three, BlindHelper) { def mice() 'mice' end } # def helper(*args, &block) - args.flatten.each do |arg| - case arg - when :all - helper all_application_helpers - when String, Symbol - file_name = arg.to_s.underscore + '_helper' - class_name = file_name.camelize - - begin - require_dependency(file_name) - rescue LoadError => load_error - requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] - if requiree == file_name - msg = "Missing helper file helpers/#{file_name}.rb" - raise LoadError.new(msg).copy_blame!(load_error) - else - raise - end - end - - super class_name.constantize - else - super args - end - end - - # Evaluate block in template class if given. - _helpers.module_eval(&block) if block_given? + super(*_modules_for_helpers(args), &block) end # Declares helper accessors for controller attributes. For example, the @@ -97,33 +70,45 @@ module ActionController # Provides a proxy to access helpers methods from outside the view. def helpers - unless @helper_proxy - @helper_proxy = ActionView::Base.new - @helper_proxy.extend _helpers - else - @helper_proxy - end + @helper_proxy ||= ActionView::Base.new.extend(_helpers) end - private - def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize + private + def _modules_for_helpers(args) + args.flatten.map! do |arg| + case arg + when :all + _modules_for_helpers all_application_helpers + when String, Symbol + file_name = "#{arg.to_s.underscore}_helper" + require_dependency(file_name, "Missing helper file helpers/%s.rb") + file_name.camelize.constantize + when Module + arg + else + raise ArgumentError, "helper must be a String, Symbol, or Module" end - rescue MissingSourceFile => e - raise unless e.is_missing? module_path - rescue NameError => e - raise unless e.missing_name? module_name end + end - # Extract helper names from files in app/helpers/**/*.rb - def all_application_helpers - extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ - Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + def default_helper_module! + unless name.blank? + module_name = name.sub(/Controller$|$/, 'Helper') + module_path = module_name.split('::').map { |m| m.underscore }.join('/') + require_dependency module_path + helper module_name.constantize end + rescue MissingSourceFile => e + raise e unless e.is_missing? module_path + rescue NameError => e + raise e unless e.missing_name? module_name + end + + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end end end end -- cgit v1.2.3 From 37be453a8709eb30c97f26369e352e5935436d97 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 12:26:03 -0700 Subject: Adding "test" to the lib directory when running action pack tests on new base --- actionpack/Rakefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 074bc90ca9..53387d305c 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -24,7 +24,9 @@ task :default => [ :test ] desc "Run all unit tests" task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests] -test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] +test_lib_dirs = ENV["NEW"] ? ["test/new_base"] : [] +test_lib_dirs.push "test", "test/lib" +# test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] Rake::TestTask.new(:test_action_pack) do |t| t.libs.concat test_lib_dirs -- cgit v1.2.3 From 316fab7de66041daf616a7126d1a9f293a928144 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:15:21 -0700 Subject: Cleaned up the #default_helper_module method to make better use of #helper instead of duplicating code. --- actionpack/lib/action_controller/new_base/helpers.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 60d1a6f775..8ddfc70607 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -16,7 +16,7 @@ module ActionController module ClassMethods def inherited(klass) - klass.__send__ :default_helper_module! + klass.class_eval { default_helper_module! unless name.blank? } super end @@ -92,16 +92,13 @@ module ActionController end def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize - end + module_name = name.sub(/Controller$/, '') + module_path = module_name.underscore + helper module_path rescue MissingSourceFile => e - raise e unless e.is_missing? module_path + raise e unless e.is_missing? "#{module_path}_helper" rescue NameError => e - raise e unless e.missing_name? module_name + raise e unless e.missing_name? "#{module_name}Helper" end # Extract helper names from files in app/helpers/**/*.rb -- cgit v1.2.3 From fbb6b936e9b896e6b8cbd6d05148a77fcf14b40b Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:28:58 -0700 Subject: Document ActionController::Helpers --- .../lib/action_controller/new_base/helpers.rb | 75 ++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb index 8ddfc70607..2fa5ea6519 100644 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -3,6 +3,51 @@ require 'active_support/core_ext/name_error' require 'active_support/dependencies' module ActionController + # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, + # +numbers+ and model objects, to name a few. These helpers are available to all templates + # by default. + # + # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to + # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will + # include a helper whose name matches that of the controller, e.g., MyController will automatically + # include MyHelper. + # + # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any + # controller which inherits from it. + # + # ==== Examples + # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if + # the Time object is blank: + # + # module FormattedTimeHelper + # def format_time(time, format=:long, blank_message=" ") + # time.blank? ? blank_message : time.to_s(format) + # end + # end + # + # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: + # + # class EventsController < ActionController::Base + # helper FormattedTimeHelper + # def index + # @events = Event.find(:all) + # end + # end + # + # Then, in any view rendered by EventController, the format_time method can be called: + # + # <% @events.each do |event| -%> + #

+ # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> + #

+ # <% end -%> + # + # Finally, assuming we have two event instances, one which has a time and one which does not, + # the output might look like this: + # + # 23 Aug 11:30 | Carolina Railhawks Soccer Match + # N/A | Carolina Railhaws Training Workshop + # module Helpers extend ActiveSupport::Concern @@ -10,8 +55,9 @@ module ActionController included do # Set the default directory for helpers - class_inheritable_accessor :helpers_dir - self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") + extlib_inheritable_accessor(:helpers_dir) do + defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers" + end end module ClassMethods @@ -22,8 +68,9 @@ module ActionController # The +helper+ class method can take a series of helper module names, a block, or both. # - # * *args: One or more modules, strings or symbols, or the special symbol :all. - # * &block: A block defining helper methods. + # ==== Parameters + # *args + # block:: A block defining helper methods # # ==== Examples # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file @@ -64,6 +111,10 @@ module ActionController # controller and makes them available to the view: # helper_attr :name # attr_accessor :name + # + # ==== Parameters + # *attrs:: Names of attributes to be converted + # into helpers. def helper_attr(*attrs) attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } end @@ -74,6 +125,22 @@ module ActionController end private + # Returns a list of modules, normalized from the acceptable kinds of + # helpers with the following behavior: + # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", + # and "foo_bar_helper.rb" is loaded using require_dependency. + # :all:: Loads all modules in the #helpers_dir + # Module:: No further processing + # + # After loading the appropriate files, the corresponding modules + # are returned. + # + # ==== Parameters + # args:: A list of helpers + # + # ==== Returns + # Array[Module]:: A normalized list of modules for the list of + # helpers provided. def _modules_for_helpers(args) args.flatten.map! do |arg| case arg -- cgit v1.2.3 From 82a10ce9f61a02a97d85915bb5fca53730ae28c8 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 14:43:43 -0700 Subject: Removed unnecessary code --- .../lib/action_controller/new_base/hide_actions.rb | 39 ++++++++++------------ actionpack/lib/action_controller/new_base/http.rb | 10 ------ 2 files changed, 17 insertions(+), 32 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb index b45e520bee..86852a26cd 100644 --- a/actionpack/lib/action_controller/new_base/hide_actions.rb +++ b/actionpack/lib/action_controller/new_base/hide_actions.rb @@ -1,39 +1,34 @@ module ActionController + # ActionController::HideActions adds the ability to prevent public methods on a controller + # to be called as actions. module HideActions extend ActiveSupport::Concern included do - extlib_inheritable_accessor :hidden_actions - self.hidden_actions ||= Set.new + extlib_inheritable_accessor(:hidden_actions) { Set.new } end def action_methods - self.class.action_names + self.class.action_methods end - def action_names - action_methods - end - - private - def action_method?(action_name) - !hidden_actions.include?(action_name) && super - end + private - module ClassMethods - def hide_action(*args) - args.each do |arg| - self.hidden_actions << arg.to_s - end - end + def action_method?(action_name) + !hidden_actions.include?(action_name) && super + end - def action_methods - @action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) + module ClassMethods + # Sets + def hide_action(*args) + args.each do |arg| + self.hidden_actions << arg.to_s end + end - def self.action_names - action_methods - end + def action_methods + @action_methods ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) end + end end end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb index c96aaaa865..b3a80094dd 100644 --- a/actionpack/lib/action_controller/new_base/http.rb +++ b/actionpack/lib/action_controller/new_base/http.rb @@ -28,16 +28,6 @@ module ActionController self.class.controller_path end - # :api: private - def self.action_names - action_methods - end - - # :api: private - def action_names - action_methods - end - # :api: plugin def self.call(env) controller = new -- cgit v1.2.3 From 47ff57f6d14fe161900bf85e2d2cf6d7e21a1eb8 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 10 Jun 2009 15:27:53 -0700 Subject: Document and clean up HideActions and Http --- actionpack/lib/action_controller/abstract/base.rb | 7 +--- .../lib/action_controller/new_base/hide_actions.rb | 19 +++++----- actionpack/lib/action_controller/new_base/http.rb | 41 ++++++++++++++++------ actionpack/test/controller/base_test.rb | 11 ++++-- 4 files changed, 49 insertions(+), 29 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index c3daef8759..a19a236ef7 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -95,11 +95,6 @@ module AbstractController end private - # See AbstractController::Base.action_methods - def action_methods - self.class.action_methods - end - # Returns true if the name can be considered an action. This can # be overridden in subclasses to modify the semantics of what # can be considered an action. @@ -110,7 +105,7 @@ module AbstractController # ==== Returns # TrueClass, FalseClass def action_method?(name) - action_methods.include?(name) + self.class.action_methods.include?(name) end # Call the action. Override this in a subclass to modify the diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb index 86852a26cd..af68c772b1 100644 --- a/actionpack/lib/action_controller/new_base/hide_actions.rb +++ b/actionpack/lib/action_controller/new_base/hide_actions.rb @@ -8,26 +8,27 @@ module ActionController extlib_inheritable_accessor(:hidden_actions) { Set.new } end - def action_methods - self.class.action_methods - end - private + # Overrides AbstractController::Base#action_method? to return false if the + # action name is in the list of hidden actions. def action_method?(action_name) !hidden_actions.include?(action_name) && super end module ClassMethods - # Sets + # Sets all of the actions passed in as hidden actions. + # + # ==== Parameters + # *args<#to_s>:: A list of actions def hide_action(*args) - args.each do |arg| - self.hidden_actions << arg.to_s - end + hidden_actions.merge(args.map! {|a| a.to_s }) end + # Overrides AbstractController::Base#action_methods to remove any methods + # that are listed as hidden methods. def action_methods - @action_methods ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)}) + @action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)}) end end end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb index b3a80094dd..2e73561f93 100644 --- a/actionpack/lib/action_controller/new_base/http.rb +++ b/actionpack/lib/action_controller/new_base/http.rb @@ -2,38 +2,48 @@ require 'action_controller/abstract' require 'active_support/core_ext/module/delegation' module ActionController + # ActionController::Http provides a way to get a valid Rack application from a controller. + # + # In AbstractController, dispatching is triggered directly by calling #process on a new controller. + # ActionController::Http provides an #action method that returns a valid Rack application for a + # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, + # can dispatch directly to the action returned by FooController.action(:index). class Http < AbstractController::Base abstract! # :api: public attr_internal :params, :env - # :api: public + # Returns the last part of the controller's name, underscored, without the ending + # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for + # controller_name + # + # ==== Returns + # String def self.controller_name @controller_name ||= controller_path.split("/").last end - # :api: public + # Delegates to the class' #controller_name def controller_name self.class.controller_name end - # :api: public + # Returns the full controller name, underscored, without the ending Controller. + # For instance, MyApp::MyPostsController would return "my_app/my_posts" for + # controller_name. + # + # ==== Returns + # String def self.controller_path @controller_path ||= self.name.sub(/Controller$/, '').underscore end - # :api: public + # Delegates to the class' #controller_path def controller_path self.class.controller_path end - # :api: plugin - def self.call(env) - controller = new - controller.call(env).to_rack - end - # The details below can be overridden to support a specific # Request and Response object. The default ActionController::Base # implementation includes RackConvenience, which makes a request @@ -47,7 +57,7 @@ module ActionController super end - # Basic implements for content_type=, location=, and headers are + # Basic implementations for content_type=, location=, and headers are # provided to reduce the dependency on the RackConvenience module # in Renderer and Redirector. @@ -71,6 +81,15 @@ module ActionController [status, headers, response_body] end + # Return a rack endpoint for the given action. Memoize the endpoint, so + # multiple calls into MyController.action will return the same object + # for the same action. + # + # ==== Parameters + # action<#to_s>:: An action name + # + # ==== Returns + # Proc:: A rack application def self.action(name) @actions ||= {} @actions[name.to_s] ||= proc do |env| diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 3a4cdb81d9..03fd98a85c 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -87,11 +87,11 @@ class ControllerInstanceTests < Test::Unit::TestCase def test_action_methods @empty_controllers.each do |c| hide_mocha_methods_from_controller(c) - assert_equal Set.new, c.__send__(:action_methods), "#{c.controller_path} should be empty!" + assert_equal Set.new, c.class.__send__(:action_methods), "#{c.controller_path} should be empty!" end @non_empty_controllers.each do |c| hide_mocha_methods_from_controller(c) - assert_equal Set.new(%w(public_action)), c.__send__(:action_methods), "#{c.controller_path} should not be empty!" + assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!" end end @@ -145,7 +145,12 @@ class PerformActionTest < ActionController::TestCase def test_method_missing_is_not_an_action_name use_controller MethodMissingController - assert ! @controller.__send__(:action_methods).include?('method_missing') + + if defined?(ActionController::Http) + assert ! @controller.__send__(:action_method?, 'method_missing') + else + assert ! @controller.__send__(:action_methods).include?('method_missing') + end get :method_missing assert_response :success -- cgit v1.2.3 From 59c83af18b1a9189f82b9c5408cd1bad35a11eb2 Mon Sep 17 00:00:00 2001 From: Andrew Kaspick Date: Thu, 4 Jun 2009 22:50:41 -0500 Subject: allow absolute paths for the asset caches Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 6 +++--- actionpack/test/template/asset_tag_helper_test.rb | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index a32beb6100..dffb7089b8 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -277,7 +277,7 @@ module ActionView if ActionController::Base.perform_caching && cache joined_javascript_name = (cache == true ? "all" : cache) + ".js" - joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) + joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name) write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path) javascript_src_tag(joined_javascript_name, options) @@ -417,7 +417,7 @@ module ActionView if ActionController::Base.perform_caching && cache joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" - joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) + joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name) write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path) stylesheet_tag(joined_stylesheet_name, options) @@ -679,4 +679,4 @@ module ActionView end end end -end \ No newline at end of file +end diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 76ceff8d6c..d586afdef6 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -317,9 +317,17 @@ class AssetTagHelperTest < ActionView::TestCase assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + assert_dom_equal( + %(), + javascript_include_tag(:all, :cache => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.js')) + ensure FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host @@ -547,9 +555,17 @@ class AssetTagHelperTest < ActionView::TestCase ) assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :cache => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css')) ensure FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host -- cgit v1.2.3 From d32965399ccfa2052a4d52b70db1bae0ca16830b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 13 Jun 2009 01:01:42 +0200 Subject: uses Object#metaclass and Object#class_eval in a few spots [#2797 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/testing/integration.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb index af4ccb7837..37ca93f22c 100644 --- a/actionpack/lib/action_controller/testing/integration.rb +++ b/actionpack/lib/action_controller/testing/integration.rb @@ -1,6 +1,7 @@ require 'stringio' require 'uri' require 'active_support/test_case' +require 'active_support/core_ext/object/metaclass' require 'rack/mock_session' require 'rack/test/cookie_jar' @@ -191,7 +192,7 @@ module ActionController unless defined? @named_routes_configured # install the named routes in this session instance. - klass = class << self; self; end + klass = metaclass Routing::Routes.install_helpers(klass) # the helpers are made protected by default--we make them public for -- cgit v1.2.3 From b56169c26240db1b1cff3c6f65a34b7516a9fc15 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 13 Jun 2009 12:46:09 +0200 Subject: moves reopening of core classes to add generic stuff from Action Pack to AS/core_ext and adds tests [#2798 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_controller/routing/routing_ext.rb | 53 ++-------------------- 1 file changed, 4 insertions(+), 49 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb index 4a82b2af5f..5e5b22b6c2 100644 --- a/actionpack/lib/action_controller/routing/routing_ext.rb +++ b/actionpack/lib/action_controller/routing/routing_ext.rb @@ -1,49 +1,4 @@ -class Object - def to_param - to_s - end -end - -class TrueClass - def to_param - self - end -end - -class FalseClass - def to_param - self - end -end - -class NilClass - def to_param - self - end -end - -class Regexp #:nodoc: - def number_of_captures - Regexp.new("|#{source}").match('').captures.length - end - - def multiline? - options & MULTILINE == MULTILINE - end - - class << self - def optionalize(pattern) - case unoptionalize(pattern) - when /\A(.|\(.*\))\Z/ then "#{pattern}?" - else "(?:#{pattern})?" - end - end - - def unoptionalize(pattern) - [/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp| - return $1 if regexp =~ pattern - end - return pattern - end - end -end +require 'active_support/core_ext/object/conversions' +require 'active_support/core_ext/boolean/conversions' +require 'active_support/core_ext/nil/conversions' +require 'active_support/core_ext/regexp' -- cgit v1.2.3 From c50b03b754948b676b74c334edfb277fa45c1d14 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 15 Jun 2009 10:23:23 -0500 Subject: Add :concat option to asset tag helpers to force concatenation. This is useful for working around IE's stylesheet limit. stylesheet_link_tag :all, :concat => true --- .../lib/action_view/helpers/asset_tag_helper.rb | 24 ++++++++++++----- actionpack/test/template/asset_tag_helper_test.rb | 30 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index dffb7089b8..babb9db38a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -272,14 +272,17 @@ module ActionView # javascript_include_tag :all, :cache => true, :recursive => true def javascript_include_tag(*sources) options = sources.extract_options!.stringify_keys - cache = options.delete("cache") + concat = options.delete("concat") + cache = concat || options.delete("cache") recursive = options.delete("recursive") - if ActionController::Base.perform_caching && cache + if concat || (ActionController::Base.perform_caching && cache) joined_javascript_name = (cache == true ? "all" : cache) + ".js" joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name) - write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path) + unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path) + write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) + end javascript_src_tag(joined_javascript_name, options) else expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n") @@ -410,16 +413,25 @@ module ActionView # The :recursive option is also available for caching: # # stylesheet_link_tag :all, :cache => true, :recursive => true + # + # To force concatenation (even in development mode) set :concat to true. This is useful if + # you have too many stylesheets for IE to load. + # + # stylesheet_link_tag :all, :concat => true + # def stylesheet_link_tag(*sources) options = sources.extract_options!.stringify_keys - cache = options.delete("cache") + concat = options.delete("concat") + cache = concat || options.delete("cache") recursive = options.delete("recursive") - if ActionController::Base.perform_caching && cache + if concat || (ActionController::Base.perform_caching && cache) joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name) - write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path) + unless ActionController::Base.perform_caching && File.exists?(joined_stylesheet_path) + write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) + end stylesheet_tag(joined_stylesheet_name, options) else expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n") diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index d586afdef6..f1dad9f50e 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -568,6 +568,36 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end + def test_concat_stylesheet_link_tag_when_caching_off + ENV["RAILS_ASSET_ID"] = "" + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => true) + ) + + expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max + assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => "money") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css')) + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) + end + def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" } -- cgit v1.2.3 From 5a8a550a45c5ca7abc9785ed180d5f46189c9958 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 11:21:08 -0700 Subject: Finish making things pass with updated internal content_type semantics --- .../lib/action_controller/base/mime_responds.rb | 2 +- actionpack/lib/action_controller/base/streaming.rb | 9 ++-- .../action_controller/new_base/rack_convenience.rb | 2 +- .../lib/action_controller/testing/process.rb | 2 + actionpack/lib/action_dispatch/http/response.rb | 56 +++++----------------- actionpack/test/controller/content_type_test.rb | 4 +- actionpack/test/controller/send_file_test.rb | 10 ++-- 7 files changed, 26 insertions(+), 59 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb index 3c17dda1a1..5c7218691e 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -125,7 +125,7 @@ module ActionController #:nodoc: end @controller.template.formats = [mime_type.to_sym] - @response.content_type = mime_type.to_s + @response.content_type = mime_type block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) end diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb index 73d4bde6c1..5c72fc9ad9 100644 --- a/actionpack/lib/action_controller/base/streaming.rb +++ b/actionpack/lib/action_controller/base/streaming.rb @@ -162,15 +162,16 @@ module ActionController #:nodoc: disposition <<= %(; filename="#{options[:filename]}") if options[:filename] content_type = options[:type] + if content_type.is_a?(Symbol) - raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.has_key?(content_type.to_s) - content_type = Mime::Type.lookup_by_extension(content_type.to_s) + raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s) + self.content_type = Mime::Type.lookup_by_extension(content_type.to_s) + else + self.content_type = content_type end - content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers headers.merge!( 'Content-Length' => options[:length], - 'Content-Type' => content_type, 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary' ) diff --git a/actionpack/lib/action_controller/new_base/rack_convenience.rb b/actionpack/lib/action_controller/new_base/rack_convenience.rb index 5dfa7d12f3..805157b0e3 100644 --- a/actionpack/lib/action_controller/new_base/rack_convenience.rb +++ b/actionpack/lib/action_controller/new_base/rack_convenience.rb @@ -3,7 +3,7 @@ module ActionController extend ActiveSupport::Concern included do - delegate :headers, :status=, :location=, + delegate :headers, :status=, :location=, :content_type=, :status, :location, :content_type, :to => "@_response" attr_internal :request, :response end diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 9647f8ce45..ea5752d4f5 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -56,6 +56,8 @@ module ActionController #:nodoc: @block = nil @length = 0 @body = [] + @charset = nil + @content_type = nil @request = @template = nil end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index b9db7a4508..e58b4b5e19 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -67,12 +67,7 @@ module ActionDispatch # :nodoc: end def body=(body) - @body = - if body.respond_to?(:to_str) - [body] - else - body - end + @body = body.respond_to?(:to_str) ? [body] : body end def body_parts @@ -96,36 +91,7 @@ module ActionDispatch # :nodoc: # If a character set has been defined for this response (see charset=) then # the character set information will also be included in the content type # information. - def content_type=(mime_type) - self.headers["Content-Type"] = - if mime_type =~ /charset/ || (c = charset).nil? - mime_type.to_s - else - "#{mime_type}; charset=#{c}" - end - end - - # Returns the response's content MIME type, or nil if content type has been set. - def content_type - content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0] - content_type.blank? ? nil : content_type - end - - # Set the charset of the Content-Type header. Set to nil to remove it. - # If no content type is set, it defaults to HTML. - def charset=(charset) - headers["Content-Type"] = - if charset - "#{content_type || Mime::HTML}; charset=#{charset}" - else - content_type || Mime::HTML.to_s - end - end - - def charset - charset = String(headers["Content-Type"] || headers["type"]).split(";")[1] - charset.blank? ? nil : charset.strip.split("=")[1] - end + attr_accessor :charset, :content_type def last_modified if last = headers['Last-Modified'] @@ -162,15 +128,15 @@ module ActionDispatch # :nodoc: end def assign_default_content_type_and_charset! - if type = headers['Content-Type'] || headers['type'] - unless type =~ /charset=/ || sending_file? - headers['Content-Type'] = "#{type}; charset=#{default_charset}" - end - else - type = Mime::HTML.to_s - type += "; charset=#{default_charset}" unless sending_file? - headers['Content-Type'] = type - end + return if !headers["Content-Type"].blank? + + @content_type ||= Mime::HTML + @charset ||= default_charset + + type = @content_type.to_s.dup + type << "; charset=#{@charset}" unless sending_file? + + headers["Content-Type"] = type end def prepare! diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index d622ac1e85..511788aec8 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -83,14 +83,14 @@ class ContentTypeTest < ActionController::TestCase # :ported: def test_content_type_from_body get :render_content_type_from_body - assert_equal "application/rss+xml", @response.content_type + assert_equal Mime::RSS, @response.content_type assert_equal "utf-8", @response.charset end # :ported: def test_content_type_from_render get :render_content_type_from_render - assert_equal "application/rss+xml", @response.content_type + assert_equal Mime::RSS, @response.content_type assert_equal "utf-8", @response.charset end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 0bc0eb2df6..4134da3b9e 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -91,10 +91,10 @@ class SendFileTest < ActionController::TestCase def test_headers_after_send_shouldnt_include_charset response = process('data') - assert_equal "application/octet-stream", response.content_type + assert_equal "application/octet-stream", response.headers["Content-Type"] response = process('file') - assert_equal "application/octet-stream", response.content_type + assert_equal "application/octet-stream", response.headers["Content-Type"] end # Test that send_file_headers! is setting the correct HTTP headers. @@ -116,7 +116,7 @@ class SendFileTest < ActionController::TestCase h = @controller.headers assert_equal 1, h['Content-Length'] - assert_equal 'image/png', h['Content-Type'] + assert_equal 'image/png', @controller.content_type assert_equal 'disposition; filename="filename"', h['Content-Disposition'] assert_equal 'binary', h['Content-Transfer-Encoding'] @@ -136,9 +136,7 @@ class SendFileTest < ActionController::TestCase @controller.headers = {} @controller.send(:send_file_headers!, options) - headers = @controller.headers - - assert_equal 'image/png', headers['Content-Type'] + assert_equal 'image/png', @controller.content_type end -- cgit v1.2.3 From a63caa4c0c2a8aabc13c354a9193ebd9c5e8ba73 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 11:44:45 -0700 Subject: Get tests to run (with failures) without old base around --- actionpack/lib/action_controller.rb | 111 +-- actionpack/lib/action_controller/base/base.rb | 1005 +++----------------- .../action_controller/base/chained/benchmarking.rb | 107 --- .../lib/action_controller/base/chained/filters.rb | 670 ------------- .../lib/action_controller/base/chained/flash.rb | 196 ---- .../lib/action_controller/base/compatibility.rb | 139 +++ .../lib/action_controller/base/conditional_get.rb | 133 +++ actionpack/lib/action_controller/base/cookies.rb | 94 -- .../base/filter_parameter_logging.rb | 95 -- actionpack/lib/action_controller/base/helpers.rb | 277 +++--- .../lib/action_controller/base/hide_actions.rb | 35 + actionpack/lib/action_controller/base/http.rb | 100 ++ .../action_controller/base/http_authentication.rb | 309 ------ actionpack/lib/action_controller/base/layout.rb | 251 ----- actionpack/lib/action_controller/base/layouts.rb | 195 ++++ .../lib/action_controller/base/mime_responds.rb | 188 ---- .../lib/action_controller/base/rack_convenience.rb | 33 + actionpack/lib/action_controller/base/redirect.rb | 89 -- .../lib/action_controller/base/redirector.rb | 19 + actionpack/lib/action_controller/base/render.rb | 403 -------- .../lib/action_controller/base/render_options.rb | 103 ++ actionpack/lib/action_controller/base/renderer.rb | 78 ++ .../base/request_forgery_protection.rb | 123 --- actionpack/lib/action_controller/base/rescuable.rb | 52 + actionpack/lib/action_controller/base/rescue.rb | 50 - actionpack/lib/action_controller/base/responder.rb | 43 - actionpack/lib/action_controller/base/session.rb | 15 + .../action_controller/base/session_management.rb | 54 -- actionpack/lib/action_controller/base/streaming.rb | 188 ---- actionpack/lib/action_controller/base/testing.rb | 39 + actionpack/lib/action_controller/base/url_for.rb | 49 + .../lib/action_controller/base/verification.rb | 133 --- actionpack/lib/action_controller/new_base.rb | 47 - actionpack/lib/action_controller/new_base/base.rb | 173 ---- .../action_controller/new_base/compatibility.rb | 139 --- .../action_controller/new_base/conditional_get.rb | 133 --- .../lib/action_controller/new_base/helpers.rb | 178 ---- .../lib/action_controller/new_base/hide_actions.rb | 35 - actionpack/lib/action_controller/new_base/http.rb | 100 -- .../lib/action_controller/new_base/layouts.rb | 195 ---- .../action_controller/new_base/rack_convenience.rb | 33 - .../lib/action_controller/new_base/redirector.rb | 19 - .../action_controller/new_base/render_options.rb | 103 -- .../lib/action_controller/new_base/renderer.rb | 78 -- .../lib/action_controller/new_base/rescuable.rb | 52 - .../lib/action_controller/new_base/session.rb | 15 - .../lib/action_controller/new_base/testing.rb | 39 - .../lib/action_controller/new_base/url_for.rb | 49 - actionpack/lib/action_controller/old_base.rb | 84 ++ actionpack/lib/action_controller/old_base/base.rb | 884 +++++++++++++++++ .../old_base/chained/benchmarking.rb | 107 +++ .../action_controller/old_base/chained/filters.rb | 670 +++++++++++++ .../action_controller/old_base/chained/flash.rb | 196 ++++ .../lib/action_controller/old_base/cookies.rb | 94 ++ .../old_base/filter_parameter_logging.rb | 95 ++ .../lib/action_controller/old_base/helpers.rb | 221 +++++ .../old_base/http_authentication.rb | 309 ++++++ .../lib/action_controller/old_base/layout.rb | 251 +++++ .../action_controller/old_base/mime_responds.rb | 188 ++++ .../lib/action_controller/old_base/redirect.rb | 89 ++ .../lib/action_controller/old_base/render.rb | 403 ++++++++ .../old_base/request_forgery_protection.rb | 123 +++ .../lib/action_controller/old_base/rescue.rb | 50 + .../lib/action_controller/old_base/responder.rb | 43 + .../old_base/session_management.rb | 54 ++ .../lib/action_controller/old_base/streaming.rb | 188 ++++ .../lib/action_controller/old_base/verification.rb | 133 +++ actionpack/test/abstract_unit.rb | 169 +++- actionpack/test/new_base/abstract_unit.rb | 173 ---- actionpack/test/old_base/abstract_unit.rb | 46 + 70 files changed, 5667 insertions(+), 5667 deletions(-) delete mode 100644 actionpack/lib/action_controller/base/chained/benchmarking.rb delete mode 100644 actionpack/lib/action_controller/base/chained/filters.rb delete mode 100644 actionpack/lib/action_controller/base/chained/flash.rb create mode 100644 actionpack/lib/action_controller/base/compatibility.rb create mode 100644 actionpack/lib/action_controller/base/conditional_get.rb delete mode 100644 actionpack/lib/action_controller/base/cookies.rb delete mode 100644 actionpack/lib/action_controller/base/filter_parameter_logging.rb create mode 100644 actionpack/lib/action_controller/base/hide_actions.rb create mode 100644 actionpack/lib/action_controller/base/http.rb delete mode 100644 actionpack/lib/action_controller/base/http_authentication.rb delete mode 100644 actionpack/lib/action_controller/base/layout.rb create mode 100644 actionpack/lib/action_controller/base/layouts.rb delete mode 100644 actionpack/lib/action_controller/base/mime_responds.rb create mode 100644 actionpack/lib/action_controller/base/rack_convenience.rb delete mode 100644 actionpack/lib/action_controller/base/redirect.rb create mode 100644 actionpack/lib/action_controller/base/redirector.rb delete mode 100644 actionpack/lib/action_controller/base/render.rb create mode 100644 actionpack/lib/action_controller/base/render_options.rb create mode 100644 actionpack/lib/action_controller/base/renderer.rb delete mode 100644 actionpack/lib/action_controller/base/request_forgery_protection.rb create mode 100644 actionpack/lib/action_controller/base/rescuable.rb delete mode 100644 actionpack/lib/action_controller/base/rescue.rb delete mode 100644 actionpack/lib/action_controller/base/responder.rb create mode 100644 actionpack/lib/action_controller/base/session.rb delete mode 100644 actionpack/lib/action_controller/base/session_management.rb delete mode 100644 actionpack/lib/action_controller/base/streaming.rb create mode 100644 actionpack/lib/action_controller/base/testing.rb create mode 100644 actionpack/lib/action_controller/base/url_for.rb delete mode 100644 actionpack/lib/action_controller/base/verification.rb delete mode 100644 actionpack/lib/action_controller/new_base.rb delete mode 100644 actionpack/lib/action_controller/new_base/base.rb delete mode 100644 actionpack/lib/action_controller/new_base/compatibility.rb delete mode 100644 actionpack/lib/action_controller/new_base/conditional_get.rb delete mode 100644 actionpack/lib/action_controller/new_base/helpers.rb delete mode 100644 actionpack/lib/action_controller/new_base/hide_actions.rb delete mode 100644 actionpack/lib/action_controller/new_base/http.rb delete mode 100644 actionpack/lib/action_controller/new_base/layouts.rb delete mode 100644 actionpack/lib/action_controller/new_base/rack_convenience.rb delete mode 100644 actionpack/lib/action_controller/new_base/redirector.rb delete mode 100644 actionpack/lib/action_controller/new_base/render_options.rb delete mode 100644 actionpack/lib/action_controller/new_base/renderer.rb delete mode 100644 actionpack/lib/action_controller/new_base/rescuable.rb delete mode 100644 actionpack/lib/action_controller/new_base/session.rb delete mode 100644 actionpack/lib/action_controller/new_base/testing.rb delete mode 100644 actionpack/lib/action_controller/new_base/url_for.rb create mode 100644 actionpack/lib/action_controller/old_base.rb create mode 100644 actionpack/lib/action_controller/old_base/base.rb create mode 100644 actionpack/lib/action_controller/old_base/chained/benchmarking.rb create mode 100644 actionpack/lib/action_controller/old_base/chained/filters.rb create mode 100644 actionpack/lib/action_controller/old_base/chained/flash.rb create mode 100644 actionpack/lib/action_controller/old_base/cookies.rb create mode 100644 actionpack/lib/action_controller/old_base/filter_parameter_logging.rb create mode 100644 actionpack/lib/action_controller/old_base/helpers.rb create mode 100644 actionpack/lib/action_controller/old_base/http_authentication.rb create mode 100644 actionpack/lib/action_controller/old_base/layout.rb create mode 100644 actionpack/lib/action_controller/old_base/mime_responds.rb create mode 100644 actionpack/lib/action_controller/old_base/redirect.rb create mode 100644 actionpack/lib/action_controller/old_base/render.rb create mode 100644 actionpack/lib/action_controller/old_base/request_forgery_protection.rb create mode 100644 actionpack/lib/action_controller/old_base/rescue.rb create mode 100644 actionpack/lib/action_controller/old_base/responder.rb create mode 100644 actionpack/lib/action_controller/old_base/session_management.rb create mode 100644 actionpack/lib/action_controller/old_base/streaming.rb create mode 100644 actionpack/lib/action_controller/old_base/verification.rb delete mode 100644 actionpack/test/new_base/abstract_unit.rb create mode 100644 actionpack/test/old_base/abstract_unit.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index dd22bfd617..44724fc9cb 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,81 +1,44 @@ -#-- -# Copyright (c) 2004-2009 David Heinemeier Hansson -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#++ - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' - -require File.join(File.dirname(__FILE__), "action_pack") - module ActionController - # TODO: Review explicit to see if they will automatically be handled by - # the initilizer if they are really needed. - def self.load_all! - [Base, Request, Response, UrlRewriter, UrlWriter] - [ActionDispatch::Http::Headers] - end + autoload :Base, "action_controller/base/base" + autoload :ConditionalGet, "action_controller/base/conditional_get" + autoload :HideActions, "action_controller/base/hide_actions" + autoload :Http, "action_controller/base/http" + autoload :Layouts, "action_controller/base/layouts" + autoload :RackConvenience, "action_controller/base/rack_convenience" + autoload :Rails2Compatibility, "action_controller/base/compatibility" + autoload :Redirector, "action_controller/base/redirector" + autoload :Renderer, "action_controller/base/renderer" + autoload :RenderOptions, "action_controller/base/render_options" + autoload :Renderers, "action_controller/base/render_options" + autoload :Rescue, "action_controller/base/rescuable" + autoload :Testing, "action_controller/base/testing" + autoload :UrlFor, "action_controller/base/url_for" + autoload :Session, "action_controller/base/session" + autoload :Helpers, "action_controller/base/helpers" - autoload :Base, 'action_controller/base/base' - autoload :Benchmarking, 'action_controller/base/chained/benchmarking' - autoload :Caching, 'action_controller/caching' - autoload :Cookies, 'action_controller/base/cookies' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Filters, 'action_controller/base/chained/filters' - autoload :Flash, 'action_controller/base/chained/flash' - autoload :Helpers, 'action_controller/base/helpers' - autoload :HttpAuthentication, 'action_controller/base/http_authentication' - autoload :Integration, 'action_controller/testing/integration' - autoload :IntegrationTest, 'action_controller/testing/integration' - autoload :Layout, 'action_controller/base/layout' - autoload :MimeResponds, 'action_controller/base/mime_responds' + # Ported modules + # require 'action_controller/routing' + autoload :Caching, 'action_controller/caching' + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :MimeResponds, 'action_controller/old_base/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Redirector, 'action_controller/base/redirect' - autoload :Renderer, 'action_controller/base/render' - autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' - autoload :Rescue, 'action_controller/base/rescue' - autoload :Resources, 'action_controller/routing/resources' - autoload :Responder, 'action_controller/base/responder' - autoload :Routing, 'action_controller/routing' - autoload :SessionManagement, 'action_controller/base/session_management' - autoload :Streaming, 'action_controller/base/streaming' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :TestProcess, 'action_controller/testing/process' - autoload :Translation, 'action_controller/translation' - autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser' - autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' - autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - autoload :Verification, 'action_controller/base/verification' - autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' + autoload :RecordIdentifier, 'action_controller/record_identifier' + autoload :Resources, 'action_controller/routing/resources' + autoload :SessionManagement, 'action_controller/old_base/session_management' + autoload :TestCase, 'action_controller/testing/test_case' + autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' + autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' + + autoload :Verification, 'action_controller/old_base/verification' + autoload :Flash, 'action_controller/old_base/chained/flash' + autoload :RequestForgeryProtection, 'action_controller/old_base/request_forgery_protection' + autoload :Streaming, 'action_controller/old_base/streaming' + autoload :HttpAuthentication, 'action_controller/old_base/http_authentication' + autoload :FilterParameterLogging, 'action_controller/old_base/filter_parameter_logging' + autoload :Translation, 'action_controller/translation' + autoload :Cookies, 'action_controller/old_base/cookies' - module Assertions - autoload :DomAssertions, 'action_controller/testing/assertions/dom' - autoload :ModelAssertions, 'action_controller/testing/assertions/model' - autoload :ResponseAssertions, 'action_controller/testing/assertions/response' - autoload :RoutingAssertions, 'action_controller/testing/assertions/routing' - autoload :SelectorAssertions, 'action_controller/testing/assertions/selector' - autoload :TagAssertions, 'action_controller/testing/assertions/tag' - end + require 'action_controller/routing' end autoload :HTML, 'action_controller/vendor/html-scanner' diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb index 67369eb122..e8fc153578 100644 --- a/actionpack/lib/action_controller/base/base.rb +++ b/actionpack/lib/action_controller/base/base.rb @@ -1,884 +1,173 @@ -require 'action_controller/deprecated' -require 'set' -require 'active_support/core_ext/class/inheritable_attributes' -require 'active_support/core_ext/module/attr_internal' - -module ActionController #:nodoc: - class ActionControllerError < StandardError #:nodoc: - end - - class SessionRestoreError < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class RoutingError < ActionControllerError #:nodoc: - attr_reader :failures - def initialize(message, failures=[]) - super(message) - @failures = failures - end - end - - class MethodNotAllowed < ActionControllerError #:nodoc: - attr_reader :allowed_methods - - def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - end - - class NotImplemented < MethodNotAllowed #:nodoc: - end - - class UnknownController < ActionControllerError #:nodoc: - end - - class UnknownAction < ActionControllerError #:nodoc: - end - - class MissingFile < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class SessionOverflowError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - class UnknownHttpMethod < ActionControllerError #:nodoc: - end - - # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed - # on request and then either render a template or redirect to another action. An action is defined as a public method - # on the controller, which will automatically be made accessible to the web-server through Rails Routes. - # - # A sample controller could look like this: - # - # class GuestBookController < ActionController::Base - # def index - # @entries = Entry.find(:all) - # end - # - # def sign - # Entry.create(params[:entry]) - # redirect_to :action => "index" - # end - # end - # - # Actions, by default, render a template in the app/views directory corresponding to the name of the controller and action - # after executing code in the action. For example, the +index+ action of the GuestBookController would render the - # template app/views/guestbook/index.erb by default after populating the @entries instance variable. - # - # Unlike index, the sign action will not render a template. After performing its main purpose (creating a - # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external - # "302 Moved" HTTP response that takes the user to the index action. - # - # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. - # Most actions are variations of these themes. - # - # == Requests - # - # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters. - # This value should hold the name of the action to be performed. Once the action has been identified, the remaining - # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to - # the action through instance variables. Then the action is performed. - # - # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries - # are made by accessing the environment hash, like this: - # - # def server_ip - # location = request.env["SERVER_ADDR"] - # render :text => "This server hosted at #{location}" - # end - # - # == Parameters - # - # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method - # which returns a hash. For example, an action that was performed through /weblog/list?category=All&limit=5 will include - # { "category" => "All", "limit" => 5 } in params. - # - # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as: - # - # - # - # - # A request stemming from a form holding these inputs will include { "post" => { "name" => "david", "address" => "hyacintvej" } }. - # If the address input had been named "post[address][street]", the params would have included - # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting. - # - # == Sessions - # - # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted, - # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such - # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely - # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at. - # - # You can place objects in the session by using the session method, which accesses a hash: - # - # session[:person] = Person.authenticate(user_name, password) - # - # And retrieved again through the same hash: - # - # Hello #{session[:person]} - # - # For removing objects from the session, you can either assign a single key to +nil+: - # - # # removes :person from session - # session[:person] = nil - # - # or you can remove the entire session with +reset_session+. - # - # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted. - # This prevents the user from tampering with the session but also allows him to see its contents. - # - # Do not put secret information in cookie-based sessions! - # - # Other options for session storage are: - # - # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and, - # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set - # - # config.action_controller.session_store = :active_record_store - # - # in your config/environment.rb and run rake db:sessions:create. - # - # * MemCacheStore - Sessions are stored as entries in your memcached cache. - # Set the session store type in config/environment.rb: - # - # config.action_controller.session_store = :mem_cache_store - # - # This assumes that memcached has been installed and configured properly. - # See the MemCacheStore docs for more information. - # - # == Responses - # - # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response - # object is generated automatically through the use of renders and redirects and requires no user intervention. - # - # == Renders - # - # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering - # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured. - # The controller passes objects to the view by assigning instance variables: - # - # def show - # @post = Post.find(params[:id]) - # end - # - # Which are then automatically available to the view: - # - # Title: <%= @post.title %> - # - # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use - # the manual rendering methods: - # - # def search - # @results = Search.find(params[:query]) - # case @results - # when 0 then render :action => "no_results" - # when 1 then render :action => "show" - # when 2..10 then render :action => "show_many" - # end - # end - # - # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html. - # - # == Redirects - # - # Redirects are used to move from one action to another. For example, after a create action, which stores a blog entry to a database, - # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to) - # a show action that we'll assume has already been created. The code might look like this: - # - # def create - # @entry = Entry.new(params[:entry]) - # if @entry.save - # # The entry was saved correctly, redirect to show - # redirect_to :action => 'show', :id => @entry.id - # else - # # things didn't go so well, do something else - # end - # end - # - # In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed. - # - # == Calling multiple redirects or renders - # - # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError: - # - # def do_something - # redirect_to :action => "elsewhere" - # render :action => "overthere" # raises DoubleRenderError - # end - # - # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution. - # - # def do_something - # redirect_to(:action => "elsewhere") and return if monkeys.nil? - # render :action => "overthere" # won't be called if monkeys is nil - # end - # - class Base - +module ActionController + class Base < Http + abstract! + + include AbstractController::Benchmarker + include AbstractController::Callbacks + include AbstractController::Logger + + include ActionController::Helpers + include ActionController::HideActions + include ActionController::UrlFor + include ActionController::Redirector + include ActionController::Renderer + include ActionController::Renderers::All + include ActionController::Layouts + include ActionController::ConditionalGet + include ActionController::RackConvenience + + # Legacy modules + include SessionManagement include ActionDispatch::StatusCodes - - cattr_reader :protected_instance_variables - # Controller specific instance variables which will not be accessible inside views. - @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller - @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params - @_flash @_response) - - # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, - # and images to a dedicated asset server away from the main web server. Example: - # ActionController::Base.asset_host = "http://assets.example.com" - cattr_accessor :asset_host - - # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors. - # When the application is ready to go public, this should be set to false, and the protected method local_request? - # should instead be implemented in the controller to determine when debugging screens should be shown. - @@consider_all_requests_local = true - cattr_accessor :consider_all_requests_local - - # Indicates whether to allow concurrent action processing. Your - # controller actions and any other code they call must also behave well - # when called from concurrent threads. Turned off by default. - @@allow_concurrency = false - cattr_accessor :allow_concurrency - - # Modern REST web services often need to submit complex data to the web application. - # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the - # params hash. These handlers are invoked for POST and PUT requests. - # - # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated - # in the params. This allows XML requests to mask themselves as regular form submissions, so you can have one - # action serve both regular forms and web service requests. - # - # Example of doing your own parser for a custom content type: - # - # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data| - # node = REXML::Document.new(post) - # { node.root.name => node.root } - # end - # - # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the - # root node for such requests. The new default is to keep the root, such that "David" results - # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can - # re-register XmlSimple as application/xml handler ike this: - # - # ActionController::Base.param_parsers[Mime::XML] = - # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) } - # - # A YAML parser is also available and can be turned on with: - # - # ActionController::Base.param_parsers[Mime::YAML] = :yaml - @@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. - @@default_charset = "utf-8" - cattr_accessor :default_charset - - # The logger is used for generating information on the action run-time (including benchmarking) if available. - # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers. - cattr_accessor :logger - - # Controls the resource action separator - @@resource_action_separator = "/" - cattr_accessor :resource_action_separator - - # Allow to override path names for default resources' actions - @@resources_path_names = { :new => 'new', :edit => 'edit' } - cattr_accessor :resources_path_names - - # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ - # sets it to :authenticity_token by default. - cattr_accessor :request_forgery_protection_token - - # Controls the IP Spoofing check when determining the remote IP. - @@ip_spoofing_check = true - cattr_accessor :ip_spoofing_check - - # Indicates whether or not optimise the generated named - # route helper methods - cattr_accessor :optimise_named_routes - self.optimise_named_routes = true - - # Indicates whether the response format should be determined by examining the Accept HTTP header, - # or by using the simpler params + ajax rules. - # - # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept - # header into account. If it is set to false then the request format will be determined solely - # by examining params[:format]. If params format is missing, the format will be either HTML or - # Javascript depending on whether the request is an AJAX request. - cattr_accessor :use_accept_header - self.use_accept_header = true - - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection - self.allow_forgery_protection = true - - # If you are deploying to a subdirectory, you will need to set - # config.action_controller.relative_url_root - # This defaults to ENV['RAILS_RELATIVE_URL_ROOT'] - cattr_accessor :relative_url_root - self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] - - # Holds the request object that's primarily used to get environment variables through access like - # request.env["REQUEST_URI"]. - attr_internal :request - - # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like params["post_id"] - # to get the post_id. No type casts are made, so all values are returned as strings. - attr_internal :params - - # Holds the response object that's primarily used to set additional HTTP headers through access like - # response.headers["Cache-Control"] = "no-cache". Can also be used to access the final body HTML after a template - # has been rendered through response.body -- useful for after_filters that wants to manipulate the output, - # such as a OutputCompressionFilter. - attr_internal :response - - # Holds a hash of objects in the session. Accessed like session[:person] to get the object tied to the "person" - # key. The session will hold any type of object as values, but the key should be a string or symbol. - def session - request.session - end - - # Holds a hash of header names and values. Accessed like headers["Cache-Control"] to get the value of the Cache-Control - # directive. Values should always be specified as strings. - attr_internal :headers - - # Returns the name of the action this controller is processing. - attr_accessor :action_name - - attr_reader :template - - def action(name, env) - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - self.action_name = name && name.to_s - process(request, response).to_a - end - - - class << self - def action(name = nil) - @actions ||= {} - @actions[name] ||= proc do |env| - new.action(name, env) - end - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". - def controller_class_name - @controller_class_name ||= name.demodulize - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". - def controller_name - @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". - def controller_path - @controller_path ||= name.gsub(/Controller$/, '').underscore - end - - # Return an array containing the names of public methods that have been marked hidden from the action processor. - # By default, all methods defined in ActionController::Base and included modules are hidden. - # More methods can be hidden using hide_action. - def hidden_actions - read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, []) - end - - # Hide each of the given methods from being callable as actions. - def hide_action(*names) - write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s }) - end - - # View load paths determine the bases from which template references can be made. So a call to - # render("test/template") will be looked up in the view load paths array and the closest match will be - # returned. - def view_paths - if defined? @view_paths - @view_paths - else - superclass.view_paths - end - end - - def view_paths=(value) - @view_paths = ActionView::Base.process_view_paths(value) if value - end - - # Adds a view_path to the front of the view_paths array. - # If the current class has no view paths, copy them from - # the superclass. This change will be visible for all future requests. - # - # ArticleController.prepend_view_path("views/default") - # ArticleController.prepend_view_path(["views/default", "views/custom"]) - # - def prepend_view_path(path) - @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil? - @view_paths.unshift(*path) + include ActionController::Caching + include ActionController::MimeResponds + + # Rails 2.x compatibility + include ActionController::Rails2Compatibility + + include ActionController::Cookies + include ActionController::Session + include ActionController::Flash + include ActionController::Verification + include ActionController::RequestForgeryProtection + include ActionController::Streaming + include ActionController::HttpAuthentication::Basic::ControllerMethods + include ActionController::HttpAuthentication::Digest::ControllerMethods + include ActionController::FilterParameterLogging + include ActionController::Translation + + # TODO: Extract into its own module + # This should be moved together with other normalizing behavior + module ImplicitRender + def send_action(*) + ret = super + default_render unless performed? + ret + end + + def default_render + render end - # Adds a view_path to the end of the view_paths array. - # If the current class has no view paths, copy them from - # the superclass. This change will be visible for all future requests. - # - # ArticleController.append_view_path("views/default") - # ArticleController.append_view_path(["views/default", "views/custom"]) - # - def append_view_path(path) - @view_paths = superclass.view_paths.dup if @view_paths.nil? - @view_paths.push(*path) - end - - @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] - - def exempt_from_layout(*types) - types.each do |type| - @@exempt_from_layout << - ActionView::Template.handler_class_for_extension(type) + def method_for_action(action_name) + super || begin + if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) + "default_render" + end end - - @@exempt_from_layout end - end - public - def call(env) - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - process(request, response).to_a - end - - # Extracts the action_name from the request parameters and performs that action. - def process(request, response, method = :perform_action, *arguments) #:nodoc: - response.request = request - - assign_shortcuts(request, response) - initialize_template_class(response) - initialize_current_url - - log_processing - send(method, *arguments) - - send_response - ensure - process_cleanup - end - - def send_response - response.prepare! - response - end - - # Returns a URL that has been rewritten according to the options hash and the defined routes. - # (For doing a complete redirect, use +redirect_to+). - # - # url_for is used to: - # - # All keys given to +url_for+ are forwarded to the Route module, save for the following: - # * :anchor - Specifies the anchor name to be appended to the path. For example, - # url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments' - # will produce "/posts/show/10#comments". - # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (false by default). - # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this - # is currently not recommended since it breaks caching. - # * :host - Overrides the default (current) host if provided. - # * :protocol - Overrides the default (current) protocol if provided. - # * :port - Optionally specify the port to connect to. - # * :user - Inline HTTP authentication (only plucked out if :password is also present). - # * :password - Inline HTTP authentication (only plucked out if :user is also present). - # * :skip_relative_url_root - If true, the url is not constructed using the +relative_url_root+ - # of the request so the path will include the web server relative installation directory. - # - # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the and a query string. - # Routes composes a query string as the key/value pairs not included in the . - # - # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with - # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs: - # - # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent' - # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts' - # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts' - # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10' - # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts' - # - # When generating a new URL, missing values may be filled in from the current request's parameters. For example, - # url_for :action => 'some_action' will retain the current controller, as expected. This behavior extends to - # other parameters, including :controller, :id, and any other parameters that are placed into a Route's - # path. - #   - # The URL helpers such as url_for have a limited form of memory: when generating a new URL, they can look for - # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be - # taken from the defaults. There are a few simple rules on how this is performed: - # - # * If the controller name begins with a slash no defaults are used: - # - # url_for :controller => '/home' - # - # In particular, a leading slash ensures no namespace is assumed. Thus, - # while url_for :controller => 'users' may resolve to - # Admin::UsersController if the current controller lives under - # that module, url_for :controller => '/users' ensures you link - # to ::UsersController no matter what. - # * If the controller changes, the action will default to index unless provided - # - # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the - # route given by map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'. - # - # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated - # from this page. - # - # * url_for :action => 'bio' -- During the generation of this URL, default values will be used for the first and - # last components, and the action shall change. The generated URL will be, "people/hh/david/bio". - # * url_for :first => 'davids-little-brother' This generates the URL 'people/hh/davids-little-brother' -- note - # that this URL leaves out the assumed action of 'bio'. - # - # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The - # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the - # value that appears in the slot for :first is not equal to default value for :first we stop using - # defaults. On its own, this rule can account for much of the typical Rails URL behavior. - #   - # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired. - # The default may be cleared by adding :name => nil to url_for's options. - # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the - # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is - # displayed on: - # - # url_for :controller => 'posts', :action => nil - # - # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the - # :overwrite_params options. Say for your posts you have different views for showing and printing them. - # Then, in the show view, you get the URL for the print view like this - # - # url_for :overwrite_params => { :action => 'print' } - # - # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print' - # would have slashed-off the path components after the changed action. - def url_for(options = {}) - options ||= {} - case options - when String - options - when Hash - @url.rewrite(rewrite_options(options)) - else - polymorphic_url(options) - end - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". - def controller_class_name - self.class.controller_class_name - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". - def controller_name - self.class.controller_name - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". - def controller_path - self.class.controller_path - end - - def session_enabled? - ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller) - end - - self.view_paths = [] - - # View load paths for controller. - def view_paths - @template.view_paths - end - - def view_paths=(value) - @template.view_paths = ActionView::Base.process_view_paths(value) - end - - # Adds a view_path to the front of the view_paths array. - # This change affects the current request only. - # - # self.prepend_view_path("views/default") - # self.prepend_view_path(["views/default", "views/custom"]) - # - def prepend_view_path(path) - @template.view_paths.unshift(*path) - end - - # Adds a view_path to the end of the view_paths array. - # This change affects the current request only. - # - # self.append_view_path("views/default") - # self.append_view_path(["views/default", "views/custom"]) - # - def append_view_path(path) - @template.view_paths.push(*path) - end - - def rewrite_options(options) #:nodoc: - if defaults = default_url_options(options) - defaults.merge(options) - else - options - end - end - - # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in - # the form of a hash, just like the one you would use for url_for directly. Example: - # - # def default_url_options(options) - # { :project => @project.active? ? @project.url_name : "unknown" } - # end - # - # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the - # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set - # by this method. - def default_url_options(options = nil) - 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, - # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # - # if stale?(:etag => @article, :last_modified => @article.created_at.utc) - # @statistics = @article.really_expensive_call - # respond_to do |format| - # # all the supported formats - # end - # end - # end - def stale?(options) - fresh_when(options) - !request.fresh?(response) - end - - # Sets the etag, last_modified, or both on the response and renders a - # "304 Not Modified" response if the request is already fresh. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) - # end - # - # This will render the show template if the request isn't sending a matching etag or - # If-Modified-Since header and just a "304 Not Modified" response if there's a match. - # - def fresh_when(options) - options.assert_valid_keys(:etag, :last_modified, :public) - - response.etag = options[:etag] if options[:etag] - response.last_modified = options[:last_modified] if options[:last_modified] - - if options[:public] - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - cache_control.delete("private") - cache_control.delete("no-cache") - cache_control << "public" - response.headers["Cache-Control"] = cache_control.join(', ') - end - - if request.fresh?(response) - head :not_modified - end - end - - # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that - # intermediate caches shouldn't cache the response. - # - # Examples: - # expires_in 20.minutes - # expires_in 3.hours, :public => true - # expires in 3.hours, 'max-stale' => 5.hours, :public => true - # - # This method will overwrite an existing Cache-Control header. - # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. - def expires_in(seconds, options = {}) #:doc: - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - - cache_control << "max-age=#{seconds}" - cache_control.delete("no-cache") - if options[:public] - cache_control.delete("private") - cache_control << "public" - else - cache_control << "private" - end - - # This allows for additional headers to be passed through like 'max-stale' => 5.hours - cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} - - response.headers["Cache-Control"] = cache_control.join(', ') - end - - # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or - # intermediate caches (like caching proxy servers). - def expires_now #:doc: - response.headers["Cache-Control"] = "no-cache" - end - - # Resets the session by clearing out all the objects stored within and initializing a new session object. - def reset_session #:doc: - request.reset_session - end - - private - def _process_options(options) - 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 - - response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE) - end + include ImplicitRender - def initialize_template_class(response) - @template = ActionView::Base.new(self.class.view_paths, {}, self, formats) - response.template = @template if response.respond_to?(:template=) - @template.helpers.send :include, self.class.master_helper_module - @performed_render = @performed_redirect = false - end - - def assign_shortcuts(request, response) - @_request, @_response, @_params = request, response, request.parameters - @_headers = @_response.headers - end + include ActionController::Rescue - def initialize_current_url - @url = UrlRewriter.new(request, params.clone) - end - - def log_processing - if logger && logger.info? - log_processing_for_request_id - log_processing_for_parameters - end - end - - def log_processing_for_request_id - request_id = "\n\nProcessing #{self.class.name}\##{action_name} " - request_id << "to #{params[:format]} " if params[:format] - request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]" + def self.inherited(klass) + ::ActionController::Base.subclasses << klass.to_s + super + end - logger.info(request_id) - end + def self.subclasses + @subclasses ||= [] + end - def default_render #:nodoc: - render + def self.app_loaded! + @subclasses.each do |subclass| + subclass.constantize._write_layout_method end + end - def perform_action - 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 - view_paths.find_by_parts(action_name, {:formats => formats, :locales => [I18n.locale]}, controller_path) - rescue => e - raise UnknownAction, "No action responded to #{action_name}. Actions: " + - "#{action_methods.sort.to_sentence}", caller + def _normalize_options(action = nil, options = {}, &blk) + if action.is_a?(Hash) + options, action = action, nil + elsif action.is_a?(String) || action.is_a?(Symbol) + key = case action = action.to_s + when %r{^/} then :file + when %r{/} then :template + else :action end - - default_render + options.merge! key => action + elsif action + options.merge! :partial => action end - # Returns true if a render or redirect has already been performed. - def performed? - @performed_render || @performed_redirect + if options.key?(:action) && options[:action].to_s.index("/") + options[:template] = options.delete(:action) end - def reset_variables_added_to_assigns - @template.instance_variable_set("@assigns_added", nil) + if options[:status] + options[:status] = interpret_status(options[:status]).to_i end - def request_origin - # this *needs* to be cached! - # otherwise you'd get different results if calling it more than once - @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" - end - - # Returns the request URI used to get to the current location - def complete_request_uri - "#{request.protocol}#{request.host}#{request.request_uri}" - end - - def default_template(action_name = self.action_name) - self.view_paths.find_template(default_template_name(action_name), default_template_format) - end - - def default_template_name(action_name = self.action_name) - if action_name - action_name = action_name.to_s - if action_name.include?('/') && template_path_includes_controller?(action_name) - action_name = strip_out_controller(action_name) - end - end - "#{controller_path}/#{action_name}" - end - - def strip_out_controller(path) - path.split('/', 2).last - end + options[:update] = blk if block_given? + options + end - def template_path_includes_controller?(path) - self.controller_path.split('/')[-1] == path.split('/')[0] - end + def render(action = nil, options = {}, &blk) + options = _normalize_options(action, options, &blk) + super(options) + end - def process_cleanup - end - end + def render_to_string(action = nil, options = {}, &blk) + options = _normalize_options(action, options, &blk) + super(options) + end - Base.class_eval do - [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers, - Cookies, Caching, Verification, Streaming, SessionManagement, - HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier, - RequestForgeryProtection, Translation, FilterParameterLogging - ].each do |mod| - include mod + # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: + # + # * Hash - The URL will be generated by calling url_for with the +options+. + # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. + # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. + # * String not containing a protocol - The current protocol and host is prepended to the string. + # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. + # Short-hand for redirect_to(request.env["HTTP_REFERER"]) + # + # 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 redirect_to :back, 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? + + status = if options.is_a?(Hash) && options.key?(:status) + interpret_status(options.delete(:status)) + elsif response_status.key?(:status) + interpret_status(response_status[:status]) + else + 302 + end + + url = 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+.-]*:.*} + options + when String + request.protocol + request.host_with_port + options + when :back + raise RedirectBackError unless refer = request.headers["Referer"] + refer + else + url_for(options) + end + + super(url, status) end end end diff --git a/actionpack/lib/action_controller/base/chained/benchmarking.rb b/actionpack/lib/action_controller/base/chained/benchmarking.rb deleted file mode 100644 index 57a1ac8314..0000000000 --- a/actionpack/lib/action_controller/base/chained/benchmarking.rb +++ /dev/null @@ -1,107 +0,0 @@ -require 'active_support/core_ext/benchmark' - -module ActionController #:nodoc: - # The benchmarking module times the performance of actions and reports to the logger. If the Active Record - # package has been included, a separate timing section for database calls will be added as well. - module Benchmarking #:nodoc: - def self.included(base) - base.extend(ClassMethods) - - base.class_eval do - alias_method_chain :perform_action, :benchmark - alias_method_chain :render, :benchmark - end - end - - module ClassMethods - # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it - # (unless use_silence is set to false). - # - # The benchmark is only recorded if the current level of the logger matches the log_level, which makes it - # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark - # will only be conducted if the log level is low enough. - def benchmark(title, log_level = Logger::DEBUG, use_silence = true) - if logger && logger.level >= log_level - result = nil - ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } - logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)") - result - else - yield - end - end - - # Silences the logger for the duration of the block. - def silence - old_logger_level, logger.level = logger.level, Logger::ERROR if logger - yield - ensure - logger.level = old_logger_level if logger - end - end - - protected - def render_with_benchmark(options = nil, extra_options = {}, &block) - if logger - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - db_runtime = ActiveRecord::Base.connection.reset_runtime - end - - render_output = nil - @view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) } - - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - @db_rt_before_render = db_runtime - @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime - @view_runtime -= @db_rt_after_render - end - - render_output - else - render_without_benchmark(options, extra_options, &block) - end - end - - private - def perform_action_with_benchmark - 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? - - log_message = 'Completed in %.0fms' % ms - - if logging_view || logging_active_record - log_message << " (" - log_message << view_runtime if logging_view - - if logging_active_record - log_message << ", " if logging_view - log_message << active_record_runtime + ")" - else - ")" - end - end - - log_message << " | #{response.status}" - log_message << " [#{complete_request_uri rescue "unknown"}]" - - logger.info(log_message) - response.headers["X-Runtime"] = "%.0f" % ms - else - perform_action_without_benchmark - end - end - - def view_runtime - "View: %.0f" % @view_runtime - end - - def active_record_runtime - db_runtime = ActiveRecord::Base.connection.reset_runtime - db_runtime += @db_rt_before_render if @db_rt_before_render - db_runtime += @db_rt_after_render if @db_rt_after_render - "DB: %.0f" % db_runtime - end - end -end diff --git a/actionpack/lib/action_controller/base/chained/filters.rb b/actionpack/lib/action_controller/base/chained/filters.rb deleted file mode 100644 index f528dd0686..0000000000 --- a/actionpack/lib/action_controller/base/chained/filters.rb +++ /dev/null @@ -1,670 +0,0 @@ -module ActionController #:nodoc: - module Filters #:nodoc: - extend ActiveSupport::Concern - - class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc: - def append_filter_to_chain(filters, filter_type, &block) - pos = find_filter_append_position(filters, filter_type) - update_filter_chain(filters, filter_type, pos, &block) - end - - def prepend_filter_to_chain(filters, filter_type, &block) - pos = find_filter_prepend_position(filters, filter_type) - update_filter_chain(filters, filter_type, pos, &block) - end - - def create_filters(filters, filter_type, &block) - filters, conditions = extract_options(filters, &block) - filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) } - filters - end - - def skip_filter_in_chain(*filters, &test) - filters, conditions = extract_options(filters) - filters.each do |filter| - if callback = find(filter) then delete(callback) end - end if conditions.empty? - update_filter_in_chain(filters, :skip => conditions, &test) - end - - private - def update_filter_chain(filters, filter_type, pos, &block) - new_filters = create_filters(filters, filter_type, &block) - insert(pos, new_filters).flatten! - end - - def find_filter_append_position(filters, filter_type) - # appending an after filter puts it at the end of the call chain - # before and around filters go before the first after filter in the chain - unless filter_type == :after - each_with_index do |f,i| - return i if f.after? - end - end - return -1 - end - - def find_filter_prepend_position(filters, filter_type) - # prepending a before or around filter puts it at the front of the call chain - # after filters go before the first after filter in the chain - if filter_type == :after - each_with_index do |f,i| - return i if f.after? - end - return -1 - end - return 0 - end - - def find_or_create_filter(filter, filter_type, options = {}) - update_filter_in_chain([filter], options) - - if found_filter = find(filter) { |f| f.type == filter_type } - found_filter - else - filter_kind = case - when filter.respond_to?(:before) && filter_type == :before - :before - when filter.respond_to?(:after) && filter_type == :after - :after - else - :filter - end - - case filter_type - when :before - BeforeFilter.new(filter_kind, filter, options) - when :after - AfterFilter.new(filter_kind, filter, options) - else - AroundFilter.new(filter_kind, filter, options) - end - end - end - - def update_filter_in_chain(filters, options, &test) - filters.map! { |f| block_given? ? find(f, &test) : find(f) } - filters.compact! - - map! do |filter| - if filters.include?(filter) - new_filter = filter.dup - new_filter.update_options!(options) - new_filter - else - filter - end - end - end - end - - class Filter < ActiveSupport::Callbacks::Callback #:nodoc: - def initialize(kind, method, options = {}) - super - update_options! options - end - - # override these to return true in appropriate subclass - def before? - false - end - - def after? - false - end - - def around? - false - end - - # Make sets of strings from :only/:except options - def update_options!(other) - if other - convert_only_and_except_options_to_sets_of_strings(other) - if other[:skip] - convert_only_and_except_options_to_sets_of_strings(other[:skip]) - end - end - - options.update(other) - end - - private - def should_not_skip?(controller) - if options[:skip] - !included_in_action?(controller, options[:skip]) - else - true - end - end - - def included_in_action?(controller, options) - if options[:only] - options[:only].include?(controller.action_name) - elsif options[:except] - !options[:except].include?(controller.action_name) - else - true - end - end - - def should_run_callback?(controller) - should_not_skip?(controller) && included_in_action?(controller, options) && super - end - - def convert_only_and_except_options_to_sets_of_strings(opts) - [:only, :except].each do |key| - if values = opts[key] - opts[key] = Array(values).map {|val| val.to_s }.to_set - end - end - end - end - - class AroundFilter < Filter #:nodoc: - def type - :around - end - - def around? - true - end - - def call(controller, &block) - if should_run_callback?(controller) - method = filter_responds_to_before_and_after? ? around_proc : self.method - - # For around_filter do |controller, action| - if method.is_a?(Proc) && method.arity == 2 - evaluate_method(method, controller, block) - else - evaluate_method(method, controller, &block) - end - else - block.call - end - end - - private - def filter_responds_to_before_and_after? - method.respond_to?(:before) && method.respond_to?(:after) - end - - def around_proc - Proc.new do |controller, action| - method.before(controller) - - if controller.__send__(:performed?) - controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) - else - begin - action.call - ensure - method.after(controller) - end - end - end - end - end - - class BeforeFilter < Filter #:nodoc: - def type - :before - end - - def before? - true - end - - def call(controller, &block) - super - if controller.__send__(:performed?) - controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) - end - end - end - - class AfterFilter < Filter #:nodoc: - def type - :after - end - - def after? - true - end - end - - # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do - # authentication, caching, or auditing before the intended action is performed. Or to do localization or output - # compression after the action has been performed. Filters have access to the request, response, and all the instance - # variables set by other filters in the chain or by the action (in the case of after filters). - # - # == Filter inheritance - # - # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without - # affecting the superclass. For example: - # - # class BankController < ActionController::Base - # before_filter :audit - # - # private - # def audit - # # record the action and parameters in an audit log - # end - # end - # - # class VaultController < BankController - # before_filter :verify_credentials - # - # private - # def verify_credentials - # # make sure the user is allowed into the vault - # end - # end - # - # Now any actions performed on the BankController will have the audit method called before. On the VaultController, - # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then - # verify_credentials and the intended action are never called. - # - # == Filter types - # - # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first - # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of - # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form. - # - # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes - # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example: - # - # class OutputCompressionFilter - # def self.filter(controller) - # controller.response.body = compress(controller.response.body) - # end - # end - # - # class NewspaperController < ActionController::Base - # after_filter OutputCompressionFilter - # end - # - # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can - # manipulate them as it sees fit. - # - # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation. - # Or just as a quick test. It works like this: - # - # class WeblogController < ActionController::Base - # before_filter { |controller| head(400) if controller.params["stop_action"] } - # end - # - # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables. - # This means that the block has access to both the request and response objects complete with convenience methods for params, - # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call - # and returns 1 or -1 on arity will do (such as a Proc or an Method object). - # - # Please note that around_filters function a little differently than the normal before and after filters with regard to filter - # types. Please see the section dedicated to around_filters below. - # - # == Filter chain ordering - # - # Using before_filter and after_filter appends the specified filters to the existing chain. That's usually - # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you - # can use prepend_before_filter and prepend_after_filter. Filters added by these methods will be put at the - # beginning of their respective chain and executed before the rest. For example: - # - # class ShoppingController < ActionController::Base - # before_filter :verify_open_shop - # - # class CheckoutController < ShoppingController - # prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock - # - # The filter chain for the CheckoutController is now :ensure_items_in_cart, :ensure_items_in_stock, - # :verify_open_shop. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop - # is open or not. - # - # You may pass multiple filter arguments of each type as well as a filter block. - # If a block is given, it is treated as the last argument. - # - # == Around filters - # - # Around filters wrap an action, executing code both before and after. - # They may be declared as method references, blocks, or objects responding - # to +filter+ or to both +before+ and +after+. - # - # To use a method as an +around_filter+, pass a symbol naming the Ruby method. - # Yield (or block.call) within the method to run the action. - # - # around_filter :catch_exceptions - # - # private - # def catch_exceptions - # yield - # rescue => exception - # logger.debug "Caught exception! #{exception}" - # raise - # end - # - # To use a block as an +around_filter+, pass a block taking as args both - # the controller and the action block. You can't call yield directly from - # an +around_filter+ block; explicitly call the action block instead: - # - # around_filter do |controller, action| - # logger.debug "before #{controller.action_name}" - # action.call - # logger.debug "after #{controller.action_name}" - # end - # - # To use a filter object with +around_filter+, pass an object responding - # to :filter or both :before and :after. With a - # filter method, yield to the block as above: - # - # around_filter BenchmarkingFilter - # - # class BenchmarkingFilter - # def self.filter(controller, &block) - # Benchmark.measure(&block) - # end - # end - # - # With +before+ and +after+ methods: - # - # around_filter Authorizer.new - # - # class Authorizer - # # This will run before the action. Redirecting aborts the action. - # def before(controller) - # unless user.authorized? - # redirect_to(login_url) - # end - # end - # - # # This will run after the action if and only if before did not render or redirect. - # def after(controller) - # end - # end - # - # If the filter has +before+ and +after+ methods, the +before+ method will be - # called before the action. If +before+ renders or redirects, the filter chain is - # halted and +after+ will not be run. See Filter Chain Halting below for - # an example. - # - # == Filter chain skipping - # - # Declaring a filter on a base class conveniently applies to its subclasses, - # but sometimes a subclass should skip some of its superclass' filters: - # - # class ApplicationController < ActionController::Base - # before_filter :authenticate - # around_filter :catch_exceptions - # end - # - # class WeblogController < ApplicationController - # # Will run the :authenticate and :catch_exceptions filters. - # end - # - # class SignupController < ApplicationController - # # Skip :authenticate, run :catch_exceptions. - # skip_before_filter :authenticate - # end - # - # class ProjectsController < ApplicationController - # # Skip :catch_exceptions, run :authenticate. - # skip_filter :catch_exceptions - # end - # - # class ClientsController < ApplicationController - # # Skip :catch_exceptions and :authenticate unless action is index. - # skip_filter :catch_exceptions, :authenticate, :except => :index - # end - # - # == Filter conditions - # - # Filters may be limited to specific actions by declaring the actions to - # include or exclude. Both options accept single actions - # (:only => :index) or arrays of actions - # (:except => [:foo, :bar]). - # - # class Journal < ActionController::Base - # # Require authentication for edit and delete. - # before_filter :authorize, :only => [:edit, :delete] - # - # # Passing options to a filter with a block. - # around_filter(:except => :index) do |controller, action_block| - # results = Profiler.run(&action_block) - # controller.response.sub! "", "#{results}" - # end - # - # private - # def authorize - # # Redirect to login unless authenticated. - # end - # end - # - # == Filter Chain Halting - # - # before_filter and around_filter may halt the request - # before a controller action is run. This is useful, for example, to deny - # access to unauthenticated users or to redirect from HTTP to HTTPS. - # Simply call render or redirect. After filters will not be executed if the filter - # chain is halted. - # - # Around filters halt the request unless the action block is called. - # Given these filters - # after_filter :after - # around_filter :around - # before_filter :before - # - # The filter chain will look like: - # - # ... - # . \ - # . #around (code before yield) - # . . \ - # . . #before (actual filter code is run) - # . . . \ - # . . . execute controller action - # . . . / - # . . ... - # . . / - # . #around (code after yield) - # . / - # #after (actual filter code is run, unless the around filter does not yield) - # - # If +around+ returns before yielding, +after+ will still not be run. The +before+ - # filter and controller action will not be run. If +before+ renders or redirects, - # the second half of +around+ and will still run but +after+ and the - # action will not. If +around+ fails to yield, +after+ will not be run. - module ClassMethods - # The passed filters will be appended to the filter_chain and - # will execute before the action on this controller is performed. - def append_before_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :before, &block) - end - - # The passed filters will be prepended to the filter_chain and - # will execute before the action on this controller is performed. - def prepend_before_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :before, &block) - end - - # Shorthand for append_before_filter since it's the most common. - alias :before_filter :append_before_filter - - # The passed filters will be appended to the array of filters - # that run _after_ actions on this controller are performed. - def append_after_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :after, &block) - end - - # The passed filters will be prepended to the array of filters - # that run _after_ actions on this controller are performed. - def prepend_after_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :after, &block) - end - - # Shorthand for append_after_filter since it's the most common. - alias :after_filter :append_after_filter - - # If you append_around_filter A.new, B.new, the filter chain looks like - # - # B#before - # A#before - # # run the action - # A#after - # B#after - # - # With around filters which yield to the action block, +before+ and +after+ - # are the code before and after the yield. - def append_around_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :around, &block) - end - - # If you prepend_around_filter A.new, B.new, the filter chain looks like: - # - # A#before - # B#before - # # run the action - # B#after - # A#after - # - # With around filters which yield to the action block, +before+ and +after+ - # are the code before and after the yield. - def prepend_around_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :around, &block) - end - - # Shorthand for +append_around_filter+ since it's the most common. - alias :around_filter :append_around_filter - - # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference - # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out - # of many sub-controllers need a different hierarchy. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_before_filter(*filters) - filter_chain.skip_filter_in_chain(*filters, &:before?) - end - - # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference - # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out - # of many sub-controllers need a different hierarchy. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_after_filter(*filters) - filter_chain.skip_filter_in_chain(*filters, &:after?) - end - - # Removes the specified filters from the filter chain. This only works for method reference (symbol) - # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that - # it will match any before, after or yielding around filter. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_filter(*filters) - filter_chain.skip_filter_in_chain(*filters) - end - - # Returns an array of Filter objects for this controller. - def filter_chain - read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new) - end - - # Returns all the before filters for this class and all its ancestors. - # This method returns the actual filter that was assigned in the controller to maintain existing functionality. - def before_filters #:nodoc: - filter_chain.select(&:before?).map(&:method) - end - - # Returns all the after filters for this class and all its ancestors. - # This method returns the actual filter that was assigned in the controller to maintain existing functionality. - def after_filters #:nodoc: - filter_chain.select(&:after?).map(&:method) - end - end - - module InstanceMethods # :nodoc: - def self.included(base) - base.class_eval do - alias_method_chain :perform_action, :filters - alias_method_chain :process, :filters - end - end - - protected - def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: - @before_filter_chain_aborted = false - process_without_filters(request, response, method, *arguments) - end - - def perform_action_with_filters - call_filters(self.class.filter_chain, 0, 0) - end - - private - def call_filters(chain, index, nesting) - index = run_before_filters(chain, index, nesting) - aborted = @before_filter_chain_aborted - perform_action_without_filters unless performed? || aborted - return index if nesting != 0 || aborted - run_after_filters(chain, index) - end - - def run_before_filters(chain, index, nesting) - while chain[index] - filter, index = chain[index], index - break unless filter # end of call chain reached - - case filter - when BeforeFilter - filter.call(self) # invoke before filter - index = index.next - break if @before_filter_chain_aborted - when AroundFilter - yielded = false - - filter.call(self) do - yielded = true - # all remaining before and around filters will be run in this call - index = call_filters(chain, index.next, nesting.next) - end - - halt_filter_chain(filter, :did_not_yield) unless yielded - - break - else - break # no before or around filters left - end - end - - index - end - - def run_after_filters(chain, index) - seen_after_filter = false - - while chain[index] - filter, index = chain[index], index - break unless filter # end of call chain reached - - case filter - when AfterFilter - seen_after_filter = true - filter.call(self) # invoke after filter - else - # implementation error or someone has mucked with the filter chain - raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter - end - - index = index.next - end - - index.next - end - - def halt_filter_chain(filter, reason) - @before_filter_chain_aborted = true - logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger - end - end - end -end diff --git a/actionpack/lib/action_controller/base/chained/flash.rb b/actionpack/lib/action_controller/base/chained/flash.rb deleted file mode 100644 index 42c6e430ca..0000000000 --- a/actionpack/lib/action_controller/base/chained/flash.rb +++ /dev/null @@ -1,196 +0,0 @@ -module ActionController #:nodoc: - # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed - # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create - # action that sets flash[:notice] = "Successfully created" before redirecting to a display action that can - # then expose the flash to its template. Actually, that exposure is automatically done. Example: - # - # class PostsController < ActionController::Base - # def create - # # save post - # flash[:notice] = "Successfully created post" - # redirect_to posts_path(@post) - # end - # - # def show - # # doesn't need to assign the flash notice to the template, that's done automatically - # end - # end - # - # show.html.erb - # <% if flash[:notice] %> - #
<%= flash[:notice] %>
- # <% end %> - # - # This example just places a string in the flash, but you can put any object in there. And of course, you can put as - # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed. - # - # See docs on the FlashHash class for more details about the flash. - module Flash - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - include Session if defined?(ActionController::Http) - - included do - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include InstanceMethodsForNewBase - else - include InstanceMethodsForBase - - alias_method_chain :perform_action, :flash - alias_method_chain :reset_session, :flash - end - end - - class FlashNow #:nodoc: - def initialize(flash) - @flash = flash - end - - def []=(k, v) - @flash[k] = v - @flash.discard(k) - v - end - - def [](k) - @flash[k] - end - end - - class FlashHash < Hash - def initialize #:nodoc: - super - @used = {} - end - - def []=(k, v) #:nodoc: - keep(k) - super - end - - def update(h) #:nodoc: - h.keys.each { |k| keep(k) } - super - end - - alias :merge! :update - - def replace(h) #:nodoc: - @used = {} - super - end - - # Sets a flash that will not be available to the next action, only to the current. - # - # flash.now[:message] = "Hello current action" - # - # This method enables you to use the flash as a central messaging system in your app. - # When you need to pass an object to the next action, you use the standard flash assign ([]=). - # When you need to pass an object to the current action, you use now, and your object will - # vanish when the current action is done. - # - # Entries set via now are accessed the same way as standard entries: flash['my-key']. - def now - FlashNow.new(self) - end - - # Keeps either the entire current flash or a specific flash entry available for the next action: - # - # flash.keep # keeps the entire flash - # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded - def keep(k = nil) - use(k, false) - end - - # Marks the entire flash or a single flash entry to be discarded by the end of the current action: - # - # flash.discard # discard the entire flash at the end of the current action - # flash.discard(:warning) # discard only the "warning" entry at the end of the current action - def discard(k = nil) - use(k) - end - - # Mark for removal entries that were kept, and delete unkept ones. - # - # This method is called automatically by filters, so you generally don't need to care about it. - def sweep #:nodoc: - keys.each do |k| - unless @used[k] - use(k) - else - delete(k) - @used.delete(k) - end - end - - # clean up after keys that could have been left over by calling reject! or shift on the flash - (@used.keys - keys).each{ |k| @used.delete(k) } - end - - def store(session, key = "flash") - return if self.empty? - session[key] = self - end - - private - # Used internally by the keep and discard methods - # use() # marks the entire flash as used - # use('msg') # marks the "msg" entry as used - # use(nil, false) # marks the entire flash as unused (keeps it around for one more action) - # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action) - # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself - # if no key is passed. - def use(key = nil, used = true) - Array(key || keys).each { |k| @used[k] = used } - return key ? self[key] : self - end - end - - module InstanceMethodsForBase #:nodoc: - protected - def perform_action_with_flash - perform_action_without_flash - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session_with_flash - reset_session_without_flash - remove_instance_variable(:@_flash) if defined?(@_flash) - end - end - - module InstanceMethodsForNewBase #:nodoc: - protected - def process_action(method_name) - super - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session - super - remove_instance_variable(:@_flash) if defined?(@_flash) - end - end - - protected - # Access the contents of the flash. Use flash["notice"] to - # read a notice you put there or flash["notice"] = "hello" - # to put a new one. - def flash #:doc: - if !defined?(@_flash) - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end - - @_flash - end - end -end diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/base/compatibility.rb new file mode 100644 index 0000000000..29ba43a879 --- /dev/null +++ b/actionpack/lib/action_controller/base/compatibility.rb @@ -0,0 +1,139 @@ +module ActionController + module Rails2Compatibility + extend ActiveSupport::Concern + + class ::ActionController::ActionControllerError < StandardError #:nodoc: + end + + # Temporary hax + included do + ::ActionController::UnknownAction = ::AbstractController::ActionNotFound + ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError + + cattr_accessor :session_options + self.session_options = {} + + cattr_accessor :allow_concurrency + self.allow_concurrency = false + + cattr_accessor :param_parsers + self.param_parsers = { Mime::MULTIPART_FORM => :multipart_form, + Mime::URL_ENCODED_FORM => :url_encoded_form, + Mime::XML => :xml_simple, + Mime::JSON => :json } + + cattr_accessor :relative_url_root + self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] + + cattr_accessor :default_charset + self.default_charset = "utf-8" + + # cattr_reader :protected_instance_variables + cattr_accessor :protected_instance_variables + self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller + @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params + @_flash @_response) + + # Indicates whether or not optimise the generated named + # route helper methods + cattr_accessor :optimise_named_routes + self.optimise_named_routes = true + + cattr_accessor :resources_path_names + self.resources_path_names = { :new => 'new', :edit => 'edit' } + + # Controls the resource action separator + cattr_accessor :resource_action_separator + self.resource_action_separator = "/" + + cattr_accessor :use_accept_header + self.use_accept_header = true + + cattr_accessor :page_cache_directory + self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" + + cattr_reader :cache_store + + cattr_accessor :consider_all_requests_local + self.consider_all_requests_local = true + + # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, + # and images to a dedicated asset server away from the main web server. Example: + # ActionController::Base.asset_host = "http://assets.example.com" + cattr_accessor :asset_host + + cattr_accessor :ip_spoofing_check + self.ip_spoofing_check = true + end + + # For old tests + def initialize_template_class(*) end + def assign_shortcuts(*) end + + # TODO: Remove this after we flip + def template + @template ||= _action_view + end + + def process_action(*) + template + super + end + + module ClassMethods + def consider_all_requests_local + end + + def rescue_action(env) + raise env["action_dispatch.rescue.exception"] + end + + # Defines the storage option for cached fragments + def cache_store=(store_option) + @@cache_store = ActiveSupport::Cache.lookup_store(store_option) + end + end + + def render_to_body(options) + if options.is_a?(Hash) && options.key?(:template) + options[:template].sub!(/^\//, '') + end + + options[:text] = nil if options[:nothing] == true + + body = super + body = [' '] if body.blank? + body + end + + def _handle_method_missing + method_missing(@_action_name.to_sym) + end + + def method_for_action(action_name) + super || (respond_to?(:method_missing) && "_handle_method_missing") + end + + def _find_by_parts(name, details) + details[:prefix] = nil if name =~ /\blayouts/ + super + end + + def performed? + response_body + end + + # ==== Request only view path switching ==== + def append_view_path(path) + view_paths.push(*path) + end + + def prepend_view_path(path) + view_paths.unshift(*path) + end + + def view_paths + _action_view.view_paths + end + end +end diff --git a/actionpack/lib/action_controller/base/conditional_get.rb b/actionpack/lib/action_controller/base/conditional_get.rb new file mode 100644 index 0000000000..d287ec4994 --- /dev/null +++ b/actionpack/lib/action_controller/base/conditional_get.rb @@ -0,0 +1,133 @@ +module ActionController + module ConditionalGet + extend ActiveSupport::Concern + + include RackConvenience + + # Sets the etag, last_modified, or both on the response and renders a + # "304 Not Modified" response if the request is already fresh. + # + # Parameters: + # * :etag + # * :last_modified + # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) + # end + # + # This will render the show template if the request isn't sending a matching etag or + # If-Modified-Since header and just a "304 Not Modified" response if there's a match. + # + def fresh_when(options) + options.assert_valid_keys(:etag, :last_modified, :public) + + response.etag = options[:etag] if options[:etag] + response.last_modified = options[:last_modified] if options[:last_modified] + + if options[:public] + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + cache_control.delete("private") + cache_control.delete("no-cache") + cache_control << "public" + response.headers["Cache-Control"] = cache_control.join(', ') + end + + if request.fresh?(response) + head :not_modified + end + 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 = 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 + + # 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, + # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. + # + # Parameters: + # * :etag + # * :last_modified + # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(:etag => @article, :last_modified => @article.created_at.utc) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + def stale?(options) + fresh_when(options) + !request.fresh?(response) + end + + # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that + # intermediate caches shouldn't cache the response. + # + # Examples: + # expires_in 20.minutes + # expires_in 3.hours, :public => true + # expires in 3.hours, 'max-stale' => 5.hours, :public => true + # + # This method will overwrite an existing Cache-Control header. + # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. + def expires_in(seconds, options = {}) #:doc: + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + + cache_control << "max-age=#{seconds}" + cache_control.delete("no-cache") + if options[:public] + cache_control.delete("private") + cache_control << "public" + else + cache_control << "private" + end + + # This allows for additional headers to be passed through like 'max-stale' => 5.hours + cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} + + response.headers["Cache-Control"] = cache_control.join(', ') + end + + # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or + # intermediate caches (like caching proxy servers). + def expires_now #:doc: + response.headers["Cache-Control"] = "no-cache" + end + end +end diff --git a/actionpack/lib/action_controller/base/cookies.rb b/actionpack/lib/action_controller/base/cookies.rb deleted file mode 100644 index d4806623c3..0000000000 --- a/actionpack/lib/action_controller/base/cookies.rb +++ /dev/null @@ -1,94 +0,0 @@ -module ActionController #:nodoc: - # Cookies are read and written through ActionController#cookies. - # - # The cookies being read are the ones received along with the request, the cookies - # being written will be sent out with the response. Reading a cookie does not get - # the cookie object itself back, just the value it holds. - # - # Examples for writing: - # - # # Sets a simple session cookie. - # cookies[:user_name] = "david" - # - # # Sets a cookie that expires in 1 hour. - # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now } - # - # Examples for reading: - # - # cookies[:user_name] # => "david" - # cookies.size # => 2 - # - # Example for deleting: - # - # cookies.delete :user_name - # - # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie: - # - # cookies[:key] = { - # :value => 'a yummy cookie', - # :expires => 1.year.from_now, - # :domain => 'domain.com' - # } - # - # cookies.delete(:key, :domain => 'domain.com') - # - # The option symbols for setting cookies are: - # - # * :value - The cookie's value or list of values (as an array). - # * :path - The path for which this cookie applies. Defaults to the root - # of the application. - # * :domain - The domain for which this cookie applies. - # * :expires - The time at which this cookie expires, as a Time object. - # * :secure - Whether this cookie is a only transmitted to HTTPS servers. - # Default is +false+. - # * :httponly - Whether this cookie is accessible via scripting or - # only HTTP. Defaults to +false+. - module Cookies - def self.included(base) - base.helper_method :cookies - end - - protected - # Returns the cookie container, which operates as described above. - def cookies - @cookies ||= CookieJar.new(self) - end - end - - class CookieJar < Hash #:nodoc: - def initialize(controller) - @controller, @cookies = controller, controller.request.cookies - super() - update(@cookies) - end - - # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. - def [](name) - super(name.to_s) - end - - # Sets the cookie named +name+. The second argument may be the very cookie - # value, or a hash of options as documented above. - def []=(key, options) - if options.is_a?(Hash) - options.symbolize_keys! - else - options = { :value => options } - end - - options[:path] = "/" unless options.has_key?(:path) - super(key.to_s, options[:value]) - @controller.response.set_cookie(key, options) - end - - # Removes the cookie on the client machine by setting the value to an empty string - # and setting its expiration date into the past. Like []=, you can pass in - # an options hash to delete cookies with extra data such as a :path. - def delete(key, options = {}) - options.symbolize_keys! - options[:path] = "/" unless options.has_key?(:path) - super(key.to_s) - @controller.response.delete_cookie(key, options) - end - end -end diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb deleted file mode 100644 index 8370ba6fc0..0000000000 --- a/actionpack/lib/action_controller/base/filter_parameter_logging.rb +++ /dev/null @@ -1,95 +0,0 @@ -module ActionController - module FilterParameterLogging - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Logger - end - - included do - include InstanceMethodsForNewBase - end - - module ClassMethods - # Replace sensitive parameter data from the request log. - # Filters parameters that have any of the arguments as a substring. - # Looks in all subhashes of the param hash for keys to filter. - # If a block is given, each key and value of the parameter hash and all - # subhashes is passed to it, the value or key - # can be replaced using String#replace or similar method. - # - # Examples: - # filter_parameter_logging - # => Does nothing, just slows the logging process down - # - # filter_parameter_logging :password - # => replaces the value to all keys matching /password/i with "[FILTERED]" - # - # filter_parameter_logging :foo, "bar" - # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - # - # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i - # - # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i, and - # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - def filter_parameter_logging(*filter_words, &block) - parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 - - define_method(:filter_parameters) do |unfiltered_parameters| - filtered_parameters = {} - - unfiltered_parameters.each do |key, value| - if key =~ parameter_filter - filtered_parameters[key] = '[FILTERED]' - elsif value.is_a?(Hash) - filtered_parameters[key] = filter_parameters(value) - elsif block_given? - key = key.dup - value = value.dup if value - yield key, value - filtered_parameters[key] = value - else - filtered_parameters[key] = value - end - end - - filtered_parameters - end - protected :filter_parameters - end - end - - module InstanceMethodsForNewBase - # TODO : Fix the order of information inside such that it's exactly same as the old base - def process(*) - ret = super - - if logger - parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup - parameters = parameters.except!(:controller, :action, :format, :_method, :only_path) - - unless parameters.empty? - # TODO : Move DelayedLog to AS - log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" } - logger.info(log) - end - end - - ret - end - end - - private - - # TODO : This method is not needed for the new base - def log_processing_for_parameters - parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup - parameters = parameters.except!(:controller, :action, :format, :_method) - - logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? - end - end -end diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index f74158bc13..2fa5ea6519 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb @@ -1,85 +1,80 @@ +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/name_error' require 'active_support/dependencies' -# FIXME: helper { ... } is broken on Ruby 1.9 -module ActionController #:nodoc: - module Helpers #:nodoc: +module ActionController + # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, + # +numbers+ and model objects, to name a few. These helpers are available to all templates + # by default. + # + # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to + # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will + # include a helper whose name matches that of the controller, e.g., MyController will automatically + # include MyHelper. + # + # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any + # controller which inherits from it. + # + # ==== Examples + # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if + # the Time object is blank: + # + # module FormattedTimeHelper + # def format_time(time, format=:long, blank_message=" ") + # time.blank? ? blank_message : time.to_s(format) + # end + # end + # + # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: + # + # class EventsController < ActionController::Base + # helper FormattedTimeHelper + # def index + # @events = Event.find(:all) + # end + # end + # + # Then, in any view rendered by EventController, the format_time method can be called: + # + # <% @events.each do |event| -%> + #

+ # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> + #

+ # <% end -%> + # + # Finally, assuming we have two event instances, one which has a time and one which does not, + # the output might look like this: + # + # 23 Aug 11:30 | Carolina Railhawks Soccer Match + # N/A | Carolina Railhaws Training Workshop + # + module Helpers extend ActiveSupport::Concern - included do - # Initialize the base module to aggregate its helpers. - class_inheritable_accessor :master_helper_module - self.master_helper_module = Module.new + include AbstractController::Helpers + included do # Set the default directory for helpers - class_inheritable_accessor :helpers_dir - self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - - class << self - alias_method_chain :inherited, :helper + extlib_inheritable_accessor(:helpers_dir) do + defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers" end end - # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, - # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates - # by default. - # - # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to - # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will - # include a helper whose name matches that of the controller, e.g., MyController will automatically - # include MyHelper. - # - # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any - # controller which inherits from it. - # - # ==== Examples - # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if - # the Time object is blank: - # - # module FormattedTimeHelper - # def format_time(time, format=:long, blank_message=" ") - # time.blank? ? blank_message : time.to_s(format) - # end - # end - # - # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: - # - # class EventsController < ActionController::Base - # helper FormattedTimeHelper - # def index - # @events = Event.find(:all) - # end - # end - # - # Then, in any view rendered by EventController, the format_time method can be called: - # - # <% @events.each do |event| -%> - #

- # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> - #

- # <% end -%> - # - # Finally, assuming we have two event instances, one which has a time and one which does not, - # the output might look like this: - # - # 23 Aug 11:30 | Carolina Railhawks Soccer Match - # N/A | Carolina Railhaws Training Workshop - # module ClassMethods - # Makes all the (instance) methods in the helper module available to templates rendered through this controller. - # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules - # available to the templates. - def add_template_helper(helper_module) #:nodoc: - master_helper_module.module_eval { include helper_module } + def inherited(klass) + klass.class_eval { default_helper_module! unless name.blank? } + super end # The +helper+ class method can take a series of helper module names, a block, or both. # - # * *args: One or more modules, strings or symbols, or the special symbol :all. - # * &block: A block defining helper methods. - # + # ==== Parameters + # *args + # block:: A block defining helper methods + # # ==== Examples - # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file - # and include the module in the template class. The second form illustrates how to include custom helpers + # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file + # and include the module in the template class. The second form illustrates how to include custom helpers # when working with namespaced controllers, or other cases where the file containing the helper definition is not # in one of Rails' standard load paths: # helper :foo # => requires 'foo_helper' and includes FooHelper @@ -92,78 +87,23 @@ module ActionController #:nodoc: # ActionController::Base.helpers_dir (defaults to app/helpers/**/*.rb under RAILS_ROOT). # helper :all # - # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available + # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available # to the template. # # One line # helper { def hello() "Hello, world!" end } # # Multi-line # helper do - # def foo(bar) - # "#{bar} is the very best" + # def foo(bar) + # "#{bar} is the very best" # end # end - # + # # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of # +symbols+, +strings+, +modules+ and blocks. # helper(:three, BlindHelper) { def mice() 'mice' end } # def helper(*args, &block) - args.flatten.each do |arg| - case arg - when Module - add_template_helper(arg) - when :all - helper(all_application_helpers) - when String, Symbol - file_name = arg.to_s.underscore + '_helper' - class_name = file_name.camelize - - begin - require_dependency(file_name) - rescue LoadError => load_error - requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] - if requiree == file_name - msg = "Missing helper file helpers/#{file_name}.rb" - raise LoadError.new(msg).copy_blame!(load_error) - else - raise - end - end - - add_template_helper(class_name.constantize) - else - raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})" - end - end - - # Evaluate block in template class if given. - master_helper_module.module_eval(&block) if block_given? - end - - # Declare a controller method as a helper. For example, the following - # makes the +current_user+ controller method available to the view: - # class ApplicationController < ActionController::Base - # helper_method :current_user, :logged_in? - # - # def current_user - # @current_user ||= User.find_by_id(session[:user]) - # end - # - # def logged_in? - # current_user != nil - # end - # end - # - # In a view: - # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> - def helper_method(*methods) - methods.flatten.each do |method| - master_helper_module.module_eval <<-end_eval - def #{method}(*args, &block) # def current_user(*args, &block) - controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block) - end # end - end_eval - end + super(*_modules_for_helpers(args), &block) end # Declares helper accessors for controller attributes. For example, the @@ -171,51 +111,68 @@ module ActionController #:nodoc: # controller and makes them available to the view: # helper_attr :name # attr_accessor :name + # + # ==== Parameters + # *attrs:: Names of attributes to be converted + # into helpers. def helper_attr(*attrs) attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } end # Provides a proxy to access helpers methods from outside the view. def helpers - unless @helper_proxy - @helper_proxy = ActionView::Base.new - @helper_proxy.extend master_helper_module - else - @helper_proxy - end + @helper_proxy ||= ActionView::Base.new.extend(_helpers) end - private - def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize + private + # Returns a list of modules, normalized from the acceptable kinds of + # helpers with the following behavior: + # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", + # and "foo_bar_helper.rb" is loaded using require_dependency. + # :all:: Loads all modules in the #helpers_dir + # Module:: No further processing + # + # After loading the appropriate files, the corresponding modules + # are returned. + # + # ==== Parameters + # args:: A list of helpers + # + # ==== Returns + # Array[Module]:: A normalized list of modules for the list of + # helpers provided. + def _modules_for_helpers(args) + args.flatten.map! do |arg| + case arg + when :all + _modules_for_helpers all_application_helpers + when String, Symbol + file_name = "#{arg.to_s.underscore}_helper" + require_dependency(file_name, "Missing helper file helpers/%s.rb") + file_name.camelize.constantize + when Module + arg + else + raise ArgumentError, "helper must be a String, Symbol, or Module" end - rescue MissingSourceFile => e - raise unless e.is_missing? module_path - rescue NameError => e - raise unless e.missing_name? module_name end + end - def inherited_with_helper(child) - inherited_without_helper(child) - - begin - child.master_helper_module = Module.new - child.master_helper_module.__send__ :include, master_helper_module - child.__send__ :default_helper_module! - rescue MissingSourceFile => e - raise unless e.is_missing?("helpers/#{child.controller_path}_helper") - end - end + def default_helper_module! + module_name = name.sub(/Controller$/, '') + module_path = module_name.underscore + helper module_path + rescue MissingSourceFile => e + raise e unless e.is_missing? "#{module_path}_helper" + rescue NameError => e + raise e unless e.missing_name? "#{module_name}Helper" + end - # Extract helper names from files in app/helpers/**/*.rb - def all_application_helpers - extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ - Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } - end + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end end end end diff --git a/actionpack/lib/action_controller/base/hide_actions.rb b/actionpack/lib/action_controller/base/hide_actions.rb new file mode 100644 index 0000000000..af68c772b1 --- /dev/null +++ b/actionpack/lib/action_controller/base/hide_actions.rb @@ -0,0 +1,35 @@ +module ActionController + # ActionController::HideActions adds the ability to prevent public methods on a controller + # to be called as actions. + module HideActions + extend ActiveSupport::Concern + + included do + extlib_inheritable_accessor(:hidden_actions) { Set.new } + end + + private + + # Overrides AbstractController::Base#action_method? to return false if the + # action name is in the list of hidden actions. + def action_method?(action_name) + !hidden_actions.include?(action_name) && super + end + + module ClassMethods + # Sets all of the actions passed in as hidden actions. + # + # ==== Parameters + # *args<#to_s>:: A list of actions + def hide_action(*args) + hidden_actions.merge(args.map! {|a| a.to_s }) + end + + # Overrides AbstractController::Base#action_methods to remove any methods + # that are listed as hidden methods. + def action_methods + @action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)}) + end + end + end +end diff --git a/actionpack/lib/action_controller/base/http.rb b/actionpack/lib/action_controller/base/http.rb new file mode 100644 index 0000000000..2e73561f93 --- /dev/null +++ b/actionpack/lib/action_controller/base/http.rb @@ -0,0 +1,100 @@ +require 'action_controller/abstract' +require 'active_support/core_ext/module/delegation' + +module ActionController + # ActionController::Http provides a way to get a valid Rack application from a controller. + # + # In AbstractController, dispatching is triggered directly by calling #process on a new controller. + # ActionController::Http provides an #action method that returns a valid Rack application for a + # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, + # can dispatch directly to the action returned by FooController.action(:index). + class Http < AbstractController::Base + abstract! + + # :api: public + attr_internal :params, :env + + # Returns the last part of the controller's name, underscored, without the ending + # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for + # controller_name + # + # ==== Returns + # String + def self.controller_name + @controller_name ||= controller_path.split("/").last + end + + # Delegates to the class' #controller_name + def controller_name + self.class.controller_name + end + + # Returns the full controller name, underscored, without the ending Controller. + # For instance, MyApp::MyPostsController would return "my_app/my_posts" for + # controller_name. + # + # ==== Returns + # String + def self.controller_path + @controller_path ||= self.name.sub(/Controller$/, '').underscore + end + + # Delegates to the class' #controller_path + def controller_path + self.class.controller_path + end + + # The details below can be overridden to support a specific + # Request and Response object. The default ActionController::Base + # implementation includes RackConvenience, which makes a request + # and response object available. You might wish to control the + # environment and response manually for performance reasons. + + attr_internal :status, :headers, :content_type + + def initialize(*) + @_headers = {} + super + end + + # Basic implementations for content_type=, location=, and headers are + # provided to reduce the dependency on the RackConvenience module + # in Renderer and Redirector. + + def content_type=(type) + headers["Content-Type"] = type.to_s + end + + def location=(url) + headers["Location"] = url + end + + # :api: private + def call(name, env) + @_env = env + process(name) + to_rack + end + + # :api: private + def to_rack + [status, headers, response_body] + end + + # Return a rack endpoint for the given action. Memoize the endpoint, so + # multiple calls into MyController.action will return the same object + # for the same action. + # + # ==== Parameters + # action<#to_s>:: An action name + # + # ==== Returns + # Proc:: A rack application + def self.action(name) + @actions ||= {} + @actions[name.to_s] ||= proc do |env| + new.call(name, env) + end + end + end +end diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb deleted file mode 100644 index 2519f55269..0000000000 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ /dev/null @@ -1,309 +0,0 @@ -require 'active_support/base64' - -module ActionController - module HttpAuthentication - # Makes it dead easy to do HTTP Basic authentication. - # - # Simple Basic example: - # - # class PostsController < ApplicationController - # USER_NAME, PASSWORD = "dhh", "secret" - # - # before_filter :authenticate, :except => [ :index ] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_basic do |user_name, password| - # user_name == USER_NAME && password == PASSWORD - # end - # end - # end - # - # - # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, - # the regular HTML interface is protected by a session approach: - # - # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate - # - # protected - # def set_account - # @account = Account.find_by_url_name(request.subdomains.first) - # end - # - # def authenticate - # case request.format - # when Mime::XML, Mime::ATOM - # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } - # @current_user = user - # else - # request_http_basic_authentication - # end - # else - # if session_authenticated? - # @current_user = @account.users.find(session[:authenticated][:user_id]) - # else - # redirect_to(login_url) and return false - # end - # end - # end - # end - # - # In your integration tests, you can do something like this: - # - # def test_access_granted_from_xml - # get( - # "/notes/1.xml", nil, - # :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) - # ) - # - # assert_equal 200, status - # end - # - # Simple Digest example: - # - # require 'digest/md5' - # class PostsController < ApplicationController - # REALM = "SuperSecret" - # USERS = {"dhh" => "secret", #plain text password - # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password - # - # before_filter :authenticate, :except => [:index] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_digest(REALM) do |username| - # USERS[username] - # end - # end - # end - # - # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately - # hash to check the user's credentials. Returning +nil+ will cause authentication to fail. - # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If - # the password file or database is compromised, the attacker would be able to use the ha1 hash to - # authenticate as the user at this +realm+, but would not have the user's password to try using at - # other sites. - # - # On shared hosts, Apache sometimes doesn't pass authentication headers to - # FCGI instances. If your environment matches this description and you cannot - # authenticate, try this rule in your Apache setup: - # - # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] - module Basic - extend self - - module ControllerMethods - def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) - authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) - end - - def authenticate_with_http_basic(&login_procedure) - HttpAuthentication::Basic.authenticate(self, &login_procedure) - end - - def request_http_basic_authentication(realm = "Application") - HttpAuthentication::Basic.authentication_request(self, realm) - end - end - - def authenticate(controller, &login_procedure) - unless authorization(controller.request).blank? - login_procedure.call(*user_name_and_password(controller.request)) - end - end - - def user_name_and_password(request) - decode_credentials(request).split(/:/, 2) - end - - def authorization(request) - request.env['HTTP_AUTHORIZATION'] || - request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] || - request.env['REDIRECT_X_HTTP_AUTHORIZATION'] - end - - def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') - end - - def encode_credentials(user_name, password) - "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}" - end - - def authentication_request(controller, realm) - controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}") - controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized - end - end - - module Digest - extend self - - module ControllerMethods - def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) - authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) - end - - # Authenticate with HTTP Digest, returns true or false - def authenticate_with_http_digest(realm = "Application", &password_procedure) - HttpAuthentication::Digest.authenticate(self, realm, &password_procedure) - end - - # Render output including the HTTP Digest authentication header - def request_http_digest_authentication(realm = "Application", message = nil) - HttpAuthentication::Digest.authentication_request(self, realm, message) - end - end - - # Returns false on a valid response, true otherwise - def authenticate(controller, realm, &password_procedure) - authorization(controller.request) && validate_digest_response(controller.request, realm, &password_procedure) - end - - def authorization(request) - request.env['HTTP_AUTHORIZATION'] || - request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] || - request.env['REDIRECT_X_HTTP_AUTHORIZATION'] - end - - # Returns false unless the request credentials response value matches the expected value. - # First try the password as a ha1 digest password. If this fails, then try it as a plain - # text password. - def validate_digest_response(request, realm, &password_procedure) - credentials = decode_credentials_header(request) - valid_nonce = validate_nonce(request, credentials[:nonce]) - - if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] - password = password_procedure.call(credentials[:username]) - return false unless password - - method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] - - [true, false].any? do |password_is_ha1| - expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) - expected == credentials[:response] - end - end - end - - # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ - # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead - # of a plain-text password. - def expected_response(http_method, uri, credentials, password, password_is_ha1=true) - ha1 = password_is_ha1 ? password : ha1(credentials, password) - ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) - ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) - end - - def ha1(credentials, password) - ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) - end - - def encode_credentials(http_method, credentials, password, password_is_ha1) - credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) - "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') - end - - def decode_credentials_header(request) - decode_credentials(authorization(request)) - end - - def decode_credentials(header) - header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| - key, value = pair.split('=', 2) - hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') - hash - end - end - - def authentication_header(controller, realm) - controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") - end - - def authentication_request(controller, realm, message = nil) - message ||= "HTTP Digest: Access denied.\n" - authentication_header(controller, realm) - controller.__send__ :render, :text => message, :status => :unauthorized - end - - # Uses an MD5 digest based on time to generate a value to be used only once. - # - # A server-specified data string which should be uniquely generated each time a 401 response is made. - # It is recommended that this string be base64 or hexadecimal data. - # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. - # - # The contents of the nonce are implementation dependent. - # The quality of the implementation depends on a good choice. - # A nonce might, for example, be constructed as the base 64 encoding of - # - # => time-stamp H(time-stamp ":" ETag ":" private-key) - # - # where time-stamp is a server-generated time or other non-repeating value, - # ETag is the value of the HTTP ETag header associated with the requested entity, - # and private-key is data known only to the server. - # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and - # reject the request if it did not match the nonce from that header or - # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. - # The inclusion of the ETag prevents a replay request for an updated version of the resource. - # (Note: including the IP address of the client in the nonce would appear to offer the server the ability - # to limit the reuse of the nonce to the same client that originally got it. - # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. - # Also, IP address spoofing is not that hard.) - # - # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to - # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for - # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 - # of this document. - # - # The nonce is opaque to the client. Composed of Time, and hash of Time with secret - # key from the Rails session secret generated upon creation of project. Ensures - # the time cannot be modifed by client. - def nonce(time = Time.now) - t = time.to_i - hashed = [t, secret_key] - digest = ::Digest::MD5.hexdigest(hashed.join(":")) - ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') - end - - # Might want a shorter timeout depending on whether the request - # is a PUT or POST, and if client is browser or web service. - # Can be much shorter if the Stale directive is implemented. This would - # allow a user to use new nonce without prompting user again for their - # username and password. - def validate_nonce(request, value, seconds_to_timeout=5*60) - t = ActiveSupport::Base64.decode64(value).split(":").first.to_i - nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout - end - - # Opaque based on random generation - but changing each request? - def opaque() - ::Digest::MD5.hexdigest(secret_key) - end - - # Set in /initializers/session_store.rb, and loaded even if sessions are not in use. - def secret_key - ActionController::Base.session_options[:secret] - end - - end - end -end diff --git a/actionpack/lib/action_controller/base/layout.rb b/actionpack/lib/action_controller/base/layout.rb deleted file mode 100644 index cf5f46a32b..0000000000 --- a/actionpack/lib/action_controller/base/layout.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/class/delegating_attributes' -require 'active_support/core_ext/class/inheritable_attributes' - -module ActionController #:nodoc: - module Layout #:nodoc: - def self.included(base) - base.extend(ClassMethods) - 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 - # repeated setups. The inclusion pattern has pages that look like this: - # - # <%= render "shared/header" %> - # Hello World - # <%= render "shared/footer" %> - # - # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose - # and if you ever want to change the structure of these two includes, you'll have to change all the templates. - # - # With layouts, you can flip it around and have the common structure know where to insert changing content. This means - # that the header and footer are only mentioned in one place, like this: - # - # // The header part of this layout - # <%= yield %> - # // The footer part of this layout - # - # And then you have content pages that look like this: - # - # hello world - # - # At rendering time, the content page is computed and then inserted in the layout, like this: - # - # // The header part of this layout - # hello world - # // The footer part of this layout - # - # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance - # variable. The preferred notation now is to use yield, as documented above. - # - # == Accessing shared variables - # - # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with - # references that won't materialize before rendering time: - # - #

<%= @page_title %>

- # <%= yield %> - # - # ...and content pages that fulfill these references _at_ rendering time: - # - # <% @page_title = "Welcome" %> - # Off-world colonies offers you a chance to start a new life - # - # The result after rendering is: - # - #

Welcome

- # Off-world colonies offers you a chance to start a new life - # - # == Automatic layout assignment - # - # If there is a template in app/views/layouts/ with the same name as the current controller then it will be automatically - # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named - # app/views/layouts/weblog.erb or app/views/layouts/weblog.builder exists then it will be automatically set as - # the layout for your WeblogController. You can create a layout with the name application.erb or application.builder - # and this will be set as the default controller if there is no layout with the same name as the current controller and there is - # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout. - # assignment. So an Admin::WeblogController will look for a template named app/views/layouts/admin/weblog.erb. - # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set. - # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child - # class has a layout with the same name. - # - # == Inheritance for layouts - # - # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples: - # - # class BankController < ActionController::Base - # layout "bank_standard" - # - # class InformationController < BankController - # - # class VaultController < BankController - # layout :access_level_layout - # - # class EmployeeController < BankController - # layout nil - # - # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites - # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. - # - # == Types of layouts - # - # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes - # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can - # be done either by specifying a method reference as a symbol or using an inline method (as a proc). - # - # The method reference is the preferred approach to variable layouts and is used like this: - # - # class WeblogController < ActionController::Base - # layout :writers_and_readers - # - # def index - # # fetching posts - # end - # - # private - # def writers_and_readers - # logged_in? ? "writer_layout" : "reader_layout" - # end - # - # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing - # is logged in or not. - # - # If you want to use an inline method, such as a proc, do something like this: - # - # class WeblogController < ActionController::Base - # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } - # - # Of course, the most common way of specifying a layout is still just as a plain template name: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. - # Otherwise, it will be looked up relative to the template root. - # - # == Conditional layouts - # - # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering - # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The - # :only and :except options can be passed to the layout call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard", :except => :rss - # - # # ... - # - # end - # - # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout - # around the rendered view. - # - # Both the :only and :except condition can accept an arbitrary number of method references, so - # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. - # - # == Using a different layout in the action render call - # - # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. - # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. - # You can do this by passing a :layout option to the render call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # def help - # render :action => "help", :layout => "help" - # end - # end - # - # 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 yields. 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) - self.layout_name = template_name - end - - 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(*args) - memoized_default_layout(*args) - @_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new - @_memoized_default_layout[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 => formats}, prefix) - end - - def find_layout(*args) - @_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new - @_memoized_find_layout[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 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 add_layout_conditions(conditions) - # :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 - - 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 - - self.class.find_layout(layout_name, formats) - end - - def _pick_layout(layout_name = nil, 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 - if only = conditions[:only] - return only.include?(action_name) - elsif except = conditions[:except] - return !except.include?(action_name) - end - end - true - end - - end -end diff --git a/actionpack/lib/action_controller/base/layouts.rb b/actionpack/lib/action_controller/base/layouts.rb new file mode 100644 index 0000000000..ace4b148c9 --- /dev/null +++ b/actionpack/lib/action_controller/base/layouts.rb @@ -0,0 +1,195 @@ +module ActionController + # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in + # repeated setups. The inclusion pattern has pages that look like this: + # + # <%= render "shared/header" %> + # Hello World + # <%= render "shared/footer" %> + # + # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose + # and if you ever want to change the structure of these two includes, you'll have to change all the templates. + # + # With layouts, you can flip it around and have the common structure know where to insert changing content. This means + # that the header and footer are only mentioned in one place, like this: + # + # // The header part of this layout + # <%= yield %> + # // The footer part of this layout + # + # And then you have content pages that look like this: + # + # hello world + # + # At rendering time, the content page is computed and then inserted in the layout, like this: + # + # // The header part of this layout + # hello world + # // The footer part of this layout + # + # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance + # variable. The preferred notation now is to use yield, as documented above. + # + # == Accessing shared variables + # + # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with + # references that won't materialize before rendering time: + # + #

<%= @page_title %>

+ # <%= yield %> + # + # ...and content pages that fulfill these references _at_ rendering time: + # + # <% @page_title = "Welcome" %> + # Off-world colonies offers you a chance to start a new life + # + # The result after rendering is: + # + #

Welcome

+ # Off-world colonies offers you a chance to start a new life + # + # == Layout assignment + # + # You can either specify a layout declaratively (using the #layout class method) or give + # it the same name as your controller, and place it in app/views/layouts. + # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance. + # + # For instance, if you have PostsController and a template named app/views/layouts/posts.html.erb, + # that template will be used for all actions in PostsController and controllers inheriting + # from PostsController. + # + # If you use a module, for instance Weblog::PostsController, you will need a template named + # app/views/layouts/weblog/posts.html.erb. + # + # Since all your controllers inherit from ApplicationController, they will use + # app/views/layouts/application.html.erb if no other layout is specified + # or provided. + # + # == Inheritance Examples + # + # class BankController < ActionController::Base + # layout "bank_standard" + # + # class InformationController < BankController + # + # class TellerController < BankController + # # teller.html.erb exists + # + # class TillController < TellerController + # + # class VaultController < BankController + # layout :access_level_layout + # + # class EmployeeController < BankController + # layout nil + # + # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites + # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. + # + # The TellerController uses +teller.html.erb+, and TillController inherits that layout and + # uses it as well. + # + # == Types of layouts + # + # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes + # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can + # be done either by specifying a method reference as a symbol or using an inline method (as a proc). + # + # The method reference is the preferred approach to variable layouts and is used like this: + # + # class WeblogController < ActionController::Base + # layout :writers_and_readers + # + # def index + # # fetching posts + # end + # + # private + # def writers_and_readers + # logged_in? ? "writer_layout" : "reader_layout" + # end + # + # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing + # is logged in or not. + # + # If you want to use an inline method, such as a proc, do something like this: + # + # class WeblogController < ActionController::Base + # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } + # + # Of course, the most common way of specifying a layout is still just as a plain template name: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. + # Otherwise, it will be looked up relative to the template root. + # + # == Conditional layouts + # + # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering + # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The + # :only and :except options can be passed to the layout call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard", :except => :rss + # + # # ... + # + # end + # + # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout + # around the rendered view. + # + # Both the :only and :except condition can accept an arbitrary number of method references, so + # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. + # + # == Using a different layout in the action render call + # + # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. + # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. + # You can do this by passing a :layout option to the render call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # def help + # render :action => "help", :layout => "help" + # end + # end + # + # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. + module Layouts + extend ActiveSupport::Concern + + include ActionController::Renderer + include AbstractController::Layouts + + module ClassMethods + # If no layout is provided, look for a layout with this name. + def _implied_layout_name + controller_path + end + end + + private + def _determine_template(options) + super + + return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout) + layout = options.key?(:layout) ? options[:layout] : :none + options[:_layout] = _layout_for_option(layout, options[:_template].details) + end + + def _layout_for_option(name, details) + case name + when String then _layout_for_name(name, details) + when true then _default_layout(details, true) + when :none then _default_layout(details, false) + when false, nil then nil + else + raise ArgumentError, + "String, true, or false, expected for `layout'; you passed #{name.inspect}" + end + end + end +end diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb deleted file mode 100644 index 5c7218691e..0000000000 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ /dev/null @@ -1,188 +0,0 @@ -module ActionController #:nodoc: - module MimeResponds #:nodoc: - # Without web-service support, an action which collects the data for displaying a list of people - # might look something like this: - # - # def index - # @people = Person.find(:all) - # end - # - # Here's the same action, with web-service support baked in: - # - # def index - # @people = Person.find(:all) - # - # respond_to do |format| - # format.html - # format.xml { render :xml => @people.to_xml } - # end - # end - # - # What that says is, "if the client wants HTML in response to this action, just respond as we - # would have before, but if the client wants XML, return them the list of people in XML format." - # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) - # - # Supposing you have an action that adds a new person, optionally creating their company - # (by name) if it does not already exist, without web-services, it might look like this: - # - # def create - # @company = Company.find_or_create_by_name(params[:company][:name]) - # @person = @company.people.create(params[:person]) - # - # redirect_to(person_list_url) - # end - # - # Here's the same action, with web-service support baked in: - # - # def create - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # @person = @company.people.create(params[:person]) - # - # respond_to do |format| - # format.html { redirect_to(person_list_url) } - # format.js - # format.xml { render :xml => @person.to_xml(:include => @company) } - # end - # end - # - # If the client wants HTML, we just redirect them back to the person list. If they want Javascript - # (format.js), then it is an RJS request and we render the RJS template associated with this action. - # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also - # include the person's company in the rendered XML, so you get something like this: - # - # - # ... - # ... - # - # ... - # ... - # ... - # - # - # - # Note, however, the extra bit at the top of that action: - # - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # - # This is because the incoming XML document (if a web-service request is in process) can only contain a - # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): - # - # person[name]=...&person[company][name]=...&... - # - # And, like this (xml-encoded): - # - # - # ... - # - # ... - # - # - # - # In other words, we make the request so that it operates on a single entity's person. Then, in the action, - # we extract the company data from the request, find or create the company, and then create the new person - # with the remaining data. - # - # Note that you can define your own XML parameter parser which would allow you to describe multiple entities - # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow - # and accept Rails' defaults, life will be much easier. - # - # If you need to use a MIME type which isn't supported by default, you can register your own handlers in - # environment.rb as follows. - # - # Mime::Type.register "image/jpg", :jpg - def respond_to(*types, &block) - raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block - block ||= lambda { |responder| types.each { |type| responder.send(type) } } - responder = Responder.new(self) - block.call(responder) - responder.respond - end - - class Responder #:nodoc: - - def initialize(controller) - @controller = controller - @request = controller.request - @response = controller.response - - @mime_type_priority = @request.formats - - @order = [] - @responses = {} - end - - def custom(mime_type, &block) - mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) - - @order << mime_type - - @responses[mime_type] ||= Proc.new do - # TODO: Remove this when new base is merged in - if defined?(Http) - @controller.formats = [mime_type.to_sym] - end - - @controller.template.formats = [mime_type.to_sym] - @response.content_type = mime_type - - block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) - end - end - - def any(*args, &block) - if args.any? - args.each { |type| send(type, &block) } - else - custom(@mime_type_priority.first, &block) - end - end - - def self.generate_method_for_mime(mime) - sym = mime.is_a?(Symbol) ? mime : mime.to_sym - const = sym.to_s.upcase - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{sym}(&block) # def html(&block) - custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) - end # end - RUBY - end - - Mime::SET.each do |mime| - generate_method_for_mime(mime) - end - - def method_missing(symbol, &block) - mime_constant = Mime.const_get(symbol.to_s.upcase) - - if Mime::SET.include?(mime_constant) - self.class.generate_method_for_mime(mime_constant) - send(symbol, &block) - else - super - end - end - - def respond - for priority in @mime_type_priority - if priority == Mime::ALL - @responses[@order.first].call - return - else - if @responses[priority] - @responses[priority].call - return # mime type match found, be happy and return - end - end - end - - if @order.include?(Mime::ALL) - @responses[Mime::ALL].call - else - @controller.send :head, :not_acceptable - end - end - end - end -end diff --git a/actionpack/lib/action_controller/base/rack_convenience.rb b/actionpack/lib/action_controller/base/rack_convenience.rb new file mode 100644 index 0000000000..805157b0e3 --- /dev/null +++ b/actionpack/lib/action_controller/base/rack_convenience.rb @@ -0,0 +1,33 @@ +module ActionController + module RackConvenience + extend ActiveSupport::Concern + + included do + delegate :headers, :status=, :location=, :content_type=, + :status, :location, :content_type, :to => "@_response" + attr_internal :request, :response + end + + def call(name, env) + @_request = ActionDispatch::Request.new(env) + @_response = ActionDispatch::Response.new + @_response.request = request + super + end + + def params + @_params ||= @_request.parameters + end + + # :api: private + def to_rack + @_response.prepare! + @_response.to_a + end + + def response_body=(body) + response.body = body if response + super + end + end +end diff --git a/actionpack/lib/action_controller/base/redirect.rb b/actionpack/lib/action_controller/base/redirect.rb deleted file mode 100644 index 7e10f614e2..0000000000 --- a/actionpack/lib/action_controller/base/redirect.rb +++ /dev/null @@ -1,89 +0,0 @@ -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: - # - # * Hash - The URL will be generated by calling url_for with the +options+. - # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. - # * String not containing a protocol - The current protocol and host is prepended to the string. - # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for redirect_to(request.env["HTTP_REFERER"]) - # - # 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 redirect_to :back, 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 - - 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? - logger.info("Redirected to #{url}") if logger && logger.info? - response.status = interpret_status(status) - response.location = url.gsub(/[\r\n]/, '') - response.body = "You are being redirected." - @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.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/redirector.rb b/actionpack/lib/action_controller/base/redirector.rb new file mode 100644 index 0000000000..20060b001f --- /dev/null +++ b/actionpack/lib/action_controller/base/redirector.rb @@ -0,0 +1,19 @@ +module ActionController + class RedirectBackError < AbstractController::Error #: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 + def redirect_to(url, status) #:doc: + raise AbstractController::DoubleRenderError if response_body + logger.info("Redirected to #{url}") if logger && logger.info? + self.status = status + self.location = url.gsub(/[\r\n]/, '') + self.response_body = "You are being redirected." + end + end +end diff --git a/actionpack/lib/action_controller/base/render.rb b/actionpack/lib/action_controller/base/render.rb deleted file mode 100644 index cc0d878e01..0000000000 --- a/actionpack/lib/action_controller/base/render.rb +++ /dev/null @@ -1,403 +0,0 @@ -require 'action_controller/abstract/renderer' - -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 :text 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 'David' - # render :xml => {:name => "David"}.to_xml - # - # It's not necessary to call to_xml on the object you want to render, since render will - # automatically do that for you: - # - # # Also renders 'David' - # 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 to_json on the object you want to render, since render 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 :callback 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 "

Good seeing you!

" 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 :update 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 :status and :location 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? - - # This handles render "string", render :symbol, and render object - # render string and symbol are handled by render_for_name - # render object becomes render :partial => object - unless options.is_a?(Hash) - if options.is_a?(String) || options.is_a?(Symbol) - original, options = options, extra_options - else - extra_options[:partial], options = options, extra_options - end - end - - 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 options.include?(:json) - json = options[:json] - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - 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 - - # Same rules as render, but returns a Rack-compatible body - # instead of sending the response. - def render_to_body(options = nil, &block) #:doc: - render(options, &block) - response.body - ensure - response.content_type = nil - erase_render_results - reset_variables_added_to_assigns - end - - def render_to_string(options = {}) - AbstractController::Renderer.body_to_s(render_to_body(options)) - end - - # Clears the rendered results, allowing for another render to be performed. - def erase_render_results #:nodoc: - response.body = [] - @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 - - # ==== Arguments - # parts:: - # Example: ["show", [:html, :xml], "users", false] - def render_for_parts(parts, layout_details, options = {}) - parts[1] = {:formats => parts[1], :locales => [I18n.locale]} - - tmp = view_paths.find_by_parts(*parts) - - layout = _pick_layout(*layout_details) unless - self.class.exempt_from_layout.include?(tmp.handler) - - 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 diff --git a/actionpack/lib/action_controller/base/render_options.rb b/actionpack/lib/action_controller/base/render_options.rb new file mode 100644 index 0000000000..fc9a02626f --- /dev/null +++ b/actionpack/lib/action_controller/base/render_options.rb @@ -0,0 +1,103 @@ +module ActionController + module RenderOptions + extend ActiveSupport::Concern + + included do + extlib_inheritable_accessor :_renderers + self._renderers = [] + end + + module ClassMethods + def _write_render_options + renderers = _renderers.map do |r| + <<-RUBY_EVAL + if options.key?(:#{r}) + _process_options(options) + return _render_#{r}(options[:#{r}], options) + end + RUBY_EVAL + end + + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def _handle_render_options(options) + #{renderers.join} + end + RUBY_EVAL + end + + def _add_render_option(name) + _renderers << name + _write_render_options + end + end + + def render_to_body(options) + _handle_render_options(options) || super + end + end + + module RenderOption #:nodoc: + def self.extended(base) + base.extend ActiveSupport::Concern + base.send :include, ::ActionController::RenderOptions + + def base.register_renderer(name) + included { _add_render_option(name) } + end + end + end + + module Renderers + module Json + extend RenderOption + register_renderer :json + + def _render_json(json, options) + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + self.content_type ||= Mime::JSON + self.response_body = json + end + end + + module Js + extend RenderOption + register_renderer :js + + def _render_js(js, options) + self.content_type ||= Mime::JS + self.response_body = js + end + end + + module Xml + extend RenderOption + register_renderer :xml + + def _render_xml(xml, options) + self.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end + end + + module RJS + extend RenderOption + register_renderer :update + + def _render_update(proc, options) + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc) + self.content_type = Mime::JS + self.response_body = generator.to_s + end + end + + module All + extend ActiveSupport::Concern + + include ActionController::Renderers::Json + include ActionController::Renderers::Js + include ActionController::Renderers::Xml + include ActionController::Renderers::RJS + end + end +end diff --git a/actionpack/lib/action_controller/base/renderer.rb b/actionpack/lib/action_controller/base/renderer.rb new file mode 100644 index 0000000000..2fab501302 --- /dev/null +++ b/actionpack/lib/action_controller/base/renderer.rb @@ -0,0 +1,78 @@ +module ActionController + module Renderer + extend ActiveSupport::Concern + + include AbstractController::Renderer + + def process_action(*) + self.formats = request.formats.map {|x| x.to_sym} + super + end + + def render(options) + super + options[:_template] ||= _action_view._partial + self.content_type ||= begin + mime = options[:_template].mime_type + formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first) + end + response_body + end + + def render_to_body(options) + _process_options(options) + + if options.key?(:partial) + _render_partial(options[:partial], options) + end + + super + end + + private + def _prefix + controller_path + end + + def _determine_template(options) + if options.key?(:text) + options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first) + elsif options.key?(:inline) + handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") + template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) + options[:_template] = template + elsif options.key?(:template) + options[:_template_name] = options[:template] + elsif options.key?(:file) + options[:_template_name] = options[:file] + elsif !options.key?(:partial) + options[:_template_name] = (options[:action] || action_name).to_s + options[:_prefix] = _prefix + end + + super + end + + def _render_partial(partial, options) + case partial + when true + options[:_prefix] = _prefix + when String + options[:_prefix] = _prefix unless partial.index('/') + options[:_template_name] = partial + else + options[:_partial_object] = true + return + end + + options[:_partial] = options[:object] || true + end + + def _process_options(options) + status, content_type, location = options.values_at(:status, :content_type, :location) + self.status = status if status + self.content_type = content_type if content_type + self.headers["Location"] = url_for(location) if location + end + end +end diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb deleted file mode 100644 index a470c8eec1..0000000000 --- a/actionpack/lib/action_controller/base/request_forgery_protection.rb +++ /dev/null @@ -1,123 +0,0 @@ -module ActionController #:nodoc: - class InvalidAuthenticityToken < ActionControllerError #:nodoc: - end - - module RequestForgeryProtection - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Helpers, Session - end - - included do - if defined?(ActionController::Http) - # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ - # sets it to :authenticity_token by default. - cattr_accessor :request_forgery_protection_token - - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection - self.allow_forgery_protection = true - end - - helper_method :form_authenticity_token - helper_method :protect_against_forgery? - end - - # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a - # forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all - # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only - # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication - # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway. - # - # This is turned on with the protect_from_forgery method, which will check the token and raise an - # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in - # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0 - # applications. - # - # The token parameter is named authenticity_token by default. If you are generating an HTML form manually (without the - # use of Rails' form_for, form_tag or other helpers), you have to include a hidden field named like that and - # set its value to what is returned by form_authenticity_token. Same applies to manually constructed Ajax requests. To - # make the token available through a global variable to scripts on a certain page, you could add something like this to a view: - # - # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %> - # - # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to - # config/environments/test.rb: - # - # # Disable request forgery protection in test environment - # config.action_controller.allow_forgery_protection = false - # - # == Learn more about CSRF (Cross-Site Request Forgery) attacks - # - # Here are some resources: - # * http://isc.sans.org/diary.html?storyid=1750 - # * http://en.wikipedia.org/wiki/Cross-site_request_forgery - # - # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. - # There are a few guidelines you should follow: - # - # * Keep your GET requests safe and idempotent. More reading material: - # * http://www.xml.com/pub/a/2002/04/24/deviant.html - # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 - # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session" - # - module ClassMethods - # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked. - # - # Example: - # - # class FooController < ApplicationController - # protect_from_forgery :except => :index - # - # # you can disable csrf protection on controller-by-controller basis: - # skip_before_filter :verify_authenticity_token - # end - # - # Valid Options: - # - # * :only/:except - Passed to the before_filter call. Set which actions are verified. - def protect_from_forgery(options = {}) - self.request_forgery_protection_token ||= :authenticity_token - before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except) - if options[:secret] || options[:digest] - ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller) - end - end - end - - protected - # The actual before_filter that is used. Modify this to change how you handle unverified requests. - def verify_authenticity_token - verified_request? || raise(ActionController::InvalidAuthenticityToken) - end - - # Returns true or false if a request is verified. Checks: - # - # * is the format restricted? By default, only HTML requests are checked. - # * is it a GET request? Gets should be safe and idempotent - # * Does the form_authenticity_token match the given token value from the params? - def verified_request? - !protect_against_forgery? || - request.method == :get || - request.xhr? || - !verifiable_request_format? || - form_authenticity_token == params[request_forgery_protection_token] - end - - def verifiable_request_format? - !request.content_type.nil? && request.content_type.verify_request? - end - - # Sets the token value for the current session. Pass a :secret option - # in +protect_from_forgery+ to add a custom salt to the hash. - def form_authenticity_token - session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32) - end - - def protect_against_forgery? - allow_forgery_protection && request_forgery_protection_token - end - end -end diff --git a/actionpack/lib/action_controller/base/rescuable.rb b/actionpack/lib/action_controller/base/rescuable.rb new file mode 100644 index 0000000000..029e643d93 --- /dev/null +++ b/actionpack/lib/action_controller/base/rescuable.rb @@ -0,0 +1,52 @@ +module ActionController #:nodoc: + # Actions that fail to perform as expected throw exceptions. These + # exceptions can either be rescued for the public view (with a nice + # user-friendly explanation) or for the developers view (with tons of + # debugging information). The developers view is already implemented by + # the Action Controller, but the public view should be tailored to your + # specific application. + # + # The default behavior for public exceptions is to render a static html + # file with the name of the error code thrown. If no such file exists, an + # empty response is sent with the correct status code. + # + # You can override what constitutes a local request by overriding the + # local_request? method in your own controller. Custom rescue + # behavior is achieved by overriding the rescue_action_in_public + # and rescue_action_locally methods. + module Rescue + extend ActiveSupport::Concern + + included do + include ActiveSupport::Rescuable + end + + module ClassMethods + # This can be removed once we can move action(:_rescue_action) into middlewares.rb + # Currently, it does controller.method(:rescue_action), which is hiding the implementation + # difference between the old and new base. + def rescue_action(env) + action(:_rescue_action).call(env) + end + end + + attr_internal :rescued_exception + + private + def method_for_action(action_name) + return action_name if self.rescued_exception = request.env.delete("action_dispatch.rescue.exception") + super + end + + def _rescue_action + rescue_with_handler(rescued_exception) || raise(rescued_exception) + end + + def process_action(*) + super + rescue Exception => exception + self.rescued_exception = exception + _rescue_action + end + end +end diff --git a/actionpack/lib/action_controller/base/rescue.rb b/actionpack/lib/action_controller/base/rescue.rb deleted file mode 100644 index 2717a06a37..0000000000 --- a/actionpack/lib/action_controller/base/rescue.rb +++ /dev/null @@ -1,50 +0,0 @@ -module ActionController #:nodoc: - # Actions that fail to perform as expected throw exceptions. These - # exceptions can either be rescued for the public view (with a nice - # user-friendly explanation) or for the developers view (with tons of - # debugging information). The developers view is already implemented by - # the Action Controller, but the public view should be tailored to your - # specific application. - # - # The default behavior for public exceptions is to render a static html - # file with the name of the error code thrown. If no such file exists, an - # empty response is sent with the correct status code. - # - # You can override what constitutes a local request by overriding the - # local_request? method in your own controller. Custom rescue - # behavior is achieved by overriding the rescue_action_in_public - # and rescue_action_locally methods. - module Rescue - def self.included(base) #:nodoc: - base.send :include, ActiveSupport::Rescuable - base.extend(ClassMethods) - - base.class_eval do - alias_method_chain :perform_action, :rescue - end - end - - module ClassMethods - def rescue_action(env) - exception = env.delete('action_dispatch.rescue.exception') - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - new.process(request, response, :rescue_action, exception).to_a - end - end - - protected - # Exception handler called when the performance of an action raises - # an exception. - def rescue_action(exception) - rescue_with_handler(exception) || raise(exception) - end - - private - def perform_action_with_rescue - perform_action_without_rescue - rescue Exception => exception - rescue_action(exception) - end - end -end diff --git a/actionpack/lib/action_controller/base/responder.rb b/actionpack/lib/action_controller/base/responder.rb deleted file mode 100644 index 1aee980da6..0000000000 --- a/actionpack/lib/action_controller/base/responder.rb +++ /dev/null @@ -1,43 +0,0 @@ -module ActionController - module Responder - def self.included(klass) - klass.extend ClassMethods - end - - private - def render_for_text(text) #:nodoc: - @performed_render = true - - case text - when Proc - response.body = text - when nil - # Safari 2 doesn't pass response headers if the response is zero-length - if response.body_parts.empty? - response.body_parts << ' ' - end - else - response.body_parts << text - end - end - - # Returns a set of the methods defined as actions in your controller - 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 diff --git a/actionpack/lib/action_controller/base/session.rb b/actionpack/lib/action_controller/base/session.rb new file mode 100644 index 0000000000..bcedd6e1c7 --- /dev/null +++ b/actionpack/lib/action_controller/base/session.rb @@ -0,0 +1,15 @@ +module ActionController + module Session + extend ActiveSupport::Concern + + include RackConvenience + + def session + @_request.session + end + + def reset_session + @_request.reset_session + end + end +end diff --git a/actionpack/lib/action_controller/base/session_management.rb b/actionpack/lib/action_controller/base/session_management.rb deleted file mode 100644 index ffce8e1bd1..0000000000 --- a/actionpack/lib/action_controller/base/session_management.rb +++ /dev/null @@ -1,54 +0,0 @@ -module ActionController #:nodoc: - module SessionManagement #:nodoc: - def self.included(base) - base.class_eval do - extend ClassMethods - end - end - - module ClassMethods - # Set the session store to be used for keeping the session data between requests. - # By default, sessions are stored in browser cookies (:cookie_store), - # but you can also specify one of the other included stores (:active_record_store, - # :mem_cache_store, or your own custom class. - def session_store=(store) - if store == :active_record_store - self.session_store = ActiveRecord::SessionStore - else - @@session_store = store.is_a?(Symbol) ? - Session.const_get(store.to_s.camelize) : - store - end - end - - # Returns the session store class currently used. - def session_store - if defined? @@session_store - @@session_store - else - ActionDispatch::Session::CookieStore - end - end - - def session=(options = {}) - self.session_store = nil if options.delete(:disabled) - session_options.merge!(options) - end - - # Returns the hash used to configure the session. Example use: - # - # ActionController::Base.session_options[:secure] = true # session only available over HTTPS - def session_options - @session_options ||= {} - end - - def session(*args) - ActiveSupport::Deprecation.warn( - "Disabling sessions for a single controller has been deprecated. " + - "Sessions are now lazy loaded. So if you don't access them, " + - "consider them off. You can still modify the session cookie " + - "options with request.session_options.", caller) - end - end - end -end diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb deleted file mode 100644 index 5c72fc9ad9..0000000000 --- a/actionpack/lib/action_controller/base/streaming.rb +++ /dev/null @@ -1,188 +0,0 @@ -module ActionController #:nodoc: - # Methods for sending arbitrary data and for streaming files to the browser, - # instead of rendering. - module Streaming - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include ActionController::Renderer - end - - DEFAULT_SEND_FILE_OPTIONS = { - :type => 'application/octet-stream'.freeze, - :disposition => 'attachment'.freeze, - :stream => true, - :buffer_size => 4096, - :x_sendfile => false - }.freeze - - X_SENDFILE_HEADER = 'X-Sendfile'.freeze - - protected - # Sends the file, by default streaming it 4096 bytes at a time. This way the - # whole file doesn't need to be read into memory at once. This makes it - # feasible to send even large files. You can optionally turn off streaming - # and send the whole file at once. - # - # Be careful to sanitize the path parameter if it is coming from a web - # page. send_file(params[:path]) allows a malicious user to - # download any file on your server. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # Defaults to File.basename(path). - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :length - used to manually override the length (in bytes) of the content that - # is going to be sent to the client. Defaults to File.size(path). - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :stream - whether to send the file to the user agent as it is read (+true+) - # or to read the entire file before sending (+false+). Defaults to +true+. - # * :buffer_size - specifies size (in bytes) of the buffer used to stream the file. - # Defaults to 4096. - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # * :url_based_filename - set to +true+ if you want the browser guess the filename from - # the URL, which is necessary for i18n filenames on certain browsers - # (setting :filename overrides this option). - # * :x_sendfile - uses X-Sendfile to send the file when set to +true+. This is currently - # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this - # uses the web server to send the file, this may lower memory consumption on your server and - # it will not block your application for further requests. - # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and - # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+. - # - # The default Content-Type and Content-Disposition headers are - # set to download arbitrary binary files in as many browsers as - # possible. IE versions 4, 5, 5.5, and 6 are all known to have - # a variety of quirks (especially when downloading over SSL). - # - # Simple download: - # - # send_file '/path/to.zip' - # - # Show a JPEG in the browser: - # - # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' - # - # Show a 404 page in the browser: - # - # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 - # - # Read about the other Content-* HTTP headers if you'd like to - # provide the user with more information (such as Content-Description) in - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. - # - # Also be aware that the document may be cached by proxies and browsers. - # The Pragma and Cache-Control headers declare how the file may be cached - # by intermediaries. They default to require clients to validate with - # the server before releasing cached responses. See - # http://www.mnot.net/cache_docs/ for an overview of web caching and - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - # for the Cache-Control header spec. - def send_file(path, options = {}) #:doc: - raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) - - options[:length] ||= File.size(path) - options[:filename] ||= File.basename(path) unless options[:url_based_filename] - send_file_headers! options - - @performed_render = false - - if options[:x_sendfile] - logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger - head options[:status], X_SENDFILE_HEADER => path - else - if options[:stream] - # TODO : Make render :text => proc {} work with the new base - render :status => options[:status], :text => Proc.new { |response, output| - logger.info "Streaming file #{path}" unless logger.nil? - len = options[:buffer_size] || 4096 - File.open(path, 'rb') do |file| - while buf = file.read(len) - output.write(buf) - end - end - } - else - logger.info "Sending file #{path}" unless logger.nil? - File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read } - end - end - end - - # Sends the given binary data to the browser. This method is similar to - # render :text => data, but also allows you to specify whether - # the browser should display the response as a file attachment (i.e. in a - # download dialog) or as inline data. You may also set the content type, - # the apparent file name, and other things. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # - # Generic data download: - # - # send_data buffer - # - # Download a dynamically-generated tarball: - # - # send_data generate_tgz('dir'), :filename => 'dir.tgz' - # - # Display an image Active Record in the browser: - # - # send_data image.data, :type => image.content_type, :disposition => 'inline' - # - # See +send_file+ for more information on HTTP Content-* headers and caching. - # - # Tip: if you want to stream large amounts of on-the-fly generated - # data to the browser, then use render :text => proc { ... } - # instead. See ActionController::Base#render for more information. - def send_data(data, options = {}) #:doc: - logger.info "Sending data #{options[:filename]}" if logger - send_file_headers! options.merge(:length => data.size) - @performed_render = false - render :status => options[:status], :text => data - end - - private - def send_file_headers!(options) - options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) - [:length, :type, :disposition].each do |arg| - raise ArgumentError, ":#{arg} option required" if options[arg].nil? - end - - disposition = options[:disposition].dup || 'attachment' - - disposition <<= %(; filename="#{options[:filename]}") if options[:filename] - - content_type = options[:type] - - if content_type.is_a?(Symbol) - raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s) - self.content_type = Mime::Type.lookup_by_extension(content_type.to_s) - else - self.content_type = content_type - end - - headers.merge!( - 'Content-Length' => options[:length], - 'Content-Disposition' => disposition, - 'Content-Transfer-Encoding' => 'binary' - ) - - # Fix a problem with IE 6.0 on opening downloaded files: - # If Cache-Control: no-cache is set (which Rails does by default), - # IE removes the file it just downloaded from its cache immediately - # after it displays the "open/save" dialog, which means that if you - # hit "open" the file isn't there anymore when the application that - # is called for handling the download is run, so let's workaround that - headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' - end - end -end diff --git a/actionpack/lib/action_controller/base/testing.rb b/actionpack/lib/action_controller/base/testing.rb new file mode 100644 index 0000000000..a4a1116d9e --- /dev/null +++ b/actionpack/lib/action_controller/base/testing.rb @@ -0,0 +1,39 @@ +module ActionController + module Testing + extend ActiveSupport::Concern + + include RackConvenience + + # OMG MEGA HAX + def process_with_new_base_test(request, response) + @_request = request + @_response = response + @_response.request = request + ret = process(request.parameters[:action]) + @_response.body ||= self.response_body + @_response.prepare! + set_test_assigns + ret + end + + def set_test_assigns + @assigns = {} + (instance_variable_names - self.class.protected_instance_variables).each do |var| + name, value = var[1..-1], instance_variable_get(var) + @assigns[name] = value + end + end + + # TODO : Rewrite tests using controller.headers= to use Rack env + def headers=(new_headers) + @_response ||= ActionDispatch::Response.new + @_response.headers.replace(new_headers) + end + + module ClassMethods + def before_filters + _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name} + end + end + end +end diff --git a/actionpack/lib/action_controller/base/url_for.rb b/actionpack/lib/action_controller/base/url_for.rb new file mode 100644 index 0000000000..7119c14cd3 --- /dev/null +++ b/actionpack/lib/action_controller/base/url_for.rb @@ -0,0 +1,49 @@ +module ActionController + module UrlFor + extend ActiveSupport::Concern + + include RackConvenience + + def process_action(*) + initialize_current_url + super + end + + def initialize_current_url + @url = UrlRewriter.new(request, params.clone) + end + + # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in + # the form of a hash, just like the one you would use for url_for directly. Example: + # + # def default_url_options(options) + # { :project => @project.active? ? @project.url_name : "unknown" } + # end + # + # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the + # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set + # by this method. + def default_url_options(options = nil) + end + + def rewrite_options(options) #:nodoc: + if defaults = default_url_options(options) + defaults.merge(options) + else + options + end + end + + def url_for(options = {}) + options ||= {} + case options + when String + options + when Hash + @url.rewrite(rewrite_options(options)) + else + polymorphic_url(options) + end + end + end +end diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb deleted file mode 100644 index d87b348ed4..0000000000 --- a/actionpack/lib/action_controller/base/verification.rb +++ /dev/null @@ -1,133 +0,0 @@ -module ActionController #:nodoc: - module Verification #:nodoc: - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Callbacks, Session, Flash, Renderer - end - - # This module provides a class-level method for specifying that certain - # actions are guarded against being called without certain prerequisites - # being met. This is essentially a special kind of before_filter. - # - # An action may be guarded against being invoked without certain request - # parameters being set, or without certain session values existing. - # - # When a verification is violated, values may be inserted into the flash, and - # a specified redirection is triggered. If no specific action is configured, - # verification failures will by default result in a 400 Bad Request response. - # - # Usage: - # - # class GlobalController < ActionController::Base - # # Prevent the #update_settings action from being invoked unless - # # the 'admin_privileges' request parameter exists. The - # # settings action will be redirected to in current controller - # # if verification fails. - # verify :params => "admin_privileges", :only => :update_post, - # :redirect_to => { :action => "settings" } - # - # # Disallow a post from being updated if there was no information - # # submitted with the post, and if there is no active post in the - # # session, and if there is no "note" key in the flash. The route - # # named category_url will be redirected to if verification fails. - # - # verify :params => "post", :session => "post", "flash" => "note", - # :only => :update_post, - # :add_flash => { "alert" => "Failed to create your message" }, - # :redirect_to => :category_url - # - # Note that these prerequisites are not business rules. They do not examine - # the content of the session or the parameters. That level of validation should - # be encapsulated by your domain model or helper methods in the controller. - module ClassMethods - # Verify the given actions so that if certain prerequisites are not met, - # the user is redirected to a different action. The +options+ parameter - # is a hash consisting of the following key/value pairs: - # - # :params:: - # a single key or an array of keys that must be in the params - # hash in order for the action(s) to be safely called. - # :session:: - # a single key or an array of keys that must be in the session - # in order for the action(s) to be safely called. - # :flash:: - # a single key or an array of keys that must be in the flash in order - # for the action(s) to be safely called. - # :method:: - # a single key or an array of keys--any one of which must match the - # current request method in order for the action(s) to be safely called. - # (The key should be a symbol: :get or :post, for - # example.) - # :xhr:: - # true/false option to ensure that the request is coming from an Ajax - # call or not. - # :add_flash:: - # a hash of name/value pairs that should be merged into the session's - # flash if the prerequisites cannot be satisfied. - # :add_headers:: - # a hash of name/value pairs that should be merged into the response's - # headers hash if the prerequisites cannot be satisfied. - # :redirect_to:: - # the redirection parameters to be used when redirecting if the - # prerequisites cannot be satisfied. You can redirect either to named - # route or to the action in some controller. - # :render:: - # the render parameters to be used when the prerequisites cannot be satisfied. - # :only:: - # only apply this verification to the actions specified in the associated - # array (may also be a single value). - # :except:: - # do not apply this verification to the actions specified in the associated - # array (may also be a single value). - def verify(options={}) - before_filter :only => options[:only], :except => options[:except] do |c| - c.__send__ :verify_action, options - end - end - end - - private - - def verify_action(options) #:nodoc: - if prereqs_invalid?(options) - flash.update(options[:add_flash]) if options[:add_flash] - response.headers.merge!(options[:add_headers]) if options[:add_headers] - apply_remaining_actions(options) unless performed? - end - end - - def prereqs_invalid?(options) # :nodoc: - verify_presence_of_keys_in_hash_flash_or_params(options) || - verify_method(options) || - verify_request_xhr_status(options) - end - - def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: - [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || - [*options[:session]].find { |v| session[v].nil? } || - [*options[:flash] ].find { |v| flash[v].nil? } - end - - def verify_method(options) # :nodoc: - [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] - end - - def verify_request_xhr_status(options) # :nodoc: - request.xhr? != options[:xhr] unless options[:xhr].nil? - end - - def apply_redirect_to(redirect_to_option) # :nodoc: - (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option - end - - def apply_remaining_actions(options) # :nodoc: - case - when options[:render] ; render(options[:render]) - when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) - else head(:bad_request) - end - end - end -end \ No newline at end of file diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb deleted file mode 100644 index df256985ac..0000000000 --- a/actionpack/lib/action_controller/new_base.rb +++ /dev/null @@ -1,47 +0,0 @@ -module ActionController - autoload :Base, "action_controller/new_base/base" - autoload :ConditionalGet, "action_controller/new_base/conditional_get" - autoload :HideActions, "action_controller/new_base/hide_actions" - autoload :Http, "action_controller/new_base/http" - autoload :Layouts, "action_controller/new_base/layouts" - autoload :RackConvenience, "action_controller/new_base/rack_convenience" - autoload :Rails2Compatibility, "action_controller/new_base/compatibility" - autoload :Redirector, "action_controller/new_base/redirector" - autoload :Renderer, "action_controller/new_base/renderer" - autoload :RenderOptions, "action_controller/new_base/render_options" - autoload :Renderers, "action_controller/new_base/render_options" - autoload :Rescue, "action_controller/new_base/rescuable" - autoload :Testing, "action_controller/new_base/testing" - autoload :UrlFor, "action_controller/new_base/url_for" - autoload :Session, "action_controller/new_base/session" - autoload :Helpers, "action_controller/new_base/helpers" - - # Ported modules - # require 'action_controller/routing' - autoload :Caching, 'action_controller/caching' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :MimeResponds, 'action_controller/base/mime_responds' - autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Resources, 'action_controller/routing/resources' - autoload :SessionManagement, 'action_controller/base/session_management' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' - autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - - autoload :Verification, 'action_controller/base/verification' - autoload :Flash, 'action_controller/base/chained/flash' - autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' - autoload :Streaming, 'action_controller/base/streaming' - autoload :HttpAuthentication, 'action_controller/base/http_authentication' - autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' - autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/base/cookies' - - require 'action_controller/routing' -end - -autoload :HTML, 'action_controller/vendor/html-scanner' - -require 'action_dispatch' -require 'action_view' diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb deleted file mode 100644 index e8fc153578..0000000000 --- a/actionpack/lib/action_controller/new_base/base.rb +++ /dev/null @@ -1,173 +0,0 @@ -module ActionController - class Base < Http - abstract! - - include AbstractController::Benchmarker - include AbstractController::Callbacks - include AbstractController::Logger - - include ActionController::Helpers - include ActionController::HideActions - include ActionController::UrlFor - include ActionController::Redirector - include ActionController::Renderer - include ActionController::Renderers::All - include ActionController::Layouts - include ActionController::ConditionalGet - include ActionController::RackConvenience - - # Legacy modules - include SessionManagement - include ActionDispatch::StatusCodes - include ActionController::Caching - include ActionController::MimeResponds - - # Rails 2.x compatibility - include ActionController::Rails2Compatibility - - include ActionController::Cookies - include ActionController::Session - include ActionController::Flash - include ActionController::Verification - include ActionController::RequestForgeryProtection - include ActionController::Streaming - include ActionController::HttpAuthentication::Basic::ControllerMethods - include ActionController::HttpAuthentication::Digest::ControllerMethods - include ActionController::FilterParameterLogging - include ActionController::Translation - - # TODO: Extract into its own module - # This should be moved together with other normalizing behavior - module ImplicitRender - def send_action(*) - ret = super - default_render unless performed? - ret - end - - def default_render - render - end - - def method_for_action(action_name) - super || begin - if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) - "default_render" - end - end - end - end - - include ImplicitRender - - include ActionController::Rescue - - def self.inherited(klass) - ::ActionController::Base.subclasses << klass.to_s - super - end - - def self.subclasses - @subclasses ||= [] - end - - def self.app_loaded! - @subclasses.each do |subclass| - subclass.constantize._write_layout_method - end - end - - def _normalize_options(action = nil, options = {}, &blk) - if action.is_a?(Hash) - options, action = action, nil - elsif action.is_a?(String) || action.is_a?(Symbol) - key = case action = action.to_s - when %r{^/} then :file - when %r{/} then :template - else :action - end - options.merge! key => action - elsif action - options.merge! :partial => action - end - - if options.key?(:action) && options[:action].to_s.index("/") - options[:template] = options.delete(:action) - end - - if options[:status] - options[:status] = interpret_status(options[:status]).to_i - end - - options[:update] = blk if block_given? - options - end - - def render(action = nil, options = {}, &blk) - options = _normalize_options(action, options, &blk) - super(options) - end - - def render_to_string(action = nil, options = {}, &blk) - options = _normalize_options(action, options, &blk) - super(options) - end - - # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: - # - # * Hash - The URL will be generated by calling url_for with the +options+. - # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. - # * String not containing a protocol - The current protocol and host is prepended to the string. - # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for redirect_to(request.env["HTTP_REFERER"]) - # - # 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 redirect_to :back, 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? - - status = if options.is_a?(Hash) && options.key?(:status) - interpret_status(options.delete(:status)) - elsif response_status.key?(:status) - interpret_status(response_status[:status]) - else - 302 - end - - url = 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+.-]*:.*} - options - when String - request.protocol + request.host_with_port + options - when :back - raise RedirectBackError unless refer = request.headers["Referer"] - refer - else - url_for(options) - end - - super(url, status) - end - end -end diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb deleted file mode 100644 index 29ba43a879..0000000000 --- a/actionpack/lib/action_controller/new_base/compatibility.rb +++ /dev/null @@ -1,139 +0,0 @@ -module ActionController - module Rails2Compatibility - extend ActiveSupport::Concern - - class ::ActionController::ActionControllerError < StandardError #:nodoc: - end - - # Temporary hax - included do - ::ActionController::UnknownAction = ::AbstractController::ActionNotFound - ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError - - cattr_accessor :session_options - self.session_options = {} - - cattr_accessor :allow_concurrency - self.allow_concurrency = false - - cattr_accessor :param_parsers - self.param_parsers = { Mime::MULTIPART_FORM => :multipart_form, - Mime::URL_ENCODED_FORM => :url_encoded_form, - Mime::XML => :xml_simple, - Mime::JSON => :json } - - cattr_accessor :relative_url_root - self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] - - cattr_accessor :default_charset - self.default_charset = "utf-8" - - # cattr_reader :protected_instance_variables - cattr_accessor :protected_instance_variables - self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller - @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params - @_flash @_response) - - # Indicates whether or not optimise the generated named - # route helper methods - cattr_accessor :optimise_named_routes - self.optimise_named_routes = true - - cattr_accessor :resources_path_names - self.resources_path_names = { :new => 'new', :edit => 'edit' } - - # Controls the resource action separator - cattr_accessor :resource_action_separator - self.resource_action_separator = "/" - - cattr_accessor :use_accept_header - self.use_accept_header = true - - cattr_accessor :page_cache_directory - self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" - - cattr_reader :cache_store - - cattr_accessor :consider_all_requests_local - self.consider_all_requests_local = true - - # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, - # and images to a dedicated asset server away from the main web server. Example: - # ActionController::Base.asset_host = "http://assets.example.com" - cattr_accessor :asset_host - - cattr_accessor :ip_spoofing_check - self.ip_spoofing_check = true - end - - # For old tests - def initialize_template_class(*) end - def assign_shortcuts(*) end - - # TODO: Remove this after we flip - def template - @template ||= _action_view - end - - def process_action(*) - template - super - end - - module ClassMethods - def consider_all_requests_local - end - - def rescue_action(env) - raise env["action_dispatch.rescue.exception"] - end - - # Defines the storage option for cached fragments - def cache_store=(store_option) - @@cache_store = ActiveSupport::Cache.lookup_store(store_option) - end - end - - def render_to_body(options) - if options.is_a?(Hash) && options.key?(:template) - options[:template].sub!(/^\//, '') - end - - options[:text] = nil if options[:nothing] == true - - body = super - body = [' '] if body.blank? - body - end - - def _handle_method_missing - method_missing(@_action_name.to_sym) - end - - def method_for_action(action_name) - super || (respond_to?(:method_missing) && "_handle_method_missing") - end - - def _find_by_parts(name, details) - details[:prefix] = nil if name =~ /\blayouts/ - super - end - - def performed? - response_body - end - - # ==== Request only view path switching ==== - def append_view_path(path) - view_paths.push(*path) - end - - def prepend_view_path(path) - view_paths.unshift(*path) - end - - def view_paths - _action_view.view_paths - end - end -end diff --git a/actionpack/lib/action_controller/new_base/conditional_get.rb b/actionpack/lib/action_controller/new_base/conditional_get.rb deleted file mode 100644 index d287ec4994..0000000000 --- a/actionpack/lib/action_controller/new_base/conditional_get.rb +++ /dev/null @@ -1,133 +0,0 @@ -module ActionController - module ConditionalGet - extend ActiveSupport::Concern - - include RackConvenience - - # Sets the etag, last_modified, or both on the response and renders a - # "304 Not Modified" response if the request is already fresh. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) - # end - # - # This will render the show template if the request isn't sending a matching etag or - # If-Modified-Since header and just a "304 Not Modified" response if there's a match. - # - def fresh_when(options) - options.assert_valid_keys(:etag, :last_modified, :public) - - response.etag = options[:etag] if options[:etag] - response.last_modified = options[:last_modified] if options[:last_modified] - - if options[:public] - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - cache_control.delete("private") - cache_control.delete("no-cache") - cache_control << "public" - response.headers["Cache-Control"] = cache_control.join(', ') - end - - if request.fresh?(response) - head :not_modified - end - 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 = 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 - - # 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, - # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # - # if stale?(:etag => @article, :last_modified => @article.created_at.utc) - # @statistics = @article.really_expensive_call - # respond_to do |format| - # # all the supported formats - # end - # end - # end - def stale?(options) - fresh_when(options) - !request.fresh?(response) - end - - # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that - # intermediate caches shouldn't cache the response. - # - # Examples: - # expires_in 20.minutes - # expires_in 3.hours, :public => true - # expires in 3.hours, 'max-stale' => 5.hours, :public => true - # - # This method will overwrite an existing Cache-Control header. - # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. - def expires_in(seconds, options = {}) #:doc: - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - - cache_control << "max-age=#{seconds}" - cache_control.delete("no-cache") - if options[:public] - cache_control.delete("private") - cache_control << "public" - else - cache_control << "private" - end - - # This allows for additional headers to be passed through like 'max-stale' => 5.hours - cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} - - response.headers["Cache-Control"] = cache_control.join(', ') - end - - # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or - # intermediate caches (like caching proxy servers). - def expires_now #:doc: - response.headers["Cache-Control"] = "no-cache" - end - end -end diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb deleted file mode 100644 index 2fa5ea6519..0000000000 --- a/actionpack/lib/action_controller/new_base/helpers.rb +++ /dev/null @@ -1,178 +0,0 @@ -require 'active_support/core_ext/load_error' -require 'active_support/core_ext/name_error' -require 'active_support/dependencies' - -module ActionController - # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, - # +numbers+ and model objects, to name a few. These helpers are available to all templates - # by default. - # - # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to - # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will - # include a helper whose name matches that of the controller, e.g., MyController will automatically - # include MyHelper. - # - # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any - # controller which inherits from it. - # - # ==== Examples - # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if - # the Time object is blank: - # - # module FormattedTimeHelper - # def format_time(time, format=:long, blank_message=" ") - # time.blank? ? blank_message : time.to_s(format) - # end - # end - # - # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: - # - # class EventsController < ActionController::Base - # helper FormattedTimeHelper - # def index - # @events = Event.find(:all) - # end - # end - # - # Then, in any view rendered by EventController, the format_time method can be called: - # - # <% @events.each do |event| -%> - #

- # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> - #

- # <% end -%> - # - # Finally, assuming we have two event instances, one which has a time and one which does not, - # the output might look like this: - # - # 23 Aug 11:30 | Carolina Railhawks Soccer Match - # N/A | Carolina Railhaws Training Workshop - # - module Helpers - extend ActiveSupport::Concern - - include AbstractController::Helpers - - included do - # Set the default directory for helpers - extlib_inheritable_accessor(:helpers_dir) do - defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers" - end - end - - module ClassMethods - def inherited(klass) - klass.class_eval { default_helper_module! unless name.blank? } - super - end - - # The +helper+ class method can take a series of helper module names, a block, or both. - # - # ==== Parameters - # *args - # block:: A block defining helper methods - # - # ==== Examples - # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file - # and include the module in the template class. The second form illustrates how to include custom helpers - # when working with namespaced controllers, or other cases where the file containing the helper definition is not - # in one of Rails' standard load paths: - # helper :foo # => requires 'foo_helper' and includes FooHelper - # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper - # - # When the argument is a module it will be included directly in the template class. - # helper FooHelper # => includes FooHelper - # - # When the argument is the symbol :all, the controller will include all helpers beneath - # ActionController::Base.helpers_dir (defaults to app/helpers/**/*.rb under RAILS_ROOT). - # helper :all - # - # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available - # to the template. - # # One line - # helper { def hello() "Hello, world!" end } - # # Multi-line - # helper do - # def foo(bar) - # "#{bar} is the very best" - # end - # end - # - # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of - # +symbols+, +strings+, +modules+ and blocks. - # helper(:three, BlindHelper) { def mice() 'mice' end } - # - def helper(*args, &block) - super(*_modules_for_helpers(args), &block) - end - - # Declares helper accessors for controller attributes. For example, the - # following adds new +name+ and name= instance methods to a - # controller and makes them available to the view: - # helper_attr :name - # attr_accessor :name - # - # ==== Parameters - # *attrs:: Names of attributes to be converted - # into helpers. - def helper_attr(*attrs) - attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } - end - - # Provides a proxy to access helpers methods from outside the view. - def helpers - @helper_proxy ||= ActionView::Base.new.extend(_helpers) - end - - private - # Returns a list of modules, normalized from the acceptable kinds of - # helpers with the following behavior: - # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", - # and "foo_bar_helper.rb" is loaded using require_dependency. - # :all:: Loads all modules in the #helpers_dir - # Module:: No further processing - # - # After loading the appropriate files, the corresponding modules - # are returned. - # - # ==== Parameters - # args:: A list of helpers - # - # ==== Returns - # Array[Module]:: A normalized list of modules for the list of - # helpers provided. - def _modules_for_helpers(args) - args.flatten.map! do |arg| - case arg - when :all - _modules_for_helpers all_application_helpers - when String, Symbol - file_name = "#{arg.to_s.underscore}_helper" - require_dependency(file_name, "Missing helper file helpers/%s.rb") - file_name.camelize.constantize - when Module - arg - else - raise ArgumentError, "helper must be a String, Symbol, or Module" - end - end - end - - def default_helper_module! - module_name = name.sub(/Controller$/, '') - module_path = module_name.underscore - helper module_path - rescue MissingSourceFile => e - raise e unless e.is_missing? "#{module_path}_helper" - rescue NameError => e - raise e unless e.missing_name? "#{module_name}Helper" - end - - # Extract helper names from files in app/helpers/**/*.rb - def all_application_helpers - extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ - Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } - end - end - end -end diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb deleted file mode 100644 index af68c772b1..0000000000 --- a/actionpack/lib/action_controller/new_base/hide_actions.rb +++ /dev/null @@ -1,35 +0,0 @@ -module ActionController - # ActionController::HideActions adds the ability to prevent public methods on a controller - # to be called as actions. - module HideActions - extend ActiveSupport::Concern - - included do - extlib_inheritable_accessor(:hidden_actions) { Set.new } - end - - private - - # Overrides AbstractController::Base#action_method? to return false if the - # action name is in the list of hidden actions. - def action_method?(action_name) - !hidden_actions.include?(action_name) && super - end - - module ClassMethods - # Sets all of the actions passed in as hidden actions. - # - # ==== Parameters - # *args<#to_s>:: A list of actions - def hide_action(*args) - hidden_actions.merge(args.map! {|a| a.to_s }) - end - - # Overrides AbstractController::Base#action_methods to remove any methods - # that are listed as hidden methods. - def action_methods - @action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)}) - end - end - end -end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb deleted file mode 100644 index 2e73561f93..0000000000 --- a/actionpack/lib/action_controller/new_base/http.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'action_controller/abstract' -require 'active_support/core_ext/module/delegation' - -module ActionController - # ActionController::Http provides a way to get a valid Rack application from a controller. - # - # In AbstractController, dispatching is triggered directly by calling #process on a new controller. - # ActionController::Http provides an #action method that returns a valid Rack application for a - # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, - # can dispatch directly to the action returned by FooController.action(:index). - class Http < AbstractController::Base - abstract! - - # :api: public - attr_internal :params, :env - - # Returns the last part of the controller's name, underscored, without the ending - # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for - # controller_name - # - # ==== Returns - # String - def self.controller_name - @controller_name ||= controller_path.split("/").last - end - - # Delegates to the class' #controller_name - def controller_name - self.class.controller_name - end - - # Returns the full controller name, underscored, without the ending Controller. - # For instance, MyApp::MyPostsController would return "my_app/my_posts" for - # controller_name. - # - # ==== Returns - # String - def self.controller_path - @controller_path ||= self.name.sub(/Controller$/, '').underscore - end - - # Delegates to the class' #controller_path - def controller_path - self.class.controller_path - end - - # The details below can be overridden to support a specific - # Request and Response object. The default ActionController::Base - # implementation includes RackConvenience, which makes a request - # and response object available. You might wish to control the - # environment and response manually for performance reasons. - - attr_internal :status, :headers, :content_type - - def initialize(*) - @_headers = {} - super - end - - # Basic implementations for content_type=, location=, and headers are - # provided to reduce the dependency on the RackConvenience module - # in Renderer and Redirector. - - def content_type=(type) - headers["Content-Type"] = type.to_s - end - - def location=(url) - headers["Location"] = url - end - - # :api: private - def call(name, env) - @_env = env - process(name) - to_rack - end - - # :api: private - def to_rack - [status, headers, response_body] - end - - # Return a rack endpoint for the given action. Memoize the endpoint, so - # multiple calls into MyController.action will return the same object - # for the same action. - # - # ==== Parameters - # action<#to_s>:: An action name - # - # ==== Returns - # Proc:: A rack application - def self.action(name) - @actions ||= {} - @actions[name.to_s] ||= proc do |env| - new.call(name, env) - end - end - end -end diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb deleted file mode 100644 index ace4b148c9..0000000000 --- a/actionpack/lib/action_controller/new_base/layouts.rb +++ /dev/null @@ -1,195 +0,0 @@ -module ActionController - # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in - # repeated setups. The inclusion pattern has pages that look like this: - # - # <%= render "shared/header" %> - # Hello World - # <%= render "shared/footer" %> - # - # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose - # and if you ever want to change the structure of these two includes, you'll have to change all the templates. - # - # With layouts, you can flip it around and have the common structure know where to insert changing content. This means - # that the header and footer are only mentioned in one place, like this: - # - # // The header part of this layout - # <%= yield %> - # // The footer part of this layout - # - # And then you have content pages that look like this: - # - # hello world - # - # At rendering time, the content page is computed and then inserted in the layout, like this: - # - # // The header part of this layout - # hello world - # // The footer part of this layout - # - # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance - # variable. The preferred notation now is to use yield, as documented above. - # - # == Accessing shared variables - # - # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with - # references that won't materialize before rendering time: - # - #

<%= @page_title %>

- # <%= yield %> - # - # ...and content pages that fulfill these references _at_ rendering time: - # - # <% @page_title = "Welcome" %> - # Off-world colonies offers you a chance to start a new life - # - # The result after rendering is: - # - #

Welcome

- # Off-world colonies offers you a chance to start a new life - # - # == Layout assignment - # - # You can either specify a layout declaratively (using the #layout class method) or give - # it the same name as your controller, and place it in app/views/layouts. - # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance. - # - # For instance, if you have PostsController and a template named app/views/layouts/posts.html.erb, - # that template will be used for all actions in PostsController and controllers inheriting - # from PostsController. - # - # If you use a module, for instance Weblog::PostsController, you will need a template named - # app/views/layouts/weblog/posts.html.erb. - # - # Since all your controllers inherit from ApplicationController, they will use - # app/views/layouts/application.html.erb if no other layout is specified - # or provided. - # - # == Inheritance Examples - # - # class BankController < ActionController::Base - # layout "bank_standard" - # - # class InformationController < BankController - # - # class TellerController < BankController - # # teller.html.erb exists - # - # class TillController < TellerController - # - # class VaultController < BankController - # layout :access_level_layout - # - # class EmployeeController < BankController - # layout nil - # - # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites - # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. - # - # The TellerController uses +teller.html.erb+, and TillController inherits that layout and - # uses it as well. - # - # == Types of layouts - # - # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes - # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can - # be done either by specifying a method reference as a symbol or using an inline method (as a proc). - # - # The method reference is the preferred approach to variable layouts and is used like this: - # - # class WeblogController < ActionController::Base - # layout :writers_and_readers - # - # def index - # # fetching posts - # end - # - # private - # def writers_and_readers - # logged_in? ? "writer_layout" : "reader_layout" - # end - # - # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing - # is logged in or not. - # - # If you want to use an inline method, such as a proc, do something like this: - # - # class WeblogController < ActionController::Base - # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } - # - # Of course, the most common way of specifying a layout is still just as a plain template name: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. - # Otherwise, it will be looked up relative to the template root. - # - # == Conditional layouts - # - # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering - # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The - # :only and :except options can be passed to the layout call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard", :except => :rss - # - # # ... - # - # end - # - # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout - # around the rendered view. - # - # Both the :only and :except condition can accept an arbitrary number of method references, so - # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. - # - # == Using a different layout in the action render call - # - # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. - # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. - # You can do this by passing a :layout option to the render call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # def help - # render :action => "help", :layout => "help" - # end - # end - # - # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout. - module Layouts - extend ActiveSupport::Concern - - include ActionController::Renderer - include AbstractController::Layouts - - module ClassMethods - # If no layout is provided, look for a layout with this name. - def _implied_layout_name - controller_path - end - end - - private - def _determine_template(options) - super - - return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout) - layout = options.key?(:layout) ? options[:layout] : :none - options[:_layout] = _layout_for_option(layout, options[:_template].details) - end - - def _layout_for_option(name, details) - case name - when String then _layout_for_name(name, details) - when true then _default_layout(details, true) - when :none then _default_layout(details, false) - when false, nil then nil - else - raise ArgumentError, - "String, true, or false, expected for `layout'; you passed #{name.inspect}" - end - end - end -end diff --git a/actionpack/lib/action_controller/new_base/rack_convenience.rb b/actionpack/lib/action_controller/new_base/rack_convenience.rb deleted file mode 100644 index 805157b0e3..0000000000 --- a/actionpack/lib/action_controller/new_base/rack_convenience.rb +++ /dev/null @@ -1,33 +0,0 @@ -module ActionController - module RackConvenience - extend ActiveSupport::Concern - - included do - delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, :to => "@_response" - attr_internal :request, :response - end - - def call(name, env) - @_request = ActionDispatch::Request.new(env) - @_response = ActionDispatch::Response.new - @_response.request = request - super - end - - def params - @_params ||= @_request.parameters - end - - # :api: private - def to_rack - @_response.prepare! - @_response.to_a - end - - def response_body=(body) - response.body = body if response - super - end - end -end diff --git a/actionpack/lib/action_controller/new_base/redirector.rb b/actionpack/lib/action_controller/new_base/redirector.rb deleted file mode 100644 index 20060b001f..0000000000 --- a/actionpack/lib/action_controller/new_base/redirector.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionController - class RedirectBackError < AbstractController::Error #: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 - def redirect_to(url, status) #:doc: - raise AbstractController::DoubleRenderError if response_body - logger.info("Redirected to #{url}") if logger && logger.info? - self.status = status - self.location = url.gsub(/[\r\n]/, '') - self.response_body = "You are being redirected." - end - end -end diff --git a/actionpack/lib/action_controller/new_base/render_options.rb b/actionpack/lib/action_controller/new_base/render_options.rb deleted file mode 100644 index fc9a02626f..0000000000 --- a/actionpack/lib/action_controller/new_base/render_options.rb +++ /dev/null @@ -1,103 +0,0 @@ -module ActionController - module RenderOptions - extend ActiveSupport::Concern - - included do - extlib_inheritable_accessor :_renderers - self._renderers = [] - end - - module ClassMethods - def _write_render_options - renderers = _renderers.map do |r| - <<-RUBY_EVAL - if options.key?(:#{r}) - _process_options(options) - return _render_#{r}(options[:#{r}], options) - end - RUBY_EVAL - end - - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def _handle_render_options(options) - #{renderers.join} - end - RUBY_EVAL - end - - def _add_render_option(name) - _renderers << name - _write_render_options - end - end - - def render_to_body(options) - _handle_render_options(options) || super - end - end - - module RenderOption #:nodoc: - def self.extended(base) - base.extend ActiveSupport::Concern - base.send :include, ::ActionController::RenderOptions - - def base.register_renderer(name) - included { _add_render_option(name) } - end - end - end - - module Renderers - module Json - extend RenderOption - register_renderer :json - - def _render_json(json, options) - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - self.content_type ||= Mime::JSON - self.response_body = json - end - end - - module Js - extend RenderOption - register_renderer :js - - def _render_js(js, options) - self.content_type ||= Mime::JS - self.response_body = js - end - end - - module Xml - extend RenderOption - register_renderer :xml - - def _render_xml(xml, options) - self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml - end - end - - module RJS - extend RenderOption - register_renderer :update - - def _render_update(proc, options) - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc) - self.content_type = Mime::JS - self.response_body = generator.to_s - end - end - - module All - extend ActiveSupport::Concern - - include ActionController::Renderers::Json - include ActionController::Renderers::Js - include ActionController::Renderers::Xml - include ActionController::Renderers::RJS - end - end -end diff --git a/actionpack/lib/action_controller/new_base/renderer.rb b/actionpack/lib/action_controller/new_base/renderer.rb deleted file mode 100644 index 2fab501302..0000000000 --- a/actionpack/lib/action_controller/new_base/renderer.rb +++ /dev/null @@ -1,78 +0,0 @@ -module ActionController - module Renderer - extend ActiveSupport::Concern - - include AbstractController::Renderer - - def process_action(*) - self.formats = request.formats.map {|x| x.to_sym} - super - end - - def render(options) - super - options[:_template] ||= _action_view._partial - self.content_type ||= begin - mime = options[:_template].mime_type - formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first) - end - response_body - end - - def render_to_body(options) - _process_options(options) - - if options.key?(:partial) - _render_partial(options[:partial], options) - end - - super - end - - private - def _prefix - controller_path - end - - def _determine_template(options) - if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first) - elsif options.key?(:inline) - handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") - template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) - options[:_template] = template - elsif options.key?(:template) - options[:_template_name] = options[:template] - elsif options.key?(:file) - options[:_template_name] = options[:file] - elsif !options.key?(:partial) - options[:_template_name] = (options[:action] || action_name).to_s - options[:_prefix] = _prefix - end - - super - end - - def _render_partial(partial, options) - case partial - when true - options[:_prefix] = _prefix - when String - options[:_prefix] = _prefix unless partial.index('/') - options[:_template_name] = partial - else - options[:_partial_object] = true - return - end - - options[:_partial] = options[:object] || true - end - - def _process_options(options) - status, content_type, location = options.values_at(:status, :content_type, :location) - self.status = status if status - self.content_type = content_type if content_type - self.headers["Location"] = url_for(location) if location - end - end -end diff --git a/actionpack/lib/action_controller/new_base/rescuable.rb b/actionpack/lib/action_controller/new_base/rescuable.rb deleted file mode 100644 index 029e643d93..0000000000 --- a/actionpack/lib/action_controller/new_base/rescuable.rb +++ /dev/null @@ -1,52 +0,0 @@ -module ActionController #:nodoc: - # Actions that fail to perform as expected throw exceptions. These - # exceptions can either be rescued for the public view (with a nice - # user-friendly explanation) or for the developers view (with tons of - # debugging information). The developers view is already implemented by - # the Action Controller, but the public view should be tailored to your - # specific application. - # - # The default behavior for public exceptions is to render a static html - # file with the name of the error code thrown. If no such file exists, an - # empty response is sent with the correct status code. - # - # You can override what constitutes a local request by overriding the - # local_request? method in your own controller. Custom rescue - # behavior is achieved by overriding the rescue_action_in_public - # and rescue_action_locally methods. - module Rescue - extend ActiveSupport::Concern - - included do - include ActiveSupport::Rescuable - end - - module ClassMethods - # This can be removed once we can move action(:_rescue_action) into middlewares.rb - # Currently, it does controller.method(:rescue_action), which is hiding the implementation - # difference between the old and new base. - def rescue_action(env) - action(:_rescue_action).call(env) - end - end - - attr_internal :rescued_exception - - private - def method_for_action(action_name) - return action_name if self.rescued_exception = request.env.delete("action_dispatch.rescue.exception") - super - end - - def _rescue_action - rescue_with_handler(rescued_exception) || raise(rescued_exception) - end - - def process_action(*) - super - rescue Exception => exception - self.rescued_exception = exception - _rescue_action - end - end -end diff --git a/actionpack/lib/action_controller/new_base/session.rb b/actionpack/lib/action_controller/new_base/session.rb deleted file mode 100644 index bcedd6e1c7..0000000000 --- a/actionpack/lib/action_controller/new_base/session.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ActionController - module Session - extend ActiveSupport::Concern - - include RackConvenience - - def session - @_request.session - end - - def reset_session - @_request.reset_session - end - end -end diff --git a/actionpack/lib/action_controller/new_base/testing.rb b/actionpack/lib/action_controller/new_base/testing.rb deleted file mode 100644 index a4a1116d9e..0000000000 --- a/actionpack/lib/action_controller/new_base/testing.rb +++ /dev/null @@ -1,39 +0,0 @@ -module ActionController - module Testing - extend ActiveSupport::Concern - - include RackConvenience - - # OMG MEGA HAX - def process_with_new_base_test(request, response) - @_request = request - @_response = response - @_response.request = request - ret = process(request.parameters[:action]) - @_response.body ||= self.response_body - @_response.prepare! - set_test_assigns - ret - end - - def set_test_assigns - @assigns = {} - (instance_variable_names - self.class.protected_instance_variables).each do |var| - name, value = var[1..-1], instance_variable_get(var) - @assigns[name] = value - end - end - - # TODO : Rewrite tests using controller.headers= to use Rack env - def headers=(new_headers) - @_response ||= ActionDispatch::Response.new - @_response.headers.replace(new_headers) - end - - module ClassMethods - def before_filters - _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name} - end - end - end -end diff --git a/actionpack/lib/action_controller/new_base/url_for.rb b/actionpack/lib/action_controller/new_base/url_for.rb deleted file mode 100644 index 7119c14cd3..0000000000 --- a/actionpack/lib/action_controller/new_base/url_for.rb +++ /dev/null @@ -1,49 +0,0 @@ -module ActionController - module UrlFor - extend ActiveSupport::Concern - - include RackConvenience - - def process_action(*) - initialize_current_url - super - end - - def initialize_current_url - @url = UrlRewriter.new(request, params.clone) - end - - # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in - # the form of a hash, just like the one you would use for url_for directly. Example: - # - # def default_url_options(options) - # { :project => @project.active? ? @project.url_name : "unknown" } - # end - # - # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the - # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set - # by this method. - def default_url_options(options = nil) - end - - def rewrite_options(options) #:nodoc: - if defaults = default_url_options(options) - defaults.merge(options) - else - options - end - end - - def url_for(options = {}) - options ||= {} - case options - when String - options - when Hash - @url.rewrite(rewrite_options(options)) - else - polymorphic_url(options) - end - end - end -end diff --git a/actionpack/lib/action_controller/old_base.rb b/actionpack/lib/action_controller/old_base.rb new file mode 100644 index 0000000000..dd22bfd617 --- /dev/null +++ b/actionpack/lib/action_controller/old_base.rb @@ -0,0 +1,84 @@ +#-- +# Copyright (c) 2004-2009 David Heinemeier Hansson +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#++ + +activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" +$:.unshift activesupport_path if File.directory?(activesupport_path) +require 'active_support' + +require File.join(File.dirname(__FILE__), "action_pack") + +module ActionController + # TODO: Review explicit to see if they will automatically be handled by + # the initilizer if they are really needed. + def self.load_all! + [Base, Request, Response, UrlRewriter, UrlWriter] + [ActionDispatch::Http::Headers] + end + + autoload :Base, 'action_controller/base/base' + autoload :Benchmarking, 'action_controller/base/chained/benchmarking' + autoload :Caching, 'action_controller/caching' + autoload :Cookies, 'action_controller/base/cookies' + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :Filters, 'action_controller/base/chained/filters' + autoload :Flash, 'action_controller/base/chained/flash' + autoload :Helpers, 'action_controller/base/helpers' + autoload :HttpAuthentication, 'action_controller/base/http_authentication' + autoload :Integration, 'action_controller/testing/integration' + autoload :IntegrationTest, 'action_controller/testing/integration' + autoload :Layout, 'action_controller/base/layout' + autoload :MimeResponds, 'action_controller/base/mime_responds' + autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' + autoload :RecordIdentifier, 'action_controller/record_identifier' + autoload :Redirector, 'action_controller/base/redirect' + autoload :Renderer, 'action_controller/base/render' + autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' + autoload :Rescue, 'action_controller/base/rescue' + autoload :Resources, 'action_controller/routing/resources' + autoload :Responder, 'action_controller/base/responder' + autoload :Routing, 'action_controller/routing' + autoload :SessionManagement, 'action_controller/base/session_management' + autoload :Streaming, 'action_controller/base/streaming' + autoload :TestCase, 'action_controller/testing/test_case' + autoload :TestProcess, 'action_controller/testing/process' + autoload :Translation, 'action_controller/translation' + autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser' + autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' + autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' + autoload :Verification, 'action_controller/base/verification' + autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' + + module Assertions + autoload :DomAssertions, 'action_controller/testing/assertions/dom' + autoload :ModelAssertions, 'action_controller/testing/assertions/model' + autoload :ResponseAssertions, 'action_controller/testing/assertions/response' + autoload :RoutingAssertions, 'action_controller/testing/assertions/routing' + autoload :SelectorAssertions, 'action_controller/testing/assertions/selector' + autoload :TagAssertions, 'action_controller/testing/assertions/tag' + end +end + +autoload :HTML, 'action_controller/vendor/html-scanner' + +require 'action_dispatch' +require 'action_view' diff --git a/actionpack/lib/action_controller/old_base/base.rb b/actionpack/lib/action_controller/old_base/base.rb new file mode 100644 index 0000000000..67369eb122 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/base.rb @@ -0,0 +1,884 @@ +require 'action_controller/deprecated' +require 'set' +require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/module/attr_internal' + +module ActionController #:nodoc: + class ActionControllerError < StandardError #:nodoc: + end + + class SessionRestoreError < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class RoutingError < ActionControllerError #:nodoc: + attr_reader :failures + def initialize(message, failures=[]) + super(message) + @failures = failures + end + end + + class MethodNotAllowed < ActionControllerError #:nodoc: + attr_reader :allowed_methods + + def initialize(*allowed_methods) + super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") + @allowed_methods = allowed_methods + end + + def allowed_methods_header + allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' + end + end + + class NotImplemented < MethodNotAllowed #:nodoc: + end + + class UnknownController < ActionControllerError #:nodoc: + end + + class UnknownAction < ActionControllerError #:nodoc: + end + + class MissingFile < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class SessionOverflowError < ActionControllerError #:nodoc: + DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + class UnknownHttpMethod < ActionControllerError #:nodoc: + end + + # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed + # on request and then either render a template or redirect to another action. An action is defined as a public method + # on the controller, which will automatically be made accessible to the web-server through Rails Routes. + # + # A sample controller could look like this: + # + # class GuestBookController < ActionController::Base + # def index + # @entries = Entry.find(:all) + # end + # + # def sign + # Entry.create(params[:entry]) + # redirect_to :action => "index" + # end + # end + # + # Actions, by default, render a template in the app/views directory corresponding to the name of the controller and action + # after executing code in the action. For example, the +index+ action of the GuestBookController would render the + # template app/views/guestbook/index.erb by default after populating the @entries instance variable. + # + # Unlike index, the sign action will not render a template. After performing its main purpose (creating a + # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external + # "302 Moved" HTTP response that takes the user to the index action. + # + # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. + # Most actions are variations of these themes. + # + # == Requests + # + # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters. + # This value should hold the name of the action to be performed. Once the action has been identified, the remaining + # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to + # the action through instance variables. Then the action is performed. + # + # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries + # are made by accessing the environment hash, like this: + # + # def server_ip + # location = request.env["SERVER_ADDR"] + # render :text => "This server hosted at #{location}" + # end + # + # == Parameters + # + # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method + # which returns a hash. For example, an action that was performed through /weblog/list?category=All&limit=5 will include + # { "category" => "All", "limit" => 5 } in params. + # + # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as: + # + # + # + # + # A request stemming from a form holding these inputs will include { "post" => { "name" => "david", "address" => "hyacintvej" } }. + # If the address input had been named "post[address][street]", the params would have included + # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting. + # + # == Sessions + # + # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted, + # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such + # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely + # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at. + # + # You can place objects in the session by using the session method, which accesses a hash: + # + # session[:person] = Person.authenticate(user_name, password) + # + # And retrieved again through the same hash: + # + # Hello #{session[:person]} + # + # For removing objects from the session, you can either assign a single key to +nil+: + # + # # removes :person from session + # session[:person] = nil + # + # or you can remove the entire session with +reset_session+. + # + # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted. + # This prevents the user from tampering with the session but also allows him to see its contents. + # + # Do not put secret information in cookie-based sessions! + # + # Other options for session storage are: + # + # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and, + # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set + # + # config.action_controller.session_store = :active_record_store + # + # in your config/environment.rb and run rake db:sessions:create. + # + # * MemCacheStore - Sessions are stored as entries in your memcached cache. + # Set the session store type in config/environment.rb: + # + # config.action_controller.session_store = :mem_cache_store + # + # This assumes that memcached has been installed and configured properly. + # See the MemCacheStore docs for more information. + # + # == Responses + # + # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response + # object is generated automatically through the use of renders and redirects and requires no user intervention. + # + # == Renders + # + # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering + # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured. + # The controller passes objects to the view by assigning instance variables: + # + # def show + # @post = Post.find(params[:id]) + # end + # + # Which are then automatically available to the view: + # + # Title: <%= @post.title %> + # + # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use + # the manual rendering methods: + # + # def search + # @results = Search.find(params[:query]) + # case @results + # when 0 then render :action => "no_results" + # when 1 then render :action => "show" + # when 2..10 then render :action => "show_many" + # end + # end + # + # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html. + # + # == Redirects + # + # Redirects are used to move from one action to another. For example, after a create action, which stores a blog entry to a database, + # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to) + # a show action that we'll assume has already been created. The code might look like this: + # + # def create + # @entry = Entry.new(params[:entry]) + # if @entry.save + # # The entry was saved correctly, redirect to show + # redirect_to :action => 'show', :id => @entry.id + # else + # # things didn't go so well, do something else + # end + # end + # + # In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed. + # + # == Calling multiple redirects or renders + # + # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError: + # + # def do_something + # redirect_to :action => "elsewhere" + # render :action => "overthere" # raises DoubleRenderError + # end + # + # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution. + # + # def do_something + # redirect_to(:action => "elsewhere") and return if monkeys.nil? + # render :action => "overthere" # won't be called if monkeys is nil + # end + # + class Base + + include ActionDispatch::StatusCodes + + cattr_reader :protected_instance_variables + # Controller specific instance variables which will not be accessible inside views. + @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller + @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params + @_flash @_response) + + # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, + # and images to a dedicated asset server away from the main web server. Example: + # ActionController::Base.asset_host = "http://assets.example.com" + cattr_accessor :asset_host + + # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors. + # When the application is ready to go public, this should be set to false, and the protected method local_request? + # should instead be implemented in the controller to determine when debugging screens should be shown. + @@consider_all_requests_local = true + cattr_accessor :consider_all_requests_local + + # Indicates whether to allow concurrent action processing. Your + # controller actions and any other code they call must also behave well + # when called from concurrent threads. Turned off by default. + @@allow_concurrency = false + cattr_accessor :allow_concurrency + + # Modern REST web services often need to submit complex data to the web application. + # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the + # params hash. These handlers are invoked for POST and PUT requests. + # + # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated + # in the params. This allows XML requests to mask themselves as regular form submissions, so you can have one + # action serve both regular forms and web service requests. + # + # Example of doing your own parser for a custom content type: + # + # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data| + # node = REXML::Document.new(post) + # { node.root.name => node.root } + # end + # + # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the + # root node for such requests. The new default is to keep the root, such that "David" results + # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can + # re-register XmlSimple as application/xml handler ike this: + # + # ActionController::Base.param_parsers[Mime::XML] = + # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) } + # + # A YAML parser is also available and can be turned on with: + # + # ActionController::Base.param_parsers[Mime::YAML] = :yaml + @@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. + @@default_charset = "utf-8" + cattr_accessor :default_charset + + # The logger is used for generating information on the action run-time (including benchmarking) if available. + # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers. + cattr_accessor :logger + + # Controls the resource action separator + @@resource_action_separator = "/" + cattr_accessor :resource_action_separator + + # Allow to override path names for default resources' actions + @@resources_path_names = { :new => 'new', :edit => 'edit' } + cattr_accessor :resources_path_names + + # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ + # sets it to :authenticity_token by default. + cattr_accessor :request_forgery_protection_token + + # Controls the IP Spoofing check when determining the remote IP. + @@ip_spoofing_check = true + cattr_accessor :ip_spoofing_check + + # Indicates whether or not optimise the generated named + # route helper methods + cattr_accessor :optimise_named_routes + self.optimise_named_routes = true + + # Indicates whether the response format should be determined by examining the Accept HTTP header, + # or by using the simpler params + ajax rules. + # + # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept + # header into account. If it is set to false then the request format will be determined solely + # by examining params[:format]. If params format is missing, the format will be either HTML or + # Javascript depending on whether the request is an AJAX request. + cattr_accessor :use_accept_header + self.use_accept_header = true + + # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + class_inheritable_accessor :allow_forgery_protection + self.allow_forgery_protection = true + + # If you are deploying to a subdirectory, you will need to set + # config.action_controller.relative_url_root + # This defaults to ENV['RAILS_RELATIVE_URL_ROOT'] + cattr_accessor :relative_url_root + self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] + + # Holds the request object that's primarily used to get environment variables through access like + # request.env["REQUEST_URI"]. + attr_internal :request + + # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like params["post_id"] + # to get the post_id. No type casts are made, so all values are returned as strings. + attr_internal :params + + # Holds the response object that's primarily used to set additional HTTP headers through access like + # response.headers["Cache-Control"] = "no-cache". Can also be used to access the final body HTML after a template + # has been rendered through response.body -- useful for after_filters that wants to manipulate the output, + # such as a OutputCompressionFilter. + attr_internal :response + + # Holds a hash of objects in the session. Accessed like session[:person] to get the object tied to the "person" + # key. The session will hold any type of object as values, but the key should be a string or symbol. + def session + request.session + end + + # Holds a hash of header names and values. Accessed like headers["Cache-Control"] to get the value of the Cache-Control + # directive. Values should always be specified as strings. + attr_internal :headers + + # Returns the name of the action this controller is processing. + attr_accessor :action_name + + attr_reader :template + + def action(name, env) + request = ActionDispatch::Request.new(env) + response = ActionDispatch::Response.new + self.action_name = name && name.to_s + process(request, response).to_a + end + + + class << self + def action(name = nil) + @actions ||= {} + @actions[name] ||= proc do |env| + new.action(name, env) + end + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". + def controller_class_name + @controller_class_name ||= name.demodulize + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". + def controller_name + @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". + def controller_path + @controller_path ||= name.gsub(/Controller$/, '').underscore + end + + # Return an array containing the names of public methods that have been marked hidden from the action processor. + # By default, all methods defined in ActionController::Base and included modules are hidden. + # More methods can be hidden using hide_action. + def hidden_actions + read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, []) + end + + # Hide each of the given methods from being callable as actions. + def hide_action(*names) + write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s }) + end + + # View load paths determine the bases from which template references can be made. So a call to + # render("test/template") will be looked up in the view load paths array and the closest match will be + # returned. + def view_paths + if defined? @view_paths + @view_paths + else + superclass.view_paths + end + end + + def view_paths=(value) + @view_paths = ActionView::Base.process_view_paths(value) if value + end + + # Adds a view_path to the front of the view_paths array. + # If the current class has no view paths, copy them from + # the superclass. This change will be visible for all future requests. + # + # ArticleController.prepend_view_path("views/default") + # ArticleController.prepend_view_path(["views/default", "views/custom"]) + # + def prepend_view_path(path) + @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil? + @view_paths.unshift(*path) + end + + # Adds a view_path to the end of the view_paths array. + # If the current class has no view paths, copy them from + # the superclass. This change will be visible for all future requests. + # + # ArticleController.append_view_path("views/default") + # ArticleController.append_view_path(["views/default", "views/custom"]) + # + def append_view_path(path) + @view_paths = superclass.view_paths.dup if @view_paths.nil? + @view_paths.push(*path) + end + + @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] + + def exempt_from_layout(*types) + types.each do |type| + @@exempt_from_layout << + ActionView::Template.handler_class_for_extension(type) + end + + @@exempt_from_layout + end + + end + + public + def call(env) + request = ActionDispatch::Request.new(env) + response = ActionDispatch::Response.new + process(request, response).to_a + end + + # Extracts the action_name from the request parameters and performs that action. + def process(request, response, method = :perform_action, *arguments) #:nodoc: + response.request = request + + assign_shortcuts(request, response) + initialize_template_class(response) + initialize_current_url + + log_processing + send(method, *arguments) + + send_response + ensure + process_cleanup + end + + def send_response + response.prepare! + response + end + + # Returns a URL that has been rewritten according to the options hash and the defined routes. + # (For doing a complete redirect, use +redirect_to+). + # + # url_for is used to: + # + # All keys given to +url_for+ are forwarded to the Route module, save for the following: + # * :anchor - Specifies the anchor name to be appended to the path. For example, + # url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments' + # will produce "/posts/show/10#comments". + # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (false by default). + # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this + # is currently not recommended since it breaks caching. + # * :host - Overrides the default (current) host if provided. + # * :protocol - Overrides the default (current) protocol if provided. + # * :port - Optionally specify the port to connect to. + # * :user - Inline HTTP authentication (only plucked out if :password is also present). + # * :password - Inline HTTP authentication (only plucked out if :user is also present). + # * :skip_relative_url_root - If true, the url is not constructed using the +relative_url_root+ + # of the request so the path will include the web server relative installation directory. + # + # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the and a query string. + # Routes composes a query string as the key/value pairs not included in the . + # + # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with + # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs: + # + # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent' + # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts' + # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts' + # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10' + # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts' + # + # When generating a new URL, missing values may be filled in from the current request's parameters. For example, + # url_for :action => 'some_action' will retain the current controller, as expected. This behavior extends to + # other parameters, including :controller, :id, and any other parameters that are placed into a Route's + # path. + #   + # The URL helpers such as url_for have a limited form of memory: when generating a new URL, they can look for + # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be + # taken from the defaults. There are a few simple rules on how this is performed: + # + # * If the controller name begins with a slash no defaults are used: + # + # url_for :controller => '/home' + # + # In particular, a leading slash ensures no namespace is assumed. Thus, + # while url_for :controller => 'users' may resolve to + # Admin::UsersController if the current controller lives under + # that module, url_for :controller => '/users' ensures you link + # to ::UsersController no matter what. + # * If the controller changes, the action will default to index unless provided + # + # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the + # route given by map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'. + # + # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated + # from this page. + # + # * url_for :action => 'bio' -- During the generation of this URL, default values will be used for the first and + # last components, and the action shall change. The generated URL will be, "people/hh/david/bio". + # * url_for :first => 'davids-little-brother' This generates the URL 'people/hh/davids-little-brother' -- note + # that this URL leaves out the assumed action of 'bio'. + # + # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The + # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the + # value that appears in the slot for :first is not equal to default value for :first we stop using + # defaults. On its own, this rule can account for much of the typical Rails URL behavior. + #   + # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired. + # The default may be cleared by adding :name => nil to url_for's options. + # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the + # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is + # displayed on: + # + # url_for :controller => 'posts', :action => nil + # + # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the + # :overwrite_params options. Say for your posts you have different views for showing and printing them. + # Then, in the show view, you get the URL for the print view like this + # + # url_for :overwrite_params => { :action => 'print' } + # + # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print' + # would have slashed-off the path components after the changed action. + def url_for(options = {}) + options ||= {} + case options + when String + options + when Hash + @url.rewrite(rewrite_options(options)) + else + polymorphic_url(options) + end + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". + def controller_class_name + self.class.controller_class_name + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". + def controller_name + self.class.controller_name + end + + # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". + def controller_path + self.class.controller_path + end + + def session_enabled? + ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller) + end + + self.view_paths = [] + + # View load paths for controller. + def view_paths + @template.view_paths + end + + def view_paths=(value) + @template.view_paths = ActionView::Base.process_view_paths(value) + end + + # Adds a view_path to the front of the view_paths array. + # This change affects the current request only. + # + # self.prepend_view_path("views/default") + # self.prepend_view_path(["views/default", "views/custom"]) + # + def prepend_view_path(path) + @template.view_paths.unshift(*path) + end + + # Adds a view_path to the end of the view_paths array. + # This change affects the current request only. + # + # self.append_view_path("views/default") + # self.append_view_path(["views/default", "views/custom"]) + # + def append_view_path(path) + @template.view_paths.push(*path) + end + + def rewrite_options(options) #:nodoc: + if defaults = default_url_options(options) + defaults.merge(options) + else + options + end + end + + # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in + # the form of a hash, just like the one you would use for url_for directly. Example: + # + # def default_url_options(options) + # { :project => @project.active? ? @project.url_name : "unknown" } + # end + # + # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the + # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set + # by this method. + def default_url_options(options = nil) + 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, + # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. + # + # Parameters: + # * :etag + # * :last_modified + # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(:etag => @article, :last_modified => @article.created_at.utc) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + def stale?(options) + fresh_when(options) + !request.fresh?(response) + end + + # Sets the etag, last_modified, or both on the response and renders a + # "304 Not Modified" response if the request is already fresh. + # + # Parameters: + # * :etag + # * :last_modified + # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) + # end + # + # This will render the show template if the request isn't sending a matching etag or + # If-Modified-Since header and just a "304 Not Modified" response if there's a match. + # + def fresh_when(options) + options.assert_valid_keys(:etag, :last_modified, :public) + + response.etag = options[:etag] if options[:etag] + response.last_modified = options[:last_modified] if options[:last_modified] + + if options[:public] + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + cache_control.delete("private") + cache_control.delete("no-cache") + cache_control << "public" + response.headers["Cache-Control"] = cache_control.join(', ') + end + + if request.fresh?(response) + head :not_modified + end + end + + # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that + # intermediate caches shouldn't cache the response. + # + # Examples: + # expires_in 20.minutes + # expires_in 3.hours, :public => true + # expires in 3.hours, 'max-stale' => 5.hours, :public => true + # + # This method will overwrite an existing Cache-Control header. + # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. + def expires_in(seconds, options = {}) #:doc: + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + + cache_control << "max-age=#{seconds}" + cache_control.delete("no-cache") + if options[:public] + cache_control.delete("private") + cache_control << "public" + else + cache_control << "private" + end + + # This allows for additional headers to be passed through like 'max-stale' => 5.hours + cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} + + response.headers["Cache-Control"] = cache_control.join(', ') + end + + # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or + # intermediate caches (like caching proxy servers). + def expires_now #:doc: + response.headers["Cache-Control"] = "no-cache" + end + + # Resets the session by clearing out all the objects stored within and initializing a new session object. + def reset_session #:doc: + request.reset_session + end + + private + def _process_options(options) + 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 + + response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE) + end + + def initialize_template_class(response) + @template = ActionView::Base.new(self.class.view_paths, {}, self, formats) + response.template = @template if response.respond_to?(:template=) + @template.helpers.send :include, self.class.master_helper_module + @performed_render = @performed_redirect = false + end + + def assign_shortcuts(request, response) + @_request, @_response, @_params = request, response, request.parameters + @_headers = @_response.headers + end + + def initialize_current_url + @url = UrlRewriter.new(request, params.clone) + end + + def log_processing + if logger && logger.info? + log_processing_for_request_id + log_processing_for_parameters + end + end + + def log_processing_for_request_id + request_id = "\n\nProcessing #{self.class.name}\##{action_name} " + request_id << "to #{params[:format]} " if params[:format] + request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]" + + logger.info(request_id) + end + + def default_render #:nodoc: + render + end + + def perform_action + 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 + view_paths.find_by_parts(action_name, {:formats => formats, :locales => [I18n.locale]}, controller_path) + rescue => e + raise UnknownAction, "No action responded to #{action_name}. Actions: " + + "#{action_methods.sort.to_sentence}", caller + end + + default_render + end + + # Returns true if a render or redirect has already been performed. + def performed? + @performed_render || @performed_redirect + end + + def reset_variables_added_to_assigns + @template.instance_variable_set("@assigns_added", nil) + end + + def request_origin + # this *needs* to be cached! + # otherwise you'd get different results if calling it more than once + @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" + end + + # Returns the request URI used to get to the current location + def complete_request_uri + "#{request.protocol}#{request.host}#{request.request_uri}" + end + + def default_template(action_name = self.action_name) + self.view_paths.find_template(default_template_name(action_name), default_template_format) + end + + def default_template_name(action_name = self.action_name) + if action_name + action_name = action_name.to_s + if action_name.include?('/') && template_path_includes_controller?(action_name) + action_name = strip_out_controller(action_name) + end + end + "#{controller_path}/#{action_name}" + end + + def strip_out_controller(path) + path.split('/', 2).last + end + + def template_path_includes_controller?(path) + self.controller_path.split('/')[-1] == path.split('/')[0] + end + + def process_cleanup + end + end + + Base.class_eval do + [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers, + Cookies, Caching, Verification, Streaming, SessionManagement, + HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier, + RequestForgeryProtection, Translation, FilterParameterLogging + ].each do |mod| + include mod + end + end +end diff --git a/actionpack/lib/action_controller/old_base/chained/benchmarking.rb b/actionpack/lib/action_controller/old_base/chained/benchmarking.rb new file mode 100644 index 0000000000..57a1ac8314 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/chained/benchmarking.rb @@ -0,0 +1,107 @@ +require 'active_support/core_ext/benchmark' + +module ActionController #:nodoc: + # The benchmarking module times the performance of actions and reports to the logger. If the Active Record + # package has been included, a separate timing section for database calls will be added as well. + module Benchmarking #:nodoc: + def self.included(base) + base.extend(ClassMethods) + + base.class_eval do + alias_method_chain :perform_action, :benchmark + alias_method_chain :render, :benchmark + end + end + + module ClassMethods + # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it + # (unless use_silence is set to false). + # + # The benchmark is only recorded if the current level of the logger matches the log_level, which makes it + # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark + # will only be conducted if the log level is low enough. + def benchmark(title, log_level = Logger::DEBUG, use_silence = true) + if logger && logger.level >= log_level + result = nil + ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } + logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)") + result + else + yield + end + end + + # Silences the logger for the duration of the block. + def silence + old_logger_level, logger.level = logger.level, Logger::ERROR if logger + yield + ensure + logger.level = old_logger_level if logger + end + end + + protected + def render_with_benchmark(options = nil, extra_options = {}, &block) + if logger + if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + db_runtime = ActiveRecord::Base.connection.reset_runtime + end + + render_output = nil + @view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) } + + if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + @db_rt_before_render = db_runtime + @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime + @view_runtime -= @db_rt_after_render + end + + render_output + else + render_without_benchmark(options, extra_options, &block) + end + end + + private + def perform_action_with_benchmark + 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? + + log_message = 'Completed in %.0fms' % ms + + if logging_view || logging_active_record + log_message << " (" + log_message << view_runtime if logging_view + + if logging_active_record + log_message << ", " if logging_view + log_message << active_record_runtime + ")" + else + ")" + end + end + + log_message << " | #{response.status}" + log_message << " [#{complete_request_uri rescue "unknown"}]" + + logger.info(log_message) + response.headers["X-Runtime"] = "%.0f" % ms + else + perform_action_without_benchmark + end + end + + def view_runtime + "View: %.0f" % @view_runtime + end + + def active_record_runtime + db_runtime = ActiveRecord::Base.connection.reset_runtime + db_runtime += @db_rt_before_render if @db_rt_before_render + db_runtime += @db_rt_after_render if @db_rt_after_render + "DB: %.0f" % db_runtime + end + end +end diff --git a/actionpack/lib/action_controller/old_base/chained/filters.rb b/actionpack/lib/action_controller/old_base/chained/filters.rb new file mode 100644 index 0000000000..f528dd0686 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/chained/filters.rb @@ -0,0 +1,670 @@ +module ActionController #:nodoc: + module Filters #:nodoc: + extend ActiveSupport::Concern + + class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc: + def append_filter_to_chain(filters, filter_type, &block) + pos = find_filter_append_position(filters, filter_type) + update_filter_chain(filters, filter_type, pos, &block) + end + + def prepend_filter_to_chain(filters, filter_type, &block) + pos = find_filter_prepend_position(filters, filter_type) + update_filter_chain(filters, filter_type, pos, &block) + end + + def create_filters(filters, filter_type, &block) + filters, conditions = extract_options(filters, &block) + filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) } + filters + end + + def skip_filter_in_chain(*filters, &test) + filters, conditions = extract_options(filters) + filters.each do |filter| + if callback = find(filter) then delete(callback) end + end if conditions.empty? + update_filter_in_chain(filters, :skip => conditions, &test) + end + + private + def update_filter_chain(filters, filter_type, pos, &block) + new_filters = create_filters(filters, filter_type, &block) + insert(pos, new_filters).flatten! + end + + def find_filter_append_position(filters, filter_type) + # appending an after filter puts it at the end of the call chain + # before and around filters go before the first after filter in the chain + unless filter_type == :after + each_with_index do |f,i| + return i if f.after? + end + end + return -1 + end + + def find_filter_prepend_position(filters, filter_type) + # prepending a before or around filter puts it at the front of the call chain + # after filters go before the first after filter in the chain + if filter_type == :after + each_with_index do |f,i| + return i if f.after? + end + return -1 + end + return 0 + end + + def find_or_create_filter(filter, filter_type, options = {}) + update_filter_in_chain([filter], options) + + if found_filter = find(filter) { |f| f.type == filter_type } + found_filter + else + filter_kind = case + when filter.respond_to?(:before) && filter_type == :before + :before + when filter.respond_to?(:after) && filter_type == :after + :after + else + :filter + end + + case filter_type + when :before + BeforeFilter.new(filter_kind, filter, options) + when :after + AfterFilter.new(filter_kind, filter, options) + else + AroundFilter.new(filter_kind, filter, options) + end + end + end + + def update_filter_in_chain(filters, options, &test) + filters.map! { |f| block_given? ? find(f, &test) : find(f) } + filters.compact! + + map! do |filter| + if filters.include?(filter) + new_filter = filter.dup + new_filter.update_options!(options) + new_filter + else + filter + end + end + end + end + + class Filter < ActiveSupport::Callbacks::Callback #:nodoc: + def initialize(kind, method, options = {}) + super + update_options! options + end + + # override these to return true in appropriate subclass + def before? + false + end + + def after? + false + end + + def around? + false + end + + # Make sets of strings from :only/:except options + def update_options!(other) + if other + convert_only_and_except_options_to_sets_of_strings(other) + if other[:skip] + convert_only_and_except_options_to_sets_of_strings(other[:skip]) + end + end + + options.update(other) + end + + private + def should_not_skip?(controller) + if options[:skip] + !included_in_action?(controller, options[:skip]) + else + true + end + end + + def included_in_action?(controller, options) + if options[:only] + options[:only].include?(controller.action_name) + elsif options[:except] + !options[:except].include?(controller.action_name) + else + true + end + end + + def should_run_callback?(controller) + should_not_skip?(controller) && included_in_action?(controller, options) && super + end + + def convert_only_and_except_options_to_sets_of_strings(opts) + [:only, :except].each do |key| + if values = opts[key] + opts[key] = Array(values).map {|val| val.to_s }.to_set + end + end + end + end + + class AroundFilter < Filter #:nodoc: + def type + :around + end + + def around? + true + end + + def call(controller, &block) + if should_run_callback?(controller) + method = filter_responds_to_before_and_after? ? around_proc : self.method + + # For around_filter do |controller, action| + if method.is_a?(Proc) && method.arity == 2 + evaluate_method(method, controller, block) + else + evaluate_method(method, controller, &block) + end + else + block.call + end + end + + private + def filter_responds_to_before_and_after? + method.respond_to?(:before) && method.respond_to?(:after) + end + + def around_proc + Proc.new do |controller, action| + method.before(controller) + + if controller.__send__(:performed?) + controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) + else + begin + action.call + ensure + method.after(controller) + end + end + end + end + end + + class BeforeFilter < Filter #:nodoc: + def type + :before + end + + def before? + true + end + + def call(controller, &block) + super + if controller.__send__(:performed?) + controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) + end + end + end + + class AfterFilter < Filter #:nodoc: + def type + :after + end + + def after? + true + end + end + + # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do + # authentication, caching, or auditing before the intended action is performed. Or to do localization or output + # compression after the action has been performed. Filters have access to the request, response, and all the instance + # variables set by other filters in the chain or by the action (in the case of after filters). + # + # == Filter inheritance + # + # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without + # affecting the superclass. For example: + # + # class BankController < ActionController::Base + # before_filter :audit + # + # private + # def audit + # # record the action and parameters in an audit log + # end + # end + # + # class VaultController < BankController + # before_filter :verify_credentials + # + # private + # def verify_credentials + # # make sure the user is allowed into the vault + # end + # end + # + # Now any actions performed on the BankController will have the audit method called before. On the VaultController, + # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then + # verify_credentials and the intended action are never called. + # + # == Filter types + # + # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first + # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of + # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form. + # + # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes + # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example: + # + # class OutputCompressionFilter + # def self.filter(controller) + # controller.response.body = compress(controller.response.body) + # end + # end + # + # class NewspaperController < ActionController::Base + # after_filter OutputCompressionFilter + # end + # + # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can + # manipulate them as it sees fit. + # + # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation. + # Or just as a quick test. It works like this: + # + # class WeblogController < ActionController::Base + # before_filter { |controller| head(400) if controller.params["stop_action"] } + # end + # + # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables. + # This means that the block has access to both the request and response objects complete with convenience methods for params, + # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call + # and returns 1 or -1 on arity will do (such as a Proc or an Method object). + # + # Please note that around_filters function a little differently than the normal before and after filters with regard to filter + # types. Please see the section dedicated to around_filters below. + # + # == Filter chain ordering + # + # Using before_filter and after_filter appends the specified filters to the existing chain. That's usually + # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you + # can use prepend_before_filter and prepend_after_filter. Filters added by these methods will be put at the + # beginning of their respective chain and executed before the rest. For example: + # + # class ShoppingController < ActionController::Base + # before_filter :verify_open_shop + # + # class CheckoutController < ShoppingController + # prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock + # + # The filter chain for the CheckoutController is now :ensure_items_in_cart, :ensure_items_in_stock, + # :verify_open_shop. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop + # is open or not. + # + # You may pass multiple filter arguments of each type as well as a filter block. + # If a block is given, it is treated as the last argument. + # + # == Around filters + # + # Around filters wrap an action, executing code both before and after. + # They may be declared as method references, blocks, or objects responding + # to +filter+ or to both +before+ and +after+. + # + # To use a method as an +around_filter+, pass a symbol naming the Ruby method. + # Yield (or block.call) within the method to run the action. + # + # around_filter :catch_exceptions + # + # private + # def catch_exceptions + # yield + # rescue => exception + # logger.debug "Caught exception! #{exception}" + # raise + # end + # + # To use a block as an +around_filter+, pass a block taking as args both + # the controller and the action block. You can't call yield directly from + # an +around_filter+ block; explicitly call the action block instead: + # + # around_filter do |controller, action| + # logger.debug "before #{controller.action_name}" + # action.call + # logger.debug "after #{controller.action_name}" + # end + # + # To use a filter object with +around_filter+, pass an object responding + # to :filter or both :before and :after. With a + # filter method, yield to the block as above: + # + # around_filter BenchmarkingFilter + # + # class BenchmarkingFilter + # def self.filter(controller, &block) + # Benchmark.measure(&block) + # end + # end + # + # With +before+ and +after+ methods: + # + # around_filter Authorizer.new + # + # class Authorizer + # # This will run before the action. Redirecting aborts the action. + # def before(controller) + # unless user.authorized? + # redirect_to(login_url) + # end + # end + # + # # This will run after the action if and only if before did not render or redirect. + # def after(controller) + # end + # end + # + # If the filter has +before+ and +after+ methods, the +before+ method will be + # called before the action. If +before+ renders or redirects, the filter chain is + # halted and +after+ will not be run. See Filter Chain Halting below for + # an example. + # + # == Filter chain skipping + # + # Declaring a filter on a base class conveniently applies to its subclasses, + # but sometimes a subclass should skip some of its superclass' filters: + # + # class ApplicationController < ActionController::Base + # before_filter :authenticate + # around_filter :catch_exceptions + # end + # + # class WeblogController < ApplicationController + # # Will run the :authenticate and :catch_exceptions filters. + # end + # + # class SignupController < ApplicationController + # # Skip :authenticate, run :catch_exceptions. + # skip_before_filter :authenticate + # end + # + # class ProjectsController < ApplicationController + # # Skip :catch_exceptions, run :authenticate. + # skip_filter :catch_exceptions + # end + # + # class ClientsController < ApplicationController + # # Skip :catch_exceptions and :authenticate unless action is index. + # skip_filter :catch_exceptions, :authenticate, :except => :index + # end + # + # == Filter conditions + # + # Filters may be limited to specific actions by declaring the actions to + # include or exclude. Both options accept single actions + # (:only => :index) or arrays of actions + # (:except => [:foo, :bar]). + # + # class Journal < ActionController::Base + # # Require authentication for edit and delete. + # before_filter :authorize, :only => [:edit, :delete] + # + # # Passing options to a filter with a block. + # around_filter(:except => :index) do |controller, action_block| + # results = Profiler.run(&action_block) + # controller.response.sub! "", "#{results}" + # end + # + # private + # def authorize + # # Redirect to login unless authenticated. + # end + # end + # + # == Filter Chain Halting + # + # before_filter and around_filter may halt the request + # before a controller action is run. This is useful, for example, to deny + # access to unauthenticated users or to redirect from HTTP to HTTPS. + # Simply call render or redirect. After filters will not be executed if the filter + # chain is halted. + # + # Around filters halt the request unless the action block is called. + # Given these filters + # after_filter :after + # around_filter :around + # before_filter :before + # + # The filter chain will look like: + # + # ... + # . \ + # . #around (code before yield) + # . . \ + # . . #before (actual filter code is run) + # . . . \ + # . . . execute controller action + # . . . / + # . . ... + # . . / + # . #around (code after yield) + # . / + # #after (actual filter code is run, unless the around filter does not yield) + # + # If +around+ returns before yielding, +after+ will still not be run. The +before+ + # filter and controller action will not be run. If +before+ renders or redirects, + # the second half of +around+ and will still run but +after+ and the + # action will not. If +around+ fails to yield, +after+ will not be run. + module ClassMethods + # The passed filters will be appended to the filter_chain and + # will execute before the action on this controller is performed. + def append_before_filter(*filters, &block) + filter_chain.append_filter_to_chain(filters, :before, &block) + end + + # The passed filters will be prepended to the filter_chain and + # will execute before the action on this controller is performed. + def prepend_before_filter(*filters, &block) + filter_chain.prepend_filter_to_chain(filters, :before, &block) + end + + # Shorthand for append_before_filter since it's the most common. + alias :before_filter :append_before_filter + + # The passed filters will be appended to the array of filters + # that run _after_ actions on this controller are performed. + def append_after_filter(*filters, &block) + filter_chain.append_filter_to_chain(filters, :after, &block) + end + + # The passed filters will be prepended to the array of filters + # that run _after_ actions on this controller are performed. + def prepend_after_filter(*filters, &block) + filter_chain.prepend_filter_to_chain(filters, :after, &block) + end + + # Shorthand for append_after_filter since it's the most common. + alias :after_filter :append_after_filter + + # If you append_around_filter A.new, B.new, the filter chain looks like + # + # B#before + # A#before + # # run the action + # A#after + # B#after + # + # With around filters which yield to the action block, +before+ and +after+ + # are the code before and after the yield. + def append_around_filter(*filters, &block) + filter_chain.append_filter_to_chain(filters, :around, &block) + end + + # If you prepend_around_filter A.new, B.new, the filter chain looks like: + # + # A#before + # B#before + # # run the action + # B#after + # A#after + # + # With around filters which yield to the action block, +before+ and +after+ + # are the code before and after the yield. + def prepend_around_filter(*filters, &block) + filter_chain.prepend_filter_to_chain(filters, :around, &block) + end + + # Shorthand for +append_around_filter+ since it's the most common. + alias :around_filter :append_around_filter + + # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference + # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out + # of many sub-controllers need a different hierarchy. + # + # You can control the actions to skip the filter for with the :only and :except options, + # just like when you apply the filters. + def skip_before_filter(*filters) + filter_chain.skip_filter_in_chain(*filters, &:before?) + end + + # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference + # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out + # of many sub-controllers need a different hierarchy. + # + # You can control the actions to skip the filter for with the :only and :except options, + # just like when you apply the filters. + def skip_after_filter(*filters) + filter_chain.skip_filter_in_chain(*filters, &:after?) + end + + # Removes the specified filters from the filter chain. This only works for method reference (symbol) + # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that + # it will match any before, after or yielding around filter. + # + # You can control the actions to skip the filter for with the :only and :except options, + # just like when you apply the filters. + def skip_filter(*filters) + filter_chain.skip_filter_in_chain(*filters) + end + + # Returns an array of Filter objects for this controller. + def filter_chain + read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new) + end + + # Returns all the before filters for this class and all its ancestors. + # This method returns the actual filter that was assigned in the controller to maintain existing functionality. + def before_filters #:nodoc: + filter_chain.select(&:before?).map(&:method) + end + + # Returns all the after filters for this class and all its ancestors. + # This method returns the actual filter that was assigned in the controller to maintain existing functionality. + def after_filters #:nodoc: + filter_chain.select(&:after?).map(&:method) + end + end + + module InstanceMethods # :nodoc: + def self.included(base) + base.class_eval do + alias_method_chain :perform_action, :filters + alias_method_chain :process, :filters + end + end + + protected + def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: + @before_filter_chain_aborted = false + process_without_filters(request, response, method, *arguments) + end + + def perform_action_with_filters + call_filters(self.class.filter_chain, 0, 0) + end + + private + def call_filters(chain, index, nesting) + index = run_before_filters(chain, index, nesting) + aborted = @before_filter_chain_aborted + perform_action_without_filters unless performed? || aborted + return index if nesting != 0 || aborted + run_after_filters(chain, index) + end + + def run_before_filters(chain, index, nesting) + while chain[index] + filter, index = chain[index], index + break unless filter # end of call chain reached + + case filter + when BeforeFilter + filter.call(self) # invoke before filter + index = index.next + break if @before_filter_chain_aborted + when AroundFilter + yielded = false + + filter.call(self) do + yielded = true + # all remaining before and around filters will be run in this call + index = call_filters(chain, index.next, nesting.next) + end + + halt_filter_chain(filter, :did_not_yield) unless yielded + + break + else + break # no before or around filters left + end + end + + index + end + + def run_after_filters(chain, index) + seen_after_filter = false + + while chain[index] + filter, index = chain[index], index + break unless filter # end of call chain reached + + case filter + when AfterFilter + seen_after_filter = true + filter.call(self) # invoke after filter + else + # implementation error or someone has mucked with the filter chain + raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter + end + + index = index.next + end + + index.next + end + + def halt_filter_chain(filter, reason) + @before_filter_chain_aborted = true + logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger + end + end + end +end diff --git a/actionpack/lib/action_controller/old_base/chained/flash.rb b/actionpack/lib/action_controller/old_base/chained/flash.rb new file mode 100644 index 0000000000..42c6e430ca --- /dev/null +++ b/actionpack/lib/action_controller/old_base/chained/flash.rb @@ -0,0 +1,196 @@ +module ActionController #:nodoc: + # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed + # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create + # action that sets flash[:notice] = "Successfully created" before redirecting to a display action that can + # then expose the flash to its template. Actually, that exposure is automatically done. Example: + # + # class PostsController < ActionController::Base + # def create + # # save post + # flash[:notice] = "Successfully created post" + # redirect_to posts_path(@post) + # end + # + # def show + # # doesn't need to assign the flash notice to the template, that's done automatically + # end + # end + # + # show.html.erb + # <% if flash[:notice] %> + #
<%= flash[:notice] %>
+ # <% end %> + # + # This example just places a string in the flash, but you can put any object in there. And of course, you can put as + # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed. + # + # See docs on the FlashHash class for more details about the flash. + module Flash + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + include Session if defined?(ActionController::Http) + + included do + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include InstanceMethodsForNewBase + else + include InstanceMethodsForBase + + alias_method_chain :perform_action, :flash + alias_method_chain :reset_session, :flash + end + end + + class FlashNow #:nodoc: + def initialize(flash) + @flash = flash + end + + def []=(k, v) + @flash[k] = v + @flash.discard(k) + v + end + + def [](k) + @flash[k] + end + end + + class FlashHash < Hash + def initialize #:nodoc: + super + @used = {} + end + + def []=(k, v) #:nodoc: + keep(k) + super + end + + def update(h) #:nodoc: + h.keys.each { |k| keep(k) } + super + end + + alias :merge! :update + + def replace(h) #:nodoc: + @used = {} + super + end + + # Sets a flash that will not be available to the next action, only to the current. + # + # flash.now[:message] = "Hello current action" + # + # This method enables you to use the flash as a central messaging system in your app. + # When you need to pass an object to the next action, you use the standard flash assign ([]=). + # When you need to pass an object to the current action, you use now, and your object will + # vanish when the current action is done. + # + # Entries set via now are accessed the same way as standard entries: flash['my-key']. + def now + FlashNow.new(self) + end + + # Keeps either the entire current flash or a specific flash entry available for the next action: + # + # flash.keep # keeps the entire flash + # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded + def keep(k = nil) + use(k, false) + end + + # Marks the entire flash or a single flash entry to be discarded by the end of the current action: + # + # flash.discard # discard the entire flash at the end of the current action + # flash.discard(:warning) # discard only the "warning" entry at the end of the current action + def discard(k = nil) + use(k) + end + + # Mark for removal entries that were kept, and delete unkept ones. + # + # This method is called automatically by filters, so you generally don't need to care about it. + def sweep #:nodoc: + keys.each do |k| + unless @used[k] + use(k) + else + delete(k) + @used.delete(k) + end + end + + # clean up after keys that could have been left over by calling reject! or shift on the flash + (@used.keys - keys).each{ |k| @used.delete(k) } + end + + def store(session, key = "flash") + return if self.empty? + session[key] = self + end + + private + # Used internally by the keep and discard methods + # use() # marks the entire flash as used + # use('msg') # marks the "msg" entry as used + # use(nil, false) # marks the entire flash as unused (keeps it around for one more action) + # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action) + # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself + # if no key is passed. + def use(key = nil, used = true) + Array(key || keys).each { |k| @used[k] = used } + return key ? self[key] : self + end + end + + module InstanceMethodsForBase #:nodoc: + protected + def perform_action_with_flash + perform_action_without_flash + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end + end + + def reset_session_with_flash + reset_session_without_flash + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end + + module InstanceMethodsForNewBase #:nodoc: + protected + def process_action(method_name) + super + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end + end + + def reset_session + super + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end + + protected + # Access the contents of the flash. Use flash["notice"] to + # read a notice you put there or flash["notice"] = "hello" + # to put a new one. + def flash #:doc: + if !defined?(@_flash) + @_flash = session["flash"] || FlashHash.new + @_flash.sweep + end + + @_flash + end + end +end diff --git a/actionpack/lib/action_controller/old_base/cookies.rb b/actionpack/lib/action_controller/old_base/cookies.rb new file mode 100644 index 0000000000..d4806623c3 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/cookies.rb @@ -0,0 +1,94 @@ +module ActionController #:nodoc: + # Cookies are read and written through ActionController#cookies. + # + # The cookies being read are the ones received along with the request, the cookies + # being written will be sent out with the response. Reading a cookie does not get + # the cookie object itself back, just the value it holds. + # + # Examples for writing: + # + # # Sets a simple session cookie. + # cookies[:user_name] = "david" + # + # # Sets a cookie that expires in 1 hour. + # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now } + # + # Examples for reading: + # + # cookies[:user_name] # => "david" + # cookies.size # => 2 + # + # Example for deleting: + # + # cookies.delete :user_name + # + # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie: + # + # cookies[:key] = { + # :value => 'a yummy cookie', + # :expires => 1.year.from_now, + # :domain => 'domain.com' + # } + # + # cookies.delete(:key, :domain => 'domain.com') + # + # The option symbols for setting cookies are: + # + # * :value - The cookie's value or list of values (as an array). + # * :path - The path for which this cookie applies. Defaults to the root + # of the application. + # * :domain - The domain for which this cookie applies. + # * :expires - The time at which this cookie expires, as a Time object. + # * :secure - Whether this cookie is a only transmitted to HTTPS servers. + # Default is +false+. + # * :httponly - Whether this cookie is accessible via scripting or + # only HTTP. Defaults to +false+. + module Cookies + def self.included(base) + base.helper_method :cookies + end + + protected + # Returns the cookie container, which operates as described above. + def cookies + @cookies ||= CookieJar.new(self) + end + end + + class CookieJar < Hash #:nodoc: + def initialize(controller) + @controller, @cookies = controller, controller.request.cookies + super() + update(@cookies) + end + + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. + def [](name) + super(name.to_s) + end + + # Sets the cookie named +name+. The second argument may be the very cookie + # value, or a hash of options as documented above. + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + else + options = { :value => options } + end + + options[:path] = "/" unless options.has_key?(:path) + super(key.to_s, options[:value]) + @controller.response.set_cookie(key, options) + end + + # Removes the cookie on the client machine by setting the value to an empty string + # and setting its expiration date into the past. Like []=, you can pass in + # an options hash to delete cookies with extra data such as a :path. + def delete(key, options = {}) + options.symbolize_keys! + options[:path] = "/" unless options.has_key?(:path) + super(key.to_s) + @controller.response.delete_cookie(key, options) + end + end +end diff --git a/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb b/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb new file mode 100644 index 0000000000..8370ba6fc0 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb @@ -0,0 +1,95 @@ +module ActionController + module FilterParameterLogging + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Logger + end + + included do + include InstanceMethodsForNewBase + end + + module ClassMethods + # Replace sensitive parameter data from the request log. + # Filters parameters that have any of the arguments as a substring. + # Looks in all subhashes of the param hash for keys to filter. + # If a block is given, each key and value of the parameter hash and all + # subhashes is passed to it, the value or key + # can be replaced using String#replace or similar method. + # + # Examples: + # filter_parameter_logging + # => Does nothing, just slows the logging process down + # + # filter_parameter_logging :password + # => replaces the value to all keys matching /password/i with "[FILTERED]" + # + # filter_parameter_logging :foo, "bar" + # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + # + # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i + # + # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i, and + # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + def filter_parameter_logging(*filter_words, &block) + parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 + + define_method(:filter_parameters) do |unfiltered_parameters| + filtered_parameters = {} + + unfiltered_parameters.each do |key, value| + if key =~ parameter_filter + filtered_parameters[key] = '[FILTERED]' + elsif value.is_a?(Hash) + filtered_parameters[key] = filter_parameters(value) + elsif block_given? + key = key.dup + value = value.dup if value + yield key, value + filtered_parameters[key] = value + else + filtered_parameters[key] = value + end + end + + filtered_parameters + end + protected :filter_parameters + end + end + + module InstanceMethodsForNewBase + # TODO : Fix the order of information inside such that it's exactly same as the old base + def process(*) + ret = super + + if logger + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method, :only_path) + + unless parameters.empty? + # TODO : Move DelayedLog to AS + log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" } + logger.info(log) + end + end + + ret + end + end + + private + + # TODO : This method is not needed for the new base + def log_processing_for_parameters + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method) + + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? + end + end +end diff --git a/actionpack/lib/action_controller/old_base/helpers.rb b/actionpack/lib/action_controller/old_base/helpers.rb new file mode 100644 index 0000000000..f74158bc13 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/helpers.rb @@ -0,0 +1,221 @@ +require 'active_support/dependencies' + +# FIXME: helper { ... } is broken on Ruby 1.9 +module ActionController #:nodoc: + module Helpers #:nodoc: + extend ActiveSupport::Concern + + included do + # Initialize the base module to aggregate its helpers. + class_inheritable_accessor :master_helper_module + self.master_helper_module = Module.new + + # Set the default directory for helpers + class_inheritable_accessor :helpers_dir + self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") + + class << self + alias_method_chain :inherited, :helper + end + end + + # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, + # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates + # by default. + # + # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to + # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will + # include a helper whose name matches that of the controller, e.g., MyController will automatically + # include MyHelper. + # + # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any + # controller which inherits from it. + # + # ==== Examples + # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if + # the Time object is blank: + # + # module FormattedTimeHelper + # def format_time(time, format=:long, blank_message=" ") + # time.blank? ? blank_message : time.to_s(format) + # end + # end + # + # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: + # + # class EventsController < ActionController::Base + # helper FormattedTimeHelper + # def index + # @events = Event.find(:all) + # end + # end + # + # Then, in any view rendered by EventController, the format_time method can be called: + # + # <% @events.each do |event| -%> + #

+ # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> + #

+ # <% end -%> + # + # Finally, assuming we have two event instances, one which has a time and one which does not, + # the output might look like this: + # + # 23 Aug 11:30 | Carolina Railhawks Soccer Match + # N/A | Carolina Railhaws Training Workshop + # + module ClassMethods + # Makes all the (instance) methods in the helper module available to templates rendered through this controller. + # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules + # available to the templates. + def add_template_helper(helper_module) #:nodoc: + master_helper_module.module_eval { include helper_module } + end + + # The +helper+ class method can take a series of helper module names, a block, or both. + # + # * *args: One or more modules, strings or symbols, or the special symbol :all. + # * &block: A block defining helper methods. + # + # ==== Examples + # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file + # and include the module in the template class. The second form illustrates how to include custom helpers + # when working with namespaced controllers, or other cases where the file containing the helper definition is not + # in one of Rails' standard load paths: + # helper :foo # => requires 'foo_helper' and includes FooHelper + # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper + # + # When the argument is a module it will be included directly in the template class. + # helper FooHelper # => includes FooHelper + # + # When the argument is the symbol :all, the controller will include all helpers beneath + # ActionController::Base.helpers_dir (defaults to app/helpers/**/*.rb under RAILS_ROOT). + # helper :all + # + # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available + # to the template. + # # One line + # helper { def hello() "Hello, world!" end } + # # Multi-line + # helper do + # def foo(bar) + # "#{bar} is the very best" + # end + # end + # + # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of + # +symbols+, +strings+, +modules+ and blocks. + # helper(:three, BlindHelper) { def mice() 'mice' end } + # + def helper(*args, &block) + args.flatten.each do |arg| + case arg + when Module + add_template_helper(arg) + when :all + helper(all_application_helpers) + when String, Symbol + file_name = arg.to_s.underscore + '_helper' + class_name = file_name.camelize + + begin + require_dependency(file_name) + rescue LoadError => load_error + requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] + if requiree == file_name + msg = "Missing helper file helpers/#{file_name}.rb" + raise LoadError.new(msg).copy_blame!(load_error) + else + raise + end + end + + add_template_helper(class_name.constantize) + else + raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})" + end + end + + # Evaluate block in template class if given. + master_helper_module.module_eval(&block) if block_given? + end + + # Declare a controller method as a helper. For example, the following + # makes the +current_user+ controller method available to the view: + # class ApplicationController < ActionController::Base + # helper_method :current_user, :logged_in? + # + # def current_user + # @current_user ||= User.find_by_id(session[:user]) + # end + # + # def logged_in? + # current_user != nil + # end + # end + # + # In a view: + # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> + def helper_method(*methods) + methods.flatten.each do |method| + master_helper_module.module_eval <<-end_eval + def #{method}(*args, &block) # def current_user(*args, &block) + controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block) + end # end + end_eval + end + end + + # Declares helper accessors for controller attributes. For example, the + # following adds new +name+ and name= instance methods to a + # controller and makes them available to the view: + # helper_attr :name + # attr_accessor :name + def helper_attr(*attrs) + attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } + end + + # Provides a proxy to access helpers methods from outside the view. + def helpers + unless @helper_proxy + @helper_proxy = ActionView::Base.new + @helper_proxy.extend master_helper_module + else + @helper_proxy + end + end + + private + def default_helper_module! + unless name.blank? + module_name = name.sub(/Controller$|$/, 'Helper') + module_path = module_name.split('::').map { |m| m.underscore }.join('/') + require_dependency module_path + helper module_name.constantize + end + rescue MissingSourceFile => e + raise unless e.is_missing? module_path + rescue NameError => e + raise unless e.missing_name? module_name + end + + def inherited_with_helper(child) + inherited_without_helper(child) + + begin + child.master_helper_module = Module.new + child.master_helper_module.__send__ :include, master_helper_module + child.__send__ :default_helper_module! + rescue MissingSourceFile => e + raise unless e.is_missing?("helpers/#{child.controller_path}_helper") + end + end + + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end + end + end +end diff --git a/actionpack/lib/action_controller/old_base/http_authentication.rb b/actionpack/lib/action_controller/old_base/http_authentication.rb new file mode 100644 index 0000000000..2519f55269 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/http_authentication.rb @@ -0,0 +1,309 @@ +require 'active_support/base64' + +module ActionController + module HttpAuthentication + # Makes it dead easy to do HTTP Basic authentication. + # + # Simple Basic example: + # + # class PostsController < ApplicationController + # USER_NAME, PASSWORD = "dhh", "secret" + # + # before_filter :authenticate, :except => [ :index ] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_basic do |user_name, password| + # user_name == USER_NAME && password == PASSWORD + # end + # end + # end + # + # + # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, + # the regular HTML interface is protected by a session approach: + # + # class ApplicationController < ActionController::Base + # before_filter :set_account, :authenticate + # + # protected + # def set_account + # @account = Account.find_by_url_name(request.subdomains.first) + # end + # + # def authenticate + # case request.format + # when Mime::XML, Mime::ATOM + # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } + # @current_user = user + # else + # request_http_basic_authentication + # end + # else + # if session_authenticated? + # @current_user = @account.users.find(session[:authenticated][:user_id]) + # else + # redirect_to(login_url) and return false + # end + # end + # end + # end + # + # In your integration tests, you can do something like this: + # + # def test_access_granted_from_xml + # get( + # "/notes/1.xml", nil, + # :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) + # ) + # + # assert_equal 200, status + # end + # + # Simple Digest example: + # + # require 'digest/md5' + # class PostsController < ApplicationController + # REALM = "SuperSecret" + # USERS = {"dhh" => "secret", #plain text password + # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password + # + # before_filter :authenticate, :except => [:index] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_digest(REALM) do |username| + # USERS[username] + # end + # end + # end + # + # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately + # hash to check the user's credentials. Returning +nil+ will cause authentication to fail. + # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If + # the password file or database is compromised, the attacker would be able to use the ha1 hash to + # authenticate as the user at this +realm+, but would not have the user's password to try using at + # other sites. + # + # On shared hosts, Apache sometimes doesn't pass authentication headers to + # FCGI instances. If your environment matches this description and you cannot + # authenticate, try this rule in your Apache setup: + # + # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] + module Basic + extend self + + module ControllerMethods + def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) + authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) + end + + def authenticate_with_http_basic(&login_procedure) + HttpAuthentication::Basic.authenticate(self, &login_procedure) + end + + def request_http_basic_authentication(realm = "Application") + HttpAuthentication::Basic.authentication_request(self, realm) + end + end + + def authenticate(controller, &login_procedure) + unless authorization(controller.request).blank? + login_procedure.call(*user_name_and_password(controller.request)) + end + end + + def user_name_and_password(request) + decode_credentials(request).split(/:/, 2) + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + def decode_credentials(request) + ActiveSupport::Base64.decode64(authorization(request).split.last || '') + end + + def encode_credentials(user_name, password) + "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}" + end + + def authentication_request(controller, realm) + controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}") + controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized + end + end + + module Digest + extend self + + module ControllerMethods + def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) + authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) + end + + # Authenticate with HTTP Digest, returns true or false + def authenticate_with_http_digest(realm = "Application", &password_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &password_procedure) + end + + # Render output including the HTTP Digest authentication header + def request_http_digest_authentication(realm = "Application", message = nil) + HttpAuthentication::Digest.authentication_request(self, realm, message) + end + end + + # Returns false on a valid response, true otherwise + def authenticate(controller, realm, &password_procedure) + authorization(controller.request) && validate_digest_response(controller.request, realm, &password_procedure) + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + # Returns false unless the request credentials response value matches the expected value. + # First try the password as a ha1 digest password. If this fails, then try it as a plain + # text password. + def validate_digest_response(request, realm, &password_procedure) + credentials = decode_credentials_header(request) + valid_nonce = validate_nonce(request, credentials[:nonce]) + + if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] + password = password_procedure.call(credentials[:username]) + return false unless password + + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + + [true, false].any? do |password_is_ha1| + expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected == credentials[:response] + end + end + end + + # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ + # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead + # of a plain-text password. + def expected_response(http_method, uri, credentials, password, password_is_ha1=true) + ha1 = password_is_ha1 ? password : ha1(credentials, password) + ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) + ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) + end + + def ha1(credentials, password) + ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + end + + def encode_credentials(http_method, credentials, password, password_is_ha1) + credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) + "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') + end + + def decode_credentials_header(request) + decode_credentials(authorization(request)) + end + + def decode_credentials(header) + header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + end + + def authentication_header(controller, realm) + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") + end + + def authentication_request(controller, realm, message = nil) + message ||= "HTTP Digest: Access denied.\n" + authentication_header(controller, realm) + controller.__send__ :render, :text => message, :status => :unauthorized + end + + # Uses an MD5 digest based on time to generate a value to be used only once. + # + # A server-specified data string which should be uniquely generated each time a 401 response is made. + # It is recommended that this string be base64 or hexadecimal data. + # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. + # + # The contents of the nonce are implementation dependent. + # The quality of the implementation depends on a good choice. + # A nonce might, for example, be constructed as the base 64 encoding of + # + # => time-stamp H(time-stamp ":" ETag ":" private-key) + # + # where time-stamp is a server-generated time or other non-repeating value, + # ETag is the value of the HTTP ETag header associated with the requested entity, + # and private-key is data known only to the server. + # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and + # reject the request if it did not match the nonce from that header or + # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. + # The inclusion of the ETag prevents a replay request for an updated version of the resource. + # (Note: including the IP address of the client in the nonce would appear to offer the server the ability + # to limit the reuse of the nonce to the same client that originally got it. + # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. + # Also, IP address spoofing is not that hard.) + # + # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to + # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for + # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 + # of this document. + # + # The nonce is opaque to the client. Composed of Time, and hash of Time with secret + # key from the Rails session secret generated upon creation of project. Ensures + # the time cannot be modifed by client. + def nonce(time = Time.now) + t = time.to_i + hashed = [t, secret_key] + digest = ::Digest::MD5.hexdigest(hashed.join(":")) + ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') + end + + # Might want a shorter timeout depending on whether the request + # is a PUT or POST, and if client is browser or web service. + # Can be much shorter if the Stale directive is implemented. This would + # allow a user to use new nonce without prompting user again for their + # username and password. + def validate_nonce(request, value, seconds_to_timeout=5*60) + t = ActiveSupport::Base64.decode64(value).split(":").first.to_i + nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout + end + + # Opaque based on random generation - but changing each request? + def opaque() + ::Digest::MD5.hexdigest(secret_key) + end + + # Set in /initializers/session_store.rb, and loaded even if sessions are not in use. + def secret_key + ActionController::Base.session_options[:secret] + end + + end + end +end diff --git a/actionpack/lib/action_controller/old_base/layout.rb b/actionpack/lib/action_controller/old_base/layout.rb new file mode 100644 index 0000000000..cf5f46a32b --- /dev/null +++ b/actionpack/lib/action_controller/old_base/layout.rb @@ -0,0 +1,251 @@ +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/class/delegating_attributes' +require 'active_support/core_ext/class/inheritable_attributes' + +module ActionController #:nodoc: + module Layout #:nodoc: + def self.included(base) + base.extend(ClassMethods) + 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 + # repeated setups. The inclusion pattern has pages that look like this: + # + # <%= render "shared/header" %> + # Hello World + # <%= render "shared/footer" %> + # + # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose + # and if you ever want to change the structure of these two includes, you'll have to change all the templates. + # + # With layouts, you can flip it around and have the common structure know where to insert changing content. This means + # that the header and footer are only mentioned in one place, like this: + # + # // The header part of this layout + # <%= yield %> + # // The footer part of this layout + # + # And then you have content pages that look like this: + # + # hello world + # + # At rendering time, the content page is computed and then inserted in the layout, like this: + # + # // The header part of this layout + # hello world + # // The footer part of this layout + # + # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance + # variable. The preferred notation now is to use yield, as documented above. + # + # == Accessing shared variables + # + # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with + # references that won't materialize before rendering time: + # + #

<%= @page_title %>

+ # <%= yield %> + # + # ...and content pages that fulfill these references _at_ rendering time: + # + # <% @page_title = "Welcome" %> + # Off-world colonies offers you a chance to start a new life + # + # The result after rendering is: + # + #

Welcome

+ # Off-world colonies offers you a chance to start a new life + # + # == Automatic layout assignment + # + # If there is a template in app/views/layouts/ with the same name as the current controller then it will be automatically + # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named + # app/views/layouts/weblog.erb or app/views/layouts/weblog.builder exists then it will be automatically set as + # the layout for your WeblogController. You can create a layout with the name application.erb or application.builder + # and this will be set as the default controller if there is no layout with the same name as the current controller and there is + # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout. + # assignment. So an Admin::WeblogController will look for a template named app/views/layouts/admin/weblog.erb. + # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set. + # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child + # class has a layout with the same name. + # + # == Inheritance for layouts + # + # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples: + # + # class BankController < ActionController::Base + # layout "bank_standard" + # + # class InformationController < BankController + # + # class VaultController < BankController + # layout :access_level_layout + # + # class EmployeeController < BankController + # layout nil + # + # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites + # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. + # + # == Types of layouts + # + # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes + # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can + # be done either by specifying a method reference as a symbol or using an inline method (as a proc). + # + # The method reference is the preferred approach to variable layouts and is used like this: + # + # class WeblogController < ActionController::Base + # layout :writers_and_readers + # + # def index + # # fetching posts + # end + # + # private + # def writers_and_readers + # logged_in? ? "writer_layout" : "reader_layout" + # end + # + # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing + # is logged in or not. + # + # If you want to use an inline method, such as a proc, do something like this: + # + # class WeblogController < ActionController::Base + # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } + # + # Of course, the most common way of specifying a layout is still just as a plain template name: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. + # Otherwise, it will be looked up relative to the template root. + # + # == Conditional layouts + # + # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering + # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The + # :only and :except options can be passed to the layout call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard", :except => :rss + # + # # ... + # + # end + # + # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout + # around the rendered view. + # + # Both the :only and :except condition can accept an arbitrary number of method references, so + # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. + # + # == Using a different layout in the action render call + # + # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. + # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. + # You can do this by passing a :layout option to the render call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # def help + # render :action => "help", :layout => "help" + # end + # end + # + # 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 yields. 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) + self.layout_name = template_name + end + + 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(*args) + memoized_default_layout(*args) + @_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new + @_memoized_default_layout[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 => formats}, prefix) + end + + def find_layout(*args) + @_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new + @_memoized_find_layout[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 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 add_layout_conditions(conditions) + # :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 + + 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 + + self.class.find_layout(layout_name, formats) + end + + def _pick_layout(layout_name = nil, 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 + if only = conditions[:only] + return only.include?(action_name) + elsif except = conditions[:except] + return !except.include?(action_name) + end + end + true + end + + end +end diff --git a/actionpack/lib/action_controller/old_base/mime_responds.rb b/actionpack/lib/action_controller/old_base/mime_responds.rb new file mode 100644 index 0000000000..5c7218691e --- /dev/null +++ b/actionpack/lib/action_controller/old_base/mime_responds.rb @@ -0,0 +1,188 @@ +module ActionController #:nodoc: + module MimeResponds #:nodoc: + # Without web-service support, an action which collects the data for displaying a list of people + # might look something like this: + # + # def index + # @people = Person.find(:all) + # end + # + # Here's the same action, with web-service support baked in: + # + # def index + # @people = Person.find(:all) + # + # respond_to do |format| + # format.html + # format.xml { render :xml => @people.to_xml } + # end + # end + # + # What that says is, "if the client wants HTML in response to this action, just respond as we + # would have before, but if the client wants XML, return them the list of people in XML format." + # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) + # + # Supposing you have an action that adds a new person, optionally creating their company + # (by name) if it does not already exist, without web-services, it might look like this: + # + # def create + # @company = Company.find_or_create_by_name(params[:company][:name]) + # @person = @company.people.create(params[:person]) + # + # redirect_to(person_list_url) + # end + # + # Here's the same action, with web-service support baked in: + # + # def create + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # @person = @company.people.create(params[:person]) + # + # respond_to do |format| + # format.html { redirect_to(person_list_url) } + # format.js + # format.xml { render :xml => @person.to_xml(:include => @company) } + # end + # end + # + # If the client wants HTML, we just redirect them back to the person list. If they want Javascript + # (format.js), then it is an RJS request and we render the RJS template associated with this action. + # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also + # include the person's company in the rendered XML, so you get something like this: + # + # + # ... + # ... + # + # ... + # ... + # ... + # + # + # + # Note, however, the extra bit at the top of that action: + # + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # + # This is because the incoming XML document (if a web-service request is in process) can only contain a + # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): + # + # person[name]=...&person[company][name]=...&... + # + # And, like this (xml-encoded): + # + # + # ... + # + # ... + # + # + # + # In other words, we make the request so that it operates on a single entity's person. Then, in the action, + # we extract the company data from the request, find or create the company, and then create the new person + # with the remaining data. + # + # Note that you can define your own XML parameter parser which would allow you to describe multiple entities + # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow + # and accept Rails' defaults, life will be much easier. + # + # If you need to use a MIME type which isn't supported by default, you can register your own handlers in + # environment.rb as follows. + # + # Mime::Type.register "image/jpg", :jpg + def respond_to(*types, &block) + raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block + block ||= lambda { |responder| types.each { |type| responder.send(type) } } + responder = Responder.new(self) + block.call(responder) + responder.respond + end + + class Responder #:nodoc: + + def initialize(controller) + @controller = controller + @request = controller.request + @response = controller.response + + @mime_type_priority = @request.formats + + @order = [] + @responses = {} + end + + def custom(mime_type, &block) + mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) + + @order << mime_type + + @responses[mime_type] ||= Proc.new do + # TODO: Remove this when new base is merged in + if defined?(Http) + @controller.formats = [mime_type.to_sym] + end + + @controller.template.formats = [mime_type.to_sym] + @response.content_type = mime_type + + block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) + end + end + + def any(*args, &block) + if args.any? + args.each { |type| send(type, &block) } + else + custom(@mime_type_priority.first, &block) + end + end + + def self.generate_method_for_mime(mime) + sym = mime.is_a?(Symbol) ? mime : mime.to_sym + const = sym.to_s.upcase + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{sym}(&block) # def html(&block) + custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) + end # end + RUBY + end + + Mime::SET.each do |mime| + generate_method_for_mime(mime) + end + + def method_missing(symbol, &block) + mime_constant = Mime.const_get(symbol.to_s.upcase) + + if Mime::SET.include?(mime_constant) + self.class.generate_method_for_mime(mime_constant) + send(symbol, &block) + else + super + end + end + + def respond + for priority in @mime_type_priority + if priority == Mime::ALL + @responses[@order.first].call + return + else + if @responses[priority] + @responses[priority].call + return # mime type match found, be happy and return + end + end + end + + if @order.include?(Mime::ALL) + @responses[Mime::ALL].call + else + @controller.send :head, :not_acceptable + end + end + end + end +end diff --git a/actionpack/lib/action_controller/old_base/redirect.rb b/actionpack/lib/action_controller/old_base/redirect.rb new file mode 100644 index 0000000000..7e10f614e2 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/redirect.rb @@ -0,0 +1,89 @@ +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: + # + # * Hash - The URL will be generated by calling url_for with the +options+. + # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. + # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. + # * String not containing a protocol - The current protocol and host is prepended to the string. + # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. + # Short-hand for redirect_to(request.env["HTTP_REFERER"]) + # + # 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 redirect_to :back, 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 + + 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? + logger.info("Redirected to #{url}") if logger && logger.info? + response.status = interpret_status(status) + response.location = url.gsub(/[\r\n]/, '') + response.body = "You are being redirected." + @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.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/old_base/render.rb b/actionpack/lib/action_controller/old_base/render.rb new file mode 100644 index 0000000000..cc0d878e01 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/render.rb @@ -0,0 +1,403 @@ +require 'action_controller/abstract/renderer' + +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 :text 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 'David' + # render :xml => {:name => "David"}.to_xml + # + # It's not necessary to call to_xml on the object you want to render, since render will + # automatically do that for you: + # + # # Also renders 'David' + # 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 to_json on the object you want to render, since render 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 :callback 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 "

Good seeing you!

" 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 :update 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 :status and :location 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? + + # This handles render "string", render :symbol, and render object + # render string and symbol are handled by render_for_name + # render object becomes render :partial => object + unless options.is_a?(Hash) + if options.is_a?(String) || options.is_a?(Symbol) + original, options = options, extra_options + else + extra_options[:partial], options = options, extra_options + end + end + + 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 options.include?(:json) + json = options[:json] + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + 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 + + # Same rules as render, but returns a Rack-compatible body + # instead of sending the response. + def render_to_body(options = nil, &block) #:doc: + render(options, &block) + response.body + ensure + response.content_type = nil + erase_render_results + reset_variables_added_to_assigns + end + + def render_to_string(options = {}) + AbstractController::Renderer.body_to_s(render_to_body(options)) + end + + # Clears the rendered results, allowing for another render to be performed. + def erase_render_results #:nodoc: + response.body = [] + @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 + + # ==== Arguments + # parts:: + # Example: ["show", [:html, :xml], "users", false] + def render_for_parts(parts, layout_details, options = {}) + parts[1] = {:formats => parts[1], :locales => [I18n.locale]} + + tmp = view_paths.find_by_parts(*parts) + + layout = _pick_layout(*layout_details) unless + self.class.exempt_from_layout.include?(tmp.handler) + + 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 diff --git a/actionpack/lib/action_controller/old_base/request_forgery_protection.rb b/actionpack/lib/action_controller/old_base/request_forgery_protection.rb new file mode 100644 index 0000000000..a470c8eec1 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/request_forgery_protection.rb @@ -0,0 +1,123 @@ +module ActionController #:nodoc: + class InvalidAuthenticityToken < ActionControllerError #:nodoc: + end + + module RequestForgeryProtection + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Helpers, Session + end + + included do + if defined?(ActionController::Http) + # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ + # sets it to :authenticity_token by default. + cattr_accessor :request_forgery_protection_token + + # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + class_inheritable_accessor :allow_forgery_protection + self.allow_forgery_protection = true + end + + helper_method :form_authenticity_token + helper_method :protect_against_forgery? + end + + # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a + # forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all + # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only + # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication + # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway. + # + # This is turned on with the protect_from_forgery method, which will check the token and raise an + # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in + # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0 + # applications. + # + # The token parameter is named authenticity_token by default. If you are generating an HTML form manually (without the + # use of Rails' form_for, form_tag or other helpers), you have to include a hidden field named like that and + # set its value to what is returned by form_authenticity_token. Same applies to manually constructed Ajax requests. To + # make the token available through a global variable to scripts on a certain page, you could add something like this to a view: + # + # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %> + # + # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to + # config/environments/test.rb: + # + # # Disable request forgery protection in test environment + # config.action_controller.allow_forgery_protection = false + # + # == Learn more about CSRF (Cross-Site Request Forgery) attacks + # + # Here are some resources: + # * http://isc.sans.org/diary.html?storyid=1750 + # * http://en.wikipedia.org/wiki/Cross-site_request_forgery + # + # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. + # There are a few guidelines you should follow: + # + # * Keep your GET requests safe and idempotent. More reading material: + # * http://www.xml.com/pub/a/2002/04/24/deviant.html + # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 + # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session" + # + module ClassMethods + # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked. + # + # Example: + # + # class FooController < ApplicationController + # protect_from_forgery :except => :index + # + # # you can disable csrf protection on controller-by-controller basis: + # skip_before_filter :verify_authenticity_token + # end + # + # Valid Options: + # + # * :only/:except - Passed to the before_filter call. Set which actions are verified. + def protect_from_forgery(options = {}) + self.request_forgery_protection_token ||= :authenticity_token + before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except) + if options[:secret] || options[:digest] + ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller) + end + end + end + + protected + # The actual before_filter that is used. Modify this to change how you handle unverified requests. + def verify_authenticity_token + verified_request? || raise(ActionController::InvalidAuthenticityToken) + end + + # Returns true or false if a request is verified. Checks: + # + # * is the format restricted? By default, only HTML requests are checked. + # * is it a GET request? Gets should be safe and idempotent + # * Does the form_authenticity_token match the given token value from the params? + def verified_request? + !protect_against_forgery? || + request.method == :get || + request.xhr? || + !verifiable_request_format? || + form_authenticity_token == params[request_forgery_protection_token] + end + + def verifiable_request_format? + !request.content_type.nil? && request.content_type.verify_request? + end + + # Sets the token value for the current session. Pass a :secret option + # in +protect_from_forgery+ to add a custom salt to the hash. + def form_authenticity_token + session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32) + end + + def protect_against_forgery? + allow_forgery_protection && request_forgery_protection_token + end + end +end diff --git a/actionpack/lib/action_controller/old_base/rescue.rb b/actionpack/lib/action_controller/old_base/rescue.rb new file mode 100644 index 0000000000..2717a06a37 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/rescue.rb @@ -0,0 +1,50 @@ +module ActionController #:nodoc: + # Actions that fail to perform as expected throw exceptions. These + # exceptions can either be rescued for the public view (with a nice + # user-friendly explanation) or for the developers view (with tons of + # debugging information). The developers view is already implemented by + # the Action Controller, but the public view should be tailored to your + # specific application. + # + # The default behavior for public exceptions is to render a static html + # file with the name of the error code thrown. If no such file exists, an + # empty response is sent with the correct status code. + # + # You can override what constitutes a local request by overriding the + # local_request? method in your own controller. Custom rescue + # behavior is achieved by overriding the rescue_action_in_public + # and rescue_action_locally methods. + module Rescue + def self.included(base) #:nodoc: + base.send :include, ActiveSupport::Rescuable + base.extend(ClassMethods) + + base.class_eval do + alias_method_chain :perform_action, :rescue + end + end + + module ClassMethods + def rescue_action(env) + exception = env.delete('action_dispatch.rescue.exception') + request = ActionDispatch::Request.new(env) + response = ActionDispatch::Response.new + new.process(request, response, :rescue_action, exception).to_a + end + end + + protected + # Exception handler called when the performance of an action raises + # an exception. + def rescue_action(exception) + rescue_with_handler(exception) || raise(exception) + end + + private + def perform_action_with_rescue + perform_action_without_rescue + rescue Exception => exception + rescue_action(exception) + end + end +end diff --git a/actionpack/lib/action_controller/old_base/responder.rb b/actionpack/lib/action_controller/old_base/responder.rb new file mode 100644 index 0000000000..1aee980da6 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/responder.rb @@ -0,0 +1,43 @@ +module ActionController + module Responder + def self.included(klass) + klass.extend ClassMethods + end + + private + def render_for_text(text) #:nodoc: + @performed_render = true + + case text + when Proc + response.body = text + when nil + # Safari 2 doesn't pass response headers if the response is zero-length + if response.body_parts.empty? + response.body_parts << ' ' + end + else + response.body_parts << text + end + end + + # Returns a set of the methods defined as actions in your controller + 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 diff --git a/actionpack/lib/action_controller/old_base/session_management.rb b/actionpack/lib/action_controller/old_base/session_management.rb new file mode 100644 index 0000000000..ffce8e1bd1 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/session_management.rb @@ -0,0 +1,54 @@ +module ActionController #:nodoc: + module SessionManagement #:nodoc: + def self.included(base) + base.class_eval do + extend ClassMethods + end + end + + module ClassMethods + # Set the session store to be used for keeping the session data between requests. + # By default, sessions are stored in browser cookies (:cookie_store), + # but you can also specify one of the other included stores (:active_record_store, + # :mem_cache_store, or your own custom class. + def session_store=(store) + if store == :active_record_store + self.session_store = ActiveRecord::SessionStore + else + @@session_store = store.is_a?(Symbol) ? + Session.const_get(store.to_s.camelize) : + store + end + end + + # Returns the session store class currently used. + def session_store + if defined? @@session_store + @@session_store + else + ActionDispatch::Session::CookieStore + end + end + + def session=(options = {}) + self.session_store = nil if options.delete(:disabled) + session_options.merge!(options) + end + + # Returns the hash used to configure the session. Example use: + # + # ActionController::Base.session_options[:secure] = true # session only available over HTTPS + def session_options + @session_options ||= {} + end + + def session(*args) + ActiveSupport::Deprecation.warn( + "Disabling sessions for a single controller has been deprecated. " + + "Sessions are now lazy loaded. So if you don't access them, " + + "consider them off. You can still modify the session cookie " + + "options with request.session_options.", caller) + end + end + end +end diff --git a/actionpack/lib/action_controller/old_base/streaming.rb b/actionpack/lib/action_controller/old_base/streaming.rb new file mode 100644 index 0000000000..5c72fc9ad9 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/streaming.rb @@ -0,0 +1,188 @@ +module ActionController #:nodoc: + # Methods for sending arbitrary data and for streaming files to the browser, + # instead of rendering. + module Streaming + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include ActionController::Renderer + end + + DEFAULT_SEND_FILE_OPTIONS = { + :type => 'application/octet-stream'.freeze, + :disposition => 'attachment'.freeze, + :stream => true, + :buffer_size => 4096, + :x_sendfile => false + }.freeze + + X_SENDFILE_HEADER = 'X-Sendfile'.freeze + + protected + # Sends the file, by default streaming it 4096 bytes at a time. This way the + # whole file doesn't need to be read into memory at once. This makes it + # feasible to send even large files. You can optionally turn off streaming + # and send the whole file at once. + # + # Be careful to sanitize the path parameter if it is coming from a web + # page. send_file(params[:path]) allows a malicious user to + # download any file on your server. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # Defaults to File.basename(path). + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :length - used to manually override the length (in bytes) of the content that + # is going to be sent to the client. Defaults to File.size(path). + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :stream - whether to send the file to the user agent as it is read (+true+) + # or to read the entire file before sending (+false+). Defaults to +true+. + # * :buffer_size - specifies size (in bytes) of the buffer used to stream the file. + # Defaults to 4096. + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # * :url_based_filename - set to +true+ if you want the browser guess the filename from + # the URL, which is necessary for i18n filenames on certain browsers + # (setting :filename overrides this option). + # * :x_sendfile - uses X-Sendfile to send the file when set to +true+. This is currently + # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this + # uses the web server to send the file, this may lower memory consumption on your server and + # it will not block your application for further requests. + # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and + # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+. + # + # The default Content-Type and Content-Disposition headers are + # set to download arbitrary binary files in as many browsers as + # possible. IE versions 4, 5, 5.5, and 6 are all known to have + # a variety of quirks (especially when downloading over SSL). + # + # Simple download: + # + # send_file '/path/to.zip' + # + # Show a JPEG in the browser: + # + # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' + # + # Show a 404 page in the browser: + # + # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 + # + # Read about the other Content-* HTTP headers if you'd like to + # provide the user with more information (such as Content-Description) in + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. + # + # Also be aware that the document may be cached by proxies and browsers. + # The Pragma and Cache-Control headers declare how the file may be cached + # by intermediaries. They default to require clients to validate with + # the server before releasing cached responses. See + # http://www.mnot.net/cache_docs/ for an overview of web caching and + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + # for the Cache-Control header spec. + def send_file(path, options = {}) #:doc: + raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) + + options[:length] ||= File.size(path) + options[:filename] ||= File.basename(path) unless options[:url_based_filename] + send_file_headers! options + + @performed_render = false + + if options[:x_sendfile] + logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger + head options[:status], X_SENDFILE_HEADER => path + else + if options[:stream] + # TODO : Make render :text => proc {} work with the new base + render :status => options[:status], :text => Proc.new { |response, output| + logger.info "Streaming file #{path}" unless logger.nil? + len = options[:buffer_size] || 4096 + File.open(path, 'rb') do |file| + while buf = file.read(len) + output.write(buf) + end + end + } + else + logger.info "Sending file #{path}" unless logger.nil? + File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read } + end + end + end + + # Sends the given binary data to the browser. This method is similar to + # render :text => data, but also allows you to specify whether + # the browser should display the response as a file attachment (i.e. in a + # download dialog) or as inline data. You may also set the content type, + # the apparent file name, and other things. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # + # Generic data download: + # + # send_data buffer + # + # Download a dynamically-generated tarball: + # + # send_data generate_tgz('dir'), :filename => 'dir.tgz' + # + # Display an image Active Record in the browser: + # + # send_data image.data, :type => image.content_type, :disposition => 'inline' + # + # See +send_file+ for more information on HTTP Content-* headers and caching. + # + # Tip: if you want to stream large amounts of on-the-fly generated + # data to the browser, then use render :text => proc { ... } + # instead. See ActionController::Base#render for more information. + def send_data(data, options = {}) #:doc: + logger.info "Sending data #{options[:filename]}" if logger + send_file_headers! options.merge(:length => data.size) + @performed_render = false + render :status => options[:status], :text => data + end + + private + def send_file_headers!(options) + options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) + [:length, :type, :disposition].each do |arg| + raise ArgumentError, ":#{arg} option required" if options[arg].nil? + end + + disposition = options[:disposition].dup || 'attachment' + + disposition <<= %(; filename="#{options[:filename]}") if options[:filename] + + content_type = options[:type] + + if content_type.is_a?(Symbol) + raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s) + self.content_type = Mime::Type.lookup_by_extension(content_type.to_s) + else + self.content_type = content_type + end + + headers.merge!( + 'Content-Length' => options[:length], + 'Content-Disposition' => disposition, + 'Content-Transfer-Encoding' => 'binary' + ) + + # Fix a problem with IE 6.0 on opening downloaded files: + # If Cache-Control: no-cache is set (which Rails does by default), + # IE removes the file it just downloaded from its cache immediately + # after it displays the "open/save" dialog, which means that if you + # hit "open" the file isn't there anymore when the application that + # is called for handling the download is run, so let's workaround that + headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' + end + end +end diff --git a/actionpack/lib/action_controller/old_base/verification.rb b/actionpack/lib/action_controller/old_base/verification.rb new file mode 100644 index 0000000000..d87b348ed4 --- /dev/null +++ b/actionpack/lib/action_controller/old_base/verification.rb @@ -0,0 +1,133 @@ +module ActionController #:nodoc: + module Verification #:nodoc: + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Callbacks, Session, Flash, Renderer + end + + # This module provides a class-level method for specifying that certain + # actions are guarded against being called without certain prerequisites + # being met. This is essentially a special kind of before_filter. + # + # An action may be guarded against being invoked without certain request + # parameters being set, or without certain session values existing. + # + # When a verification is violated, values may be inserted into the flash, and + # a specified redirection is triggered. If no specific action is configured, + # verification failures will by default result in a 400 Bad Request response. + # + # Usage: + # + # class GlobalController < ActionController::Base + # # Prevent the #update_settings action from being invoked unless + # # the 'admin_privileges' request parameter exists. The + # # settings action will be redirected to in current controller + # # if verification fails. + # verify :params => "admin_privileges", :only => :update_post, + # :redirect_to => { :action => "settings" } + # + # # Disallow a post from being updated if there was no information + # # submitted with the post, and if there is no active post in the + # # session, and if there is no "note" key in the flash. The route + # # named category_url will be redirected to if verification fails. + # + # verify :params => "post", :session => "post", "flash" => "note", + # :only => :update_post, + # :add_flash => { "alert" => "Failed to create your message" }, + # :redirect_to => :category_url + # + # Note that these prerequisites are not business rules. They do not examine + # the content of the session or the parameters. That level of validation should + # be encapsulated by your domain model or helper methods in the controller. + module ClassMethods + # Verify the given actions so that if certain prerequisites are not met, + # the user is redirected to a different action. The +options+ parameter + # is a hash consisting of the following key/value pairs: + # + # :params:: + # a single key or an array of keys that must be in the params + # hash in order for the action(s) to be safely called. + # :session:: + # a single key or an array of keys that must be in the session + # in order for the action(s) to be safely called. + # :flash:: + # a single key or an array of keys that must be in the flash in order + # for the action(s) to be safely called. + # :method:: + # a single key or an array of keys--any one of which must match the + # current request method in order for the action(s) to be safely called. + # (The key should be a symbol: :get or :post, for + # example.) + # :xhr:: + # true/false option to ensure that the request is coming from an Ajax + # call or not. + # :add_flash:: + # a hash of name/value pairs that should be merged into the session's + # flash if the prerequisites cannot be satisfied. + # :add_headers:: + # a hash of name/value pairs that should be merged into the response's + # headers hash if the prerequisites cannot be satisfied. + # :redirect_to:: + # the redirection parameters to be used when redirecting if the + # prerequisites cannot be satisfied. You can redirect either to named + # route or to the action in some controller. + # :render:: + # the render parameters to be used when the prerequisites cannot be satisfied. + # :only:: + # only apply this verification to the actions specified in the associated + # array (may also be a single value). + # :except:: + # do not apply this verification to the actions specified in the associated + # array (may also be a single value). + def verify(options={}) + before_filter :only => options[:only], :except => options[:except] do |c| + c.__send__ :verify_action, options + end + end + end + + private + + def verify_action(options) #:nodoc: + if prereqs_invalid?(options) + flash.update(options[:add_flash]) if options[:add_flash] + response.headers.merge!(options[:add_headers]) if options[:add_headers] + apply_remaining_actions(options) unless performed? + end + end + + def prereqs_invalid?(options) # :nodoc: + verify_presence_of_keys_in_hash_flash_or_params(options) || + verify_method(options) || + verify_request_xhr_status(options) + end + + def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: + [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || + [*options[:session]].find { |v| session[v].nil? } || + [*options[:flash] ].find { |v| flash[v].nil? } + end + + def verify_method(options) # :nodoc: + [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] + end + + def verify_request_xhr_status(options) # :nodoc: + request.xhr? != options[:xhr] unless options[:xhr].nil? + end + + def apply_redirect_to(redirect_to_option) # :nodoc: + (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option + end + + def apply_remaining_actions(options) # :nodoc: + case + when options[:render] ; render(options[:render]) + when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) + else head(:bad_request) + end + end + end +end \ No newline at end of file diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index c71da7fa6c..dab5061414 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,19 +1,26 @@ -if ENV['new_base'] - puts *caller - raise 'new_base/abstract_unit already loaded' -end $:.unshift(File.dirname(__FILE__) + '/../lib') $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') +$:.unshift(File.dirname(__FILE__) + '/lib') + $:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') $:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') -require 'rubygems' -require 'yaml' -require 'stringio' +ENV['new_base'] = "true" +$stderr.puts "Running old tests on new_base" + require 'test/unit' +require 'active_support' + +require 'active_support/test_case' +require 'action_controller/abstract' +require 'action_controller' +require 'fixture_template' +require 'action_controller/testing/process2' +require 'action_view/test_case' +require 'action_controller/testing/integration' +require 'active_support/dependencies' -gem 'mocha', '>= 0.9.5' -require 'mocha' +$tags[:new_base] = true begin require 'ruby-debug' @@ -23,24 +30,144 @@ rescue LoadError # Debugging disabled. `gem install ruby-debug` to enable. end -require 'action_controller' -require 'action_controller/testing/process' -require 'action_view/test_case' - -$tags[:old_base] = true +ActiveSupport::Dependencies.hook! # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true -ActionController::Base.logger = nil -ActionController::Routing::Routes.reload rescue nil - -ActionController::Base.session_store = nil - # Register danish language for testing I18n.backend.store_translations 'da', {} I18n.backend.store_translations 'pt-BR', {} ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') -ActionController::Base.view_paths = FIXTURE_LOAD_PATH +FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), '../fixtures') + +module ActionView + class TestCase + setup do + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + end + end + end +end + +module ActionController + Base.session = { + :key => '_testing_session', + :secret => '8273f16463985e2b3747dc25e30f2528' +} + + class ActionControllerError < StandardError #:nodoc: + end + + class SessionRestoreError < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class RoutingError < ActionControllerError #:nodoc: + attr_reader :failures + def initialize(message, failures=[]) + super(message) + @failures = failures + end + end + + class MethodNotAllowed < ActionControllerError #:nodoc: + attr_reader :allowed_methods + + def initialize(*allowed_methods) + super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") + @allowed_methods = allowed_methods + end + + def allowed_methods_header + allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' + end + + def handle_response!(response) + response.headers['Allow'] ||= allowed_methods_header + end + end + + class NotImplemented < MethodNotAllowed #:nodoc: + end + + class UnknownController < ActionControllerError #:nodoc: + end + + class MissingFile < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class SessionOverflowError < ActionControllerError #:nodoc: + DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + class UnknownHttpMethod < ActionControllerError #:nodoc: + end + + class Base + include ActionController::Testing + end + + Base.view_paths = FIXTURE_LOAD_PATH + + class TestCase + include TestProcess + setup do + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + end + end + + def assert_template(options = {}, message = nil) + validate_request! + + hax = @controller._action_view.instance_variable_get(:@_rendered) + + case options + when NilClass, String + rendered = (hax[:template] || []).map { |t| t.identifier } + msg = build_message(message, + "expecting but rendering with ", + options, rendered.join(', ')) + assert_block(msg) do + if options.nil? + hax[:template].blank? + else + rendered.any? { |t| t.match(options) } + end + end + when Hash + if expected_partial = options[:partial] + partials = hax[:partials] + if expected_count = options[:count] + found = partials.detect { |p, _| p.identifier.match(expected_partial) } + actual_count = found.nil? ? 0 : found.second + msg = build_message(message, + "expecting ? to be rendered ? time(s) but rendered ? time(s)", + expected_partial, expected_count, actual_count) + assert(actual_count == expected_count.to_i, msg) + else + msg = build_message(message, + "expecting partial but action rendered ", + options[:partial], partials.keys) + assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg) + end + else + assert hax[:partials].empty?, + "Expected no partials to be rendered" + end + end + end + end +end diff --git a/actionpack/test/new_base/abstract_unit.rb b/actionpack/test/new_base/abstract_unit.rb deleted file mode 100644 index 7d72f8393b..0000000000 --- a/actionpack/test/new_base/abstract_unit.rb +++ /dev/null @@ -1,173 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../../lib') -$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/../lib') - -$:.unshift(File.dirname(__FILE__) + '/../fixtures/helpers') -$:.unshift(File.dirname(__FILE__) + '/../fixtures/alternate_helpers') - -ENV['new_base'] = "true" -$stderr.puts "Running old tests on new_base" - -require 'test/unit' -require 'active_support' - -require 'active_support/test_case' -require 'action_controller/abstract' -require 'action_controller/new_base' -require 'fixture_template' -require 'action_controller/testing/process2' -require 'action_view/test_case' -require 'action_controller/testing/integration' -require 'active_support/dependencies' - -$tags[:new_base] = true - -begin - require 'ruby-debug' - Debugger.settings[:autoeval] = true - Debugger.start -rescue LoadError - # Debugging disabled. `gem install ruby-debug` to enable. -end - -ActiveSupport::Dependencies.hook! - -# Show backtraces for deprecated behavior for quicker cleanup. -ActiveSupport::Deprecation.debug = true - -# Register danish language for testing -I18n.backend.store_translations 'da', {} -I18n.backend.store_translations 'pt-BR', {} -ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort - -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), '../fixtures') - -module ActionView - class TestCase - setup do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - end - end - end -end - -module ActionController - Base.session = { - :key => '_testing_session', - :secret => '8273f16463985e2b3747dc25e30f2528' -} - - class ActionControllerError < StandardError #:nodoc: - end - - class SessionRestoreError < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class RoutingError < ActionControllerError #:nodoc: - attr_reader :failures - def initialize(message, failures=[]) - super(message) - @failures = failures - end - end - - class MethodNotAllowed < ActionControllerError #:nodoc: - attr_reader :allowed_methods - - def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - - def handle_response!(response) - response.headers['Allow'] ||= allowed_methods_header - end - end - - class NotImplemented < MethodNotAllowed #:nodoc: - end - - class UnknownController < ActionControllerError #:nodoc: - end - - class MissingFile < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class SessionOverflowError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - class UnknownHttpMethod < ActionControllerError #:nodoc: - end - - class Base - include ActionController::Testing - end - - Base.view_paths = FIXTURE_LOAD_PATH - - class TestCase - include TestProcess - setup do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - end - end - - def assert_template(options = {}, message = nil) - validate_request! - - hax = @controller._action_view.instance_variable_get(:@_rendered) - - case options - when NilClass, String - rendered = (hax[:template] || []).map { |t| t.identifier } - msg = build_message(message, - "expecting but rendering with ", - options, rendered.join(', ')) - assert_block(msg) do - if options.nil? - hax[:template].blank? - else - rendered.any? { |t| t.match(options) } - end - end - when Hash - if expected_partial = options[:partial] - partials = hax[:partials] - if expected_count = options[:count] - found = partials.detect { |p, _| p.identifier.match(expected_partial) } - actual_count = found.nil? ? 0 : found.second - msg = build_message(message, - "expecting ? to be rendered ? time(s) but rendered ? time(s)", - expected_partial, expected_count, actual_count) - assert(actual_count == expected_count.to_i, msg) - else - msg = build_message(message, - "expecting partial but action rendered ", - options[:partial], partials.keys) - assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg) - end - else - assert hax[:partials].empty?, - "Expected no partials to be rendered" - end - end - end - end -end diff --git a/actionpack/test/old_base/abstract_unit.rb b/actionpack/test/old_base/abstract_unit.rb new file mode 100644 index 0000000000..c71da7fa6c --- /dev/null +++ b/actionpack/test/old_base/abstract_unit.rb @@ -0,0 +1,46 @@ +if ENV['new_base'] + puts *caller + raise 'new_base/abstract_unit already loaded' +end +$:.unshift(File.dirname(__FILE__) + '/../lib') +$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') +$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') +$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') + +require 'rubygems' +require 'yaml' +require 'stringio' +require 'test/unit' + +gem 'mocha', '>= 0.9.5' +require 'mocha' + +begin + require 'ruby-debug' + Debugger.settings[:autoeval] = true + Debugger.start +rescue LoadError + # Debugging disabled. `gem install ruby-debug` to enable. +end + +require 'action_controller' +require 'action_controller/testing/process' +require 'action_view/test_case' + +$tags[:old_base] = true + +# Show backtraces for deprecated behavior for quicker cleanup. +ActiveSupport::Deprecation.debug = true + +ActionController::Base.logger = nil +ActionController::Routing::Routes.reload rescue nil + +ActionController::Base.session_store = nil + +# Register danish language for testing +I18n.backend.store_translations 'da', {} +I18n.backend.store_translations 'pt-BR', {} +ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort + +FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +ActionController::Base.view_paths = FIXTURE_LOAD_PATH -- cgit v1.2.3 From 80d1e2778860d1825d749aad274e70e0ea810bc6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 11:50:11 -0700 Subject: Fix the fixture path --- actionpack/test/abstract_unit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index dab5061414..a785722d4f 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -40,7 +40,7 @@ I18n.backend.store_translations 'da', {} I18n.backend.store_translations 'pt-BR', {} ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), '../fixtures') +FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') module ActionView class TestCase -- cgit v1.2.3 From 5314abed182450dbdcc25ebe601a2bbdf4cb926f Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 11:59:28 -0700 Subject: Keep ActionMailer using the old layouts code until it gets refactored. --- actionpack/Rakefile | 2 +- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/old_base/layout.rb | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 53387d305c..142c72ce6b 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -22,7 +22,7 @@ task :default => [ :test ] # Run the unit tests desc "Run all unit tests" -task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests] +task :test => [:test_action_pack, :test_active_record_integration, :test_new_base] test_lib_dirs = ENV["NEW"] ? ["test/new_base"] : [] test_lib_dirs.push "test", "test/lib" diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 44724fc9cb..8e5e5736ab 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -42,6 +42,7 @@ module ActionController end autoload :HTML, 'action_controller/vendor/html-scanner' +autoload :AbstractController, 'action_controller/abstract' require 'action_dispatch' require 'action_view' diff --git a/actionpack/lib/action_controller/old_base/layout.rb b/actionpack/lib/action_controller/old_base/layout.rb index cf5f46a32b..3046e082d9 100644 --- a/actionpack/lib/action_controller/old_base/layout.rb +++ b/actionpack/lib/action_controller/old_base/layout.rb @@ -1,8 +1,16 @@ require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/class' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/class/inheritable_attributes' module ActionController #:nodoc: + # MegasuperultraHAX + # plz refactor ActionMailer + class Base + @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] + cattr_accessor :exempt_from_layout + end + module Layout #:nodoc: def self.included(base) base.extend(ClassMethods) -- cgit v1.2.3 From 0e558f0bbdbfecf9ebc5218a78ba224a295c432d Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 15:55:09 -0700 Subject: Get the AR integration tests to pass --- actionpack/test/abstract_unit.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index a785722d4f..a7957ed944 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -54,9 +54,10 @@ end module ActionController Base.session = { - :key => '_testing_session', - :secret => '8273f16463985e2b3747dc25e30f2528' -} + :key => '_testing_session', + :secret => '8273f16463985e2b3747dc25e30f2528' + } + Base.session_store = nil class ActionControllerError < StandardError #:nodoc: end -- cgit v1.2.3 From c7ba9aa195196d534109575affab700f7bc10984 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 15:56:18 -0700 Subject: Get the new base tests to pass --- actionpack/test/new_base/test_helper.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/new_base/test_helper.rb b/actionpack/test/new_base/test_helper.rb index d92029df7f..722dddcfc2 100644 --- a/actionpack/test/new_base/test_helper.rb +++ b/actionpack/test/new_base/test_helper.rb @@ -16,8 +16,7 @@ rescue LoadError # Debugging disabled. `gem install ruby-debug` to enable. end -require 'action_controller/abstract' -require 'action_controller/new_base' +require 'action_controller' require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late require 'action_controller/testing/process' -- cgit v1.2.3 From 7b1f483fda4fc8e4fc931649364a211a9f9d945f Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 16:14:45 -0700 Subject: Get all of rake tests to pass --- actionpack/lib/action_controller.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 8e5e5736ab..924c58e0e8 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -20,12 +20,14 @@ module ActionController # require 'action_controller/routing' autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :Integration, 'action_controller/testing/integration' autoload :MimeResponds, 'action_controller/old_base/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' autoload :SessionManagement, 'action_controller/old_base/session_management' autoload :TestCase, 'action_controller/testing/test_case' + autoload :TestProcess, 'action_controller/testing/process' autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' -- cgit v1.2.3 From 19c3495a671c364e0dc76c276efbcd9dc6914c74 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 16:29:45 -0700 Subject: rm -r controller/base! --- actionpack/lib/action_controller.rb | 18 +- actionpack/lib/action_controller/base/cookies.rb | 94 +++ .../base/filter_parameter_logging.rb | 95 +++ actionpack/lib/action_controller/base/flash.rb | 196 +++++ .../action_controller/base/http_authentication.rb | 309 +++++++ .../lib/action_controller/base/mime_responds.rb | 188 +++++ .../base/request_forgery_protection.rb | 123 +++ .../action_controller/base/session_management.rb | 54 ++ actionpack/lib/action_controller/base/streaming.rb | 188 +++++ .../lib/action_controller/base/verification.rb | 133 ++++ actionpack/lib/action_controller/legacy/layout.rb | 259 ++++++ actionpack/lib/action_controller/old_base/base.rb | 884 --------------------- .../old_base/chained/benchmarking.rb | 107 --- .../action_controller/old_base/chained/filters.rb | 670 ---------------- .../action_controller/old_base/chained/flash.rb | 196 ----- .../lib/action_controller/old_base/cookies.rb | 94 --- .../old_base/filter_parameter_logging.rb | 95 --- .../lib/action_controller/old_base/helpers.rb | 221 ------ .../old_base/http_authentication.rb | 309 ------- .../lib/action_controller/old_base/layout.rb | 259 ------ .../action_controller/old_base/mime_responds.rb | 188 ----- .../lib/action_controller/old_base/redirect.rb | 89 --- .../lib/action_controller/old_base/render.rb | 403 ---------- .../old_base/request_forgery_protection.rb | 123 --- .../lib/action_controller/old_base/rescue.rb | 50 -- .../lib/action_controller/old_base/responder.rb | 43 - .../old_base/session_management.rb | 54 -- .../lib/action_controller/old_base/streaming.rb | 188 ----- .../lib/action_controller/old_base/verification.rb | 133 ---- 29 files changed, 1648 insertions(+), 4115 deletions(-) create mode 100644 actionpack/lib/action_controller/base/cookies.rb create mode 100644 actionpack/lib/action_controller/base/filter_parameter_logging.rb create mode 100644 actionpack/lib/action_controller/base/flash.rb create mode 100644 actionpack/lib/action_controller/base/http_authentication.rb create mode 100644 actionpack/lib/action_controller/base/mime_responds.rb create mode 100644 actionpack/lib/action_controller/base/request_forgery_protection.rb create mode 100644 actionpack/lib/action_controller/base/session_management.rb create mode 100644 actionpack/lib/action_controller/base/streaming.rb create mode 100644 actionpack/lib/action_controller/base/verification.rb create mode 100644 actionpack/lib/action_controller/legacy/layout.rb delete mode 100644 actionpack/lib/action_controller/old_base/base.rb delete mode 100644 actionpack/lib/action_controller/old_base/chained/benchmarking.rb delete mode 100644 actionpack/lib/action_controller/old_base/chained/filters.rb delete mode 100644 actionpack/lib/action_controller/old_base/chained/flash.rb delete mode 100644 actionpack/lib/action_controller/old_base/cookies.rb delete mode 100644 actionpack/lib/action_controller/old_base/filter_parameter_logging.rb delete mode 100644 actionpack/lib/action_controller/old_base/helpers.rb delete mode 100644 actionpack/lib/action_controller/old_base/http_authentication.rb delete mode 100644 actionpack/lib/action_controller/old_base/layout.rb delete mode 100644 actionpack/lib/action_controller/old_base/mime_responds.rb delete mode 100644 actionpack/lib/action_controller/old_base/redirect.rb delete mode 100644 actionpack/lib/action_controller/old_base/render.rb delete mode 100644 actionpack/lib/action_controller/old_base/request_forgery_protection.rb delete mode 100644 actionpack/lib/action_controller/old_base/rescue.rb delete mode 100644 actionpack/lib/action_controller/old_base/responder.rb delete mode 100644 actionpack/lib/action_controller/old_base/session_management.rb delete mode 100644 actionpack/lib/action_controller/old_base/streaming.rb delete mode 100644 actionpack/lib/action_controller/old_base/verification.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 924c58e0e8..073600f69b 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -21,24 +21,24 @@ module ActionController autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' autoload :Integration, 'action_controller/testing/integration' - autoload :MimeResponds, 'action_controller/old_base/mime_responds' + autoload :MimeResponds, 'action_controller/base/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' - autoload :SessionManagement, 'action_controller/old_base/session_management' + autoload :SessionManagement, 'action_controller/base/session_management' autoload :TestCase, 'action_controller/testing/test_case' autoload :TestProcess, 'action_controller/testing/process' autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - autoload :Verification, 'action_controller/old_base/verification' - autoload :Flash, 'action_controller/old_base/chained/flash' - autoload :RequestForgeryProtection, 'action_controller/old_base/request_forgery_protection' - autoload :Streaming, 'action_controller/old_base/streaming' - autoload :HttpAuthentication, 'action_controller/old_base/http_authentication' - autoload :FilterParameterLogging, 'action_controller/old_base/filter_parameter_logging' + autoload :Verification, 'action_controller/base/verification' + autoload :Flash, 'action_controller/base/flash' + autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' + autoload :Streaming, 'action_controller/base/streaming' + autoload :HttpAuthentication, 'action_controller/base/http_authentication' + autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/old_base/cookies' + autoload :Cookies, 'action_controller/base/cookies' require 'action_controller/routing' end diff --git a/actionpack/lib/action_controller/base/cookies.rb b/actionpack/lib/action_controller/base/cookies.rb new file mode 100644 index 0000000000..d4806623c3 --- /dev/null +++ b/actionpack/lib/action_controller/base/cookies.rb @@ -0,0 +1,94 @@ +module ActionController #:nodoc: + # Cookies are read and written through ActionController#cookies. + # + # The cookies being read are the ones received along with the request, the cookies + # being written will be sent out with the response. Reading a cookie does not get + # the cookie object itself back, just the value it holds. + # + # Examples for writing: + # + # # Sets a simple session cookie. + # cookies[:user_name] = "david" + # + # # Sets a cookie that expires in 1 hour. + # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now } + # + # Examples for reading: + # + # cookies[:user_name] # => "david" + # cookies.size # => 2 + # + # Example for deleting: + # + # cookies.delete :user_name + # + # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie: + # + # cookies[:key] = { + # :value => 'a yummy cookie', + # :expires => 1.year.from_now, + # :domain => 'domain.com' + # } + # + # cookies.delete(:key, :domain => 'domain.com') + # + # The option symbols for setting cookies are: + # + # * :value - The cookie's value or list of values (as an array). + # * :path - The path for which this cookie applies. Defaults to the root + # of the application. + # * :domain - The domain for which this cookie applies. + # * :expires - The time at which this cookie expires, as a Time object. + # * :secure - Whether this cookie is a only transmitted to HTTPS servers. + # Default is +false+. + # * :httponly - Whether this cookie is accessible via scripting or + # only HTTP. Defaults to +false+. + module Cookies + def self.included(base) + base.helper_method :cookies + end + + protected + # Returns the cookie container, which operates as described above. + def cookies + @cookies ||= CookieJar.new(self) + end + end + + class CookieJar < Hash #:nodoc: + def initialize(controller) + @controller, @cookies = controller, controller.request.cookies + super() + update(@cookies) + end + + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. + def [](name) + super(name.to_s) + end + + # Sets the cookie named +name+. The second argument may be the very cookie + # value, or a hash of options as documented above. + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + else + options = { :value => options } + end + + options[:path] = "/" unless options.has_key?(:path) + super(key.to_s, options[:value]) + @controller.response.set_cookie(key, options) + end + + # Removes the cookie on the client machine by setting the value to an empty string + # and setting its expiration date into the past. Like []=, you can pass in + # an options hash to delete cookies with extra data such as a :path. + def delete(key, options = {}) + options.symbolize_keys! + options[:path] = "/" unless options.has_key?(:path) + super(key.to_s) + @controller.response.delete_cookie(key, options) + end + end +end diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb new file mode 100644 index 0000000000..8370ba6fc0 --- /dev/null +++ b/actionpack/lib/action_controller/base/filter_parameter_logging.rb @@ -0,0 +1,95 @@ +module ActionController + module FilterParameterLogging + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Logger + end + + included do + include InstanceMethodsForNewBase + end + + module ClassMethods + # Replace sensitive parameter data from the request log. + # Filters parameters that have any of the arguments as a substring. + # Looks in all subhashes of the param hash for keys to filter. + # If a block is given, each key and value of the parameter hash and all + # subhashes is passed to it, the value or key + # can be replaced using String#replace or similar method. + # + # Examples: + # filter_parameter_logging + # => Does nothing, just slows the logging process down + # + # filter_parameter_logging :password + # => replaces the value to all keys matching /password/i with "[FILTERED]" + # + # filter_parameter_logging :foo, "bar" + # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + # + # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i + # + # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i, and + # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + def filter_parameter_logging(*filter_words, &block) + parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 + + define_method(:filter_parameters) do |unfiltered_parameters| + filtered_parameters = {} + + unfiltered_parameters.each do |key, value| + if key =~ parameter_filter + filtered_parameters[key] = '[FILTERED]' + elsif value.is_a?(Hash) + filtered_parameters[key] = filter_parameters(value) + elsif block_given? + key = key.dup + value = value.dup if value + yield key, value + filtered_parameters[key] = value + else + filtered_parameters[key] = value + end + end + + filtered_parameters + end + protected :filter_parameters + end + end + + module InstanceMethodsForNewBase + # TODO : Fix the order of information inside such that it's exactly same as the old base + def process(*) + ret = super + + if logger + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method, :only_path) + + unless parameters.empty? + # TODO : Move DelayedLog to AS + log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" } + logger.info(log) + end + end + + ret + end + end + + private + + # TODO : This method is not needed for the new base + def log_processing_for_parameters + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method) + + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? + end + end +end diff --git a/actionpack/lib/action_controller/base/flash.rb b/actionpack/lib/action_controller/base/flash.rb new file mode 100644 index 0000000000..42c6e430ca --- /dev/null +++ b/actionpack/lib/action_controller/base/flash.rb @@ -0,0 +1,196 @@ +module ActionController #:nodoc: + # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed + # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create + # action that sets flash[:notice] = "Successfully created" before redirecting to a display action that can + # then expose the flash to its template. Actually, that exposure is automatically done. Example: + # + # class PostsController < ActionController::Base + # def create + # # save post + # flash[:notice] = "Successfully created post" + # redirect_to posts_path(@post) + # end + # + # def show + # # doesn't need to assign the flash notice to the template, that's done automatically + # end + # end + # + # show.html.erb + # <% if flash[:notice] %> + #
<%= flash[:notice] %>
+ # <% end %> + # + # This example just places a string in the flash, but you can put any object in there. And of course, you can put as + # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed. + # + # See docs on the FlashHash class for more details about the flash. + module Flash + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + include Session if defined?(ActionController::Http) + + included do + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include InstanceMethodsForNewBase + else + include InstanceMethodsForBase + + alias_method_chain :perform_action, :flash + alias_method_chain :reset_session, :flash + end + end + + class FlashNow #:nodoc: + def initialize(flash) + @flash = flash + end + + def []=(k, v) + @flash[k] = v + @flash.discard(k) + v + end + + def [](k) + @flash[k] + end + end + + class FlashHash < Hash + def initialize #:nodoc: + super + @used = {} + end + + def []=(k, v) #:nodoc: + keep(k) + super + end + + def update(h) #:nodoc: + h.keys.each { |k| keep(k) } + super + end + + alias :merge! :update + + def replace(h) #:nodoc: + @used = {} + super + end + + # Sets a flash that will not be available to the next action, only to the current. + # + # flash.now[:message] = "Hello current action" + # + # This method enables you to use the flash as a central messaging system in your app. + # When you need to pass an object to the next action, you use the standard flash assign ([]=). + # When you need to pass an object to the current action, you use now, and your object will + # vanish when the current action is done. + # + # Entries set via now are accessed the same way as standard entries: flash['my-key']. + def now + FlashNow.new(self) + end + + # Keeps either the entire current flash or a specific flash entry available for the next action: + # + # flash.keep # keeps the entire flash + # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded + def keep(k = nil) + use(k, false) + end + + # Marks the entire flash or a single flash entry to be discarded by the end of the current action: + # + # flash.discard # discard the entire flash at the end of the current action + # flash.discard(:warning) # discard only the "warning" entry at the end of the current action + def discard(k = nil) + use(k) + end + + # Mark for removal entries that were kept, and delete unkept ones. + # + # This method is called automatically by filters, so you generally don't need to care about it. + def sweep #:nodoc: + keys.each do |k| + unless @used[k] + use(k) + else + delete(k) + @used.delete(k) + end + end + + # clean up after keys that could have been left over by calling reject! or shift on the flash + (@used.keys - keys).each{ |k| @used.delete(k) } + end + + def store(session, key = "flash") + return if self.empty? + session[key] = self + end + + private + # Used internally by the keep and discard methods + # use() # marks the entire flash as used + # use('msg') # marks the "msg" entry as used + # use(nil, false) # marks the entire flash as unused (keeps it around for one more action) + # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action) + # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself + # if no key is passed. + def use(key = nil, used = true) + Array(key || keys).each { |k| @used[k] = used } + return key ? self[key] : self + end + end + + module InstanceMethodsForBase #:nodoc: + protected + def perform_action_with_flash + perform_action_without_flash + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end + end + + def reset_session_with_flash + reset_session_without_flash + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end + + module InstanceMethodsForNewBase #:nodoc: + protected + def process_action(method_name) + super + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end + end + + def reset_session + super + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end + + protected + # Access the contents of the flash. Use flash["notice"] to + # read a notice you put there or flash["notice"] = "hello" + # to put a new one. + def flash #:doc: + if !defined?(@_flash) + @_flash = session["flash"] || FlashHash.new + @_flash.sweep + end + + @_flash + end + end +end diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb new file mode 100644 index 0000000000..2519f55269 --- /dev/null +++ b/actionpack/lib/action_controller/base/http_authentication.rb @@ -0,0 +1,309 @@ +require 'active_support/base64' + +module ActionController + module HttpAuthentication + # Makes it dead easy to do HTTP Basic authentication. + # + # Simple Basic example: + # + # class PostsController < ApplicationController + # USER_NAME, PASSWORD = "dhh", "secret" + # + # before_filter :authenticate, :except => [ :index ] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_basic do |user_name, password| + # user_name == USER_NAME && password == PASSWORD + # end + # end + # end + # + # + # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, + # the regular HTML interface is protected by a session approach: + # + # class ApplicationController < ActionController::Base + # before_filter :set_account, :authenticate + # + # protected + # def set_account + # @account = Account.find_by_url_name(request.subdomains.first) + # end + # + # def authenticate + # case request.format + # when Mime::XML, Mime::ATOM + # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } + # @current_user = user + # else + # request_http_basic_authentication + # end + # else + # if session_authenticated? + # @current_user = @account.users.find(session[:authenticated][:user_id]) + # else + # redirect_to(login_url) and return false + # end + # end + # end + # end + # + # In your integration tests, you can do something like this: + # + # def test_access_granted_from_xml + # get( + # "/notes/1.xml", nil, + # :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) + # ) + # + # assert_equal 200, status + # end + # + # Simple Digest example: + # + # require 'digest/md5' + # class PostsController < ApplicationController + # REALM = "SuperSecret" + # USERS = {"dhh" => "secret", #plain text password + # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password + # + # before_filter :authenticate, :except => [:index] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_digest(REALM) do |username| + # USERS[username] + # end + # end + # end + # + # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately + # hash to check the user's credentials. Returning +nil+ will cause authentication to fail. + # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If + # the password file or database is compromised, the attacker would be able to use the ha1 hash to + # authenticate as the user at this +realm+, but would not have the user's password to try using at + # other sites. + # + # On shared hosts, Apache sometimes doesn't pass authentication headers to + # FCGI instances. If your environment matches this description and you cannot + # authenticate, try this rule in your Apache setup: + # + # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] + module Basic + extend self + + module ControllerMethods + def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) + authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) + end + + def authenticate_with_http_basic(&login_procedure) + HttpAuthentication::Basic.authenticate(self, &login_procedure) + end + + def request_http_basic_authentication(realm = "Application") + HttpAuthentication::Basic.authentication_request(self, realm) + end + end + + def authenticate(controller, &login_procedure) + unless authorization(controller.request).blank? + login_procedure.call(*user_name_and_password(controller.request)) + end + end + + def user_name_and_password(request) + decode_credentials(request).split(/:/, 2) + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + def decode_credentials(request) + ActiveSupport::Base64.decode64(authorization(request).split.last || '') + end + + def encode_credentials(user_name, password) + "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}" + end + + def authentication_request(controller, realm) + controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}") + controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized + end + end + + module Digest + extend self + + module ControllerMethods + def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) + authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) + end + + # Authenticate with HTTP Digest, returns true or false + def authenticate_with_http_digest(realm = "Application", &password_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &password_procedure) + end + + # Render output including the HTTP Digest authentication header + def request_http_digest_authentication(realm = "Application", message = nil) + HttpAuthentication::Digest.authentication_request(self, realm, message) + end + end + + # Returns false on a valid response, true otherwise + def authenticate(controller, realm, &password_procedure) + authorization(controller.request) && validate_digest_response(controller.request, realm, &password_procedure) + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + # Returns false unless the request credentials response value matches the expected value. + # First try the password as a ha1 digest password. If this fails, then try it as a plain + # text password. + def validate_digest_response(request, realm, &password_procedure) + credentials = decode_credentials_header(request) + valid_nonce = validate_nonce(request, credentials[:nonce]) + + if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] + password = password_procedure.call(credentials[:username]) + return false unless password + + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + + [true, false].any? do |password_is_ha1| + expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected == credentials[:response] + end + end + end + + # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ + # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead + # of a plain-text password. + def expected_response(http_method, uri, credentials, password, password_is_ha1=true) + ha1 = password_is_ha1 ? password : ha1(credentials, password) + ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) + ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) + end + + def ha1(credentials, password) + ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + end + + def encode_credentials(http_method, credentials, password, password_is_ha1) + credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) + "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') + end + + def decode_credentials_header(request) + decode_credentials(authorization(request)) + end + + def decode_credentials(header) + header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + end + + def authentication_header(controller, realm) + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") + end + + def authentication_request(controller, realm, message = nil) + message ||= "HTTP Digest: Access denied.\n" + authentication_header(controller, realm) + controller.__send__ :render, :text => message, :status => :unauthorized + end + + # Uses an MD5 digest based on time to generate a value to be used only once. + # + # A server-specified data string which should be uniquely generated each time a 401 response is made. + # It is recommended that this string be base64 or hexadecimal data. + # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. + # + # The contents of the nonce are implementation dependent. + # The quality of the implementation depends on a good choice. + # A nonce might, for example, be constructed as the base 64 encoding of + # + # => time-stamp H(time-stamp ":" ETag ":" private-key) + # + # where time-stamp is a server-generated time or other non-repeating value, + # ETag is the value of the HTTP ETag header associated with the requested entity, + # and private-key is data known only to the server. + # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and + # reject the request if it did not match the nonce from that header or + # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. + # The inclusion of the ETag prevents a replay request for an updated version of the resource. + # (Note: including the IP address of the client in the nonce would appear to offer the server the ability + # to limit the reuse of the nonce to the same client that originally got it. + # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. + # Also, IP address spoofing is not that hard.) + # + # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to + # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for + # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 + # of this document. + # + # The nonce is opaque to the client. Composed of Time, and hash of Time with secret + # key from the Rails session secret generated upon creation of project. Ensures + # the time cannot be modifed by client. + def nonce(time = Time.now) + t = time.to_i + hashed = [t, secret_key] + digest = ::Digest::MD5.hexdigest(hashed.join(":")) + ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') + end + + # Might want a shorter timeout depending on whether the request + # is a PUT or POST, and if client is browser or web service. + # Can be much shorter if the Stale directive is implemented. This would + # allow a user to use new nonce without prompting user again for their + # username and password. + def validate_nonce(request, value, seconds_to_timeout=5*60) + t = ActiveSupport::Base64.decode64(value).split(":").first.to_i + nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout + end + + # Opaque based on random generation - but changing each request? + def opaque() + ::Digest::MD5.hexdigest(secret_key) + end + + # Set in /initializers/session_store.rb, and loaded even if sessions are not in use. + def secret_key + ActionController::Base.session_options[:secret] + end + + end + end +end diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb new file mode 100644 index 0000000000..5c7218691e --- /dev/null +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -0,0 +1,188 @@ +module ActionController #:nodoc: + module MimeResponds #:nodoc: + # Without web-service support, an action which collects the data for displaying a list of people + # might look something like this: + # + # def index + # @people = Person.find(:all) + # end + # + # Here's the same action, with web-service support baked in: + # + # def index + # @people = Person.find(:all) + # + # respond_to do |format| + # format.html + # format.xml { render :xml => @people.to_xml } + # end + # end + # + # What that says is, "if the client wants HTML in response to this action, just respond as we + # would have before, but if the client wants XML, return them the list of people in XML format." + # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) + # + # Supposing you have an action that adds a new person, optionally creating their company + # (by name) if it does not already exist, without web-services, it might look like this: + # + # def create + # @company = Company.find_or_create_by_name(params[:company][:name]) + # @person = @company.people.create(params[:person]) + # + # redirect_to(person_list_url) + # end + # + # Here's the same action, with web-service support baked in: + # + # def create + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # @person = @company.people.create(params[:person]) + # + # respond_to do |format| + # format.html { redirect_to(person_list_url) } + # format.js + # format.xml { render :xml => @person.to_xml(:include => @company) } + # end + # end + # + # If the client wants HTML, we just redirect them back to the person list. If they want Javascript + # (format.js), then it is an RJS request and we render the RJS template associated with this action. + # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also + # include the person's company in the rendered XML, so you get something like this: + # + # + # ... + # ... + # + # ... + # ... + # ... + # + # + # + # Note, however, the extra bit at the top of that action: + # + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # + # This is because the incoming XML document (if a web-service request is in process) can only contain a + # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): + # + # person[name]=...&person[company][name]=...&... + # + # And, like this (xml-encoded): + # + # + # ... + # + # ... + # + # + # + # In other words, we make the request so that it operates on a single entity's person. Then, in the action, + # we extract the company data from the request, find or create the company, and then create the new person + # with the remaining data. + # + # Note that you can define your own XML parameter parser which would allow you to describe multiple entities + # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow + # and accept Rails' defaults, life will be much easier. + # + # If you need to use a MIME type which isn't supported by default, you can register your own handlers in + # environment.rb as follows. + # + # Mime::Type.register "image/jpg", :jpg + def respond_to(*types, &block) + raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block + block ||= lambda { |responder| types.each { |type| responder.send(type) } } + responder = Responder.new(self) + block.call(responder) + responder.respond + end + + class Responder #:nodoc: + + def initialize(controller) + @controller = controller + @request = controller.request + @response = controller.response + + @mime_type_priority = @request.formats + + @order = [] + @responses = {} + end + + def custom(mime_type, &block) + mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) + + @order << mime_type + + @responses[mime_type] ||= Proc.new do + # TODO: Remove this when new base is merged in + if defined?(Http) + @controller.formats = [mime_type.to_sym] + end + + @controller.template.formats = [mime_type.to_sym] + @response.content_type = mime_type + + block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) + end + end + + def any(*args, &block) + if args.any? + args.each { |type| send(type, &block) } + else + custom(@mime_type_priority.first, &block) + end + end + + def self.generate_method_for_mime(mime) + sym = mime.is_a?(Symbol) ? mime : mime.to_sym + const = sym.to_s.upcase + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{sym}(&block) # def html(&block) + custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) + end # end + RUBY + end + + Mime::SET.each do |mime| + generate_method_for_mime(mime) + end + + def method_missing(symbol, &block) + mime_constant = Mime.const_get(symbol.to_s.upcase) + + if Mime::SET.include?(mime_constant) + self.class.generate_method_for_mime(mime_constant) + send(symbol, &block) + else + super + end + end + + def respond + for priority in @mime_type_priority + if priority == Mime::ALL + @responses[@order.first].call + return + else + if @responses[priority] + @responses[priority].call + return # mime type match found, be happy and return + end + end + end + + if @order.include?(Mime::ALL) + @responses[Mime::ALL].call + else + @controller.send :head, :not_acceptable + end + end + end + end +end diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb new file mode 100644 index 0000000000..a470c8eec1 --- /dev/null +++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb @@ -0,0 +1,123 @@ +module ActionController #:nodoc: + class InvalidAuthenticityToken < ActionControllerError #:nodoc: + end + + module RequestForgeryProtection + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Helpers, Session + end + + included do + if defined?(ActionController::Http) + # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ + # sets it to :authenticity_token by default. + cattr_accessor :request_forgery_protection_token + + # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + class_inheritable_accessor :allow_forgery_protection + self.allow_forgery_protection = true + end + + helper_method :form_authenticity_token + helper_method :protect_against_forgery? + end + + # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a + # forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all + # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only + # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication + # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway. + # + # This is turned on with the protect_from_forgery method, which will check the token and raise an + # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in + # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0 + # applications. + # + # The token parameter is named authenticity_token by default. If you are generating an HTML form manually (without the + # use of Rails' form_for, form_tag or other helpers), you have to include a hidden field named like that and + # set its value to what is returned by form_authenticity_token. Same applies to manually constructed Ajax requests. To + # make the token available through a global variable to scripts on a certain page, you could add something like this to a view: + # + # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %> + # + # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to + # config/environments/test.rb: + # + # # Disable request forgery protection in test environment + # config.action_controller.allow_forgery_protection = false + # + # == Learn more about CSRF (Cross-Site Request Forgery) attacks + # + # Here are some resources: + # * http://isc.sans.org/diary.html?storyid=1750 + # * http://en.wikipedia.org/wiki/Cross-site_request_forgery + # + # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. + # There are a few guidelines you should follow: + # + # * Keep your GET requests safe and idempotent. More reading material: + # * http://www.xml.com/pub/a/2002/04/24/deviant.html + # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 + # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session" + # + module ClassMethods + # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked. + # + # Example: + # + # class FooController < ApplicationController + # protect_from_forgery :except => :index + # + # # you can disable csrf protection on controller-by-controller basis: + # skip_before_filter :verify_authenticity_token + # end + # + # Valid Options: + # + # * :only/:except - Passed to the before_filter call. Set which actions are verified. + def protect_from_forgery(options = {}) + self.request_forgery_protection_token ||= :authenticity_token + before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except) + if options[:secret] || options[:digest] + ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller) + end + end + end + + protected + # The actual before_filter that is used. Modify this to change how you handle unverified requests. + def verify_authenticity_token + verified_request? || raise(ActionController::InvalidAuthenticityToken) + end + + # Returns true or false if a request is verified. Checks: + # + # * is the format restricted? By default, only HTML requests are checked. + # * is it a GET request? Gets should be safe and idempotent + # * Does the form_authenticity_token match the given token value from the params? + def verified_request? + !protect_against_forgery? || + request.method == :get || + request.xhr? || + !verifiable_request_format? || + form_authenticity_token == params[request_forgery_protection_token] + end + + def verifiable_request_format? + !request.content_type.nil? && request.content_type.verify_request? + end + + # Sets the token value for the current session. Pass a :secret option + # in +protect_from_forgery+ to add a custom salt to the hash. + def form_authenticity_token + session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32) + end + + def protect_against_forgery? + allow_forgery_protection && request_forgery_protection_token + end + end +end diff --git a/actionpack/lib/action_controller/base/session_management.rb b/actionpack/lib/action_controller/base/session_management.rb new file mode 100644 index 0000000000..ffce8e1bd1 --- /dev/null +++ b/actionpack/lib/action_controller/base/session_management.rb @@ -0,0 +1,54 @@ +module ActionController #:nodoc: + module SessionManagement #:nodoc: + def self.included(base) + base.class_eval do + extend ClassMethods + end + end + + module ClassMethods + # Set the session store to be used for keeping the session data between requests. + # By default, sessions are stored in browser cookies (:cookie_store), + # but you can also specify one of the other included stores (:active_record_store, + # :mem_cache_store, or your own custom class. + def session_store=(store) + if store == :active_record_store + self.session_store = ActiveRecord::SessionStore + else + @@session_store = store.is_a?(Symbol) ? + Session.const_get(store.to_s.camelize) : + store + end + end + + # Returns the session store class currently used. + def session_store + if defined? @@session_store + @@session_store + else + ActionDispatch::Session::CookieStore + end + end + + def session=(options = {}) + self.session_store = nil if options.delete(:disabled) + session_options.merge!(options) + end + + # Returns the hash used to configure the session. Example use: + # + # ActionController::Base.session_options[:secure] = true # session only available over HTTPS + def session_options + @session_options ||= {} + end + + def session(*args) + ActiveSupport::Deprecation.warn( + "Disabling sessions for a single controller has been deprecated. " + + "Sessions are now lazy loaded. So if you don't access them, " + + "consider them off. You can still modify the session cookie " + + "options with request.session_options.", caller) + end + end + end +end diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb new file mode 100644 index 0000000000..5c72fc9ad9 --- /dev/null +++ b/actionpack/lib/action_controller/base/streaming.rb @@ -0,0 +1,188 @@ +module ActionController #:nodoc: + # Methods for sending arbitrary data and for streaming files to the browser, + # instead of rendering. + module Streaming + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include ActionController::Renderer + end + + DEFAULT_SEND_FILE_OPTIONS = { + :type => 'application/octet-stream'.freeze, + :disposition => 'attachment'.freeze, + :stream => true, + :buffer_size => 4096, + :x_sendfile => false + }.freeze + + X_SENDFILE_HEADER = 'X-Sendfile'.freeze + + protected + # Sends the file, by default streaming it 4096 bytes at a time. This way the + # whole file doesn't need to be read into memory at once. This makes it + # feasible to send even large files. You can optionally turn off streaming + # and send the whole file at once. + # + # Be careful to sanitize the path parameter if it is coming from a web + # page. send_file(params[:path]) allows a malicious user to + # download any file on your server. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # Defaults to File.basename(path). + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :length - used to manually override the length (in bytes) of the content that + # is going to be sent to the client. Defaults to File.size(path). + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :stream - whether to send the file to the user agent as it is read (+true+) + # or to read the entire file before sending (+false+). Defaults to +true+. + # * :buffer_size - specifies size (in bytes) of the buffer used to stream the file. + # Defaults to 4096. + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # * :url_based_filename - set to +true+ if you want the browser guess the filename from + # the URL, which is necessary for i18n filenames on certain browsers + # (setting :filename overrides this option). + # * :x_sendfile - uses X-Sendfile to send the file when set to +true+. This is currently + # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this + # uses the web server to send the file, this may lower memory consumption on your server and + # it will not block your application for further requests. + # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and + # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+. + # + # The default Content-Type and Content-Disposition headers are + # set to download arbitrary binary files in as many browsers as + # possible. IE versions 4, 5, 5.5, and 6 are all known to have + # a variety of quirks (especially when downloading over SSL). + # + # Simple download: + # + # send_file '/path/to.zip' + # + # Show a JPEG in the browser: + # + # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' + # + # Show a 404 page in the browser: + # + # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 + # + # Read about the other Content-* HTTP headers if you'd like to + # provide the user with more information (such as Content-Description) in + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. + # + # Also be aware that the document may be cached by proxies and browsers. + # The Pragma and Cache-Control headers declare how the file may be cached + # by intermediaries. They default to require clients to validate with + # the server before releasing cached responses. See + # http://www.mnot.net/cache_docs/ for an overview of web caching and + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + # for the Cache-Control header spec. + def send_file(path, options = {}) #:doc: + raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) + + options[:length] ||= File.size(path) + options[:filename] ||= File.basename(path) unless options[:url_based_filename] + send_file_headers! options + + @performed_render = false + + if options[:x_sendfile] + logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger + head options[:status], X_SENDFILE_HEADER => path + else + if options[:stream] + # TODO : Make render :text => proc {} work with the new base + render :status => options[:status], :text => Proc.new { |response, output| + logger.info "Streaming file #{path}" unless logger.nil? + len = options[:buffer_size] || 4096 + File.open(path, 'rb') do |file| + while buf = file.read(len) + output.write(buf) + end + end + } + else + logger.info "Sending file #{path}" unless logger.nil? + File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read } + end + end + end + + # Sends the given binary data to the browser. This method is similar to + # render :text => data, but also allows you to specify whether + # the browser should display the response as a file attachment (i.e. in a + # download dialog) or as inline data. You may also set the content type, + # the apparent file name, and other things. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # + # Generic data download: + # + # send_data buffer + # + # Download a dynamically-generated tarball: + # + # send_data generate_tgz('dir'), :filename => 'dir.tgz' + # + # Display an image Active Record in the browser: + # + # send_data image.data, :type => image.content_type, :disposition => 'inline' + # + # See +send_file+ for more information on HTTP Content-* headers and caching. + # + # Tip: if you want to stream large amounts of on-the-fly generated + # data to the browser, then use render :text => proc { ... } + # instead. See ActionController::Base#render for more information. + def send_data(data, options = {}) #:doc: + logger.info "Sending data #{options[:filename]}" if logger + send_file_headers! options.merge(:length => data.size) + @performed_render = false + render :status => options[:status], :text => data + end + + private + def send_file_headers!(options) + options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) + [:length, :type, :disposition].each do |arg| + raise ArgumentError, ":#{arg} option required" if options[arg].nil? + end + + disposition = options[:disposition].dup || 'attachment' + + disposition <<= %(; filename="#{options[:filename]}") if options[:filename] + + content_type = options[:type] + + if content_type.is_a?(Symbol) + raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s) + self.content_type = Mime::Type.lookup_by_extension(content_type.to_s) + else + self.content_type = content_type + end + + headers.merge!( + 'Content-Length' => options[:length], + 'Content-Disposition' => disposition, + 'Content-Transfer-Encoding' => 'binary' + ) + + # Fix a problem with IE 6.0 on opening downloaded files: + # If Cache-Control: no-cache is set (which Rails does by default), + # IE removes the file it just downloaded from its cache immediately + # after it displays the "open/save" dialog, which means that if you + # hit "open" the file isn't there anymore when the application that + # is called for handling the download is run, so let's workaround that + headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' + end + end +end diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb new file mode 100644 index 0000000000..d87b348ed4 --- /dev/null +++ b/actionpack/lib/action_controller/base/verification.rb @@ -0,0 +1,133 @@ +module ActionController #:nodoc: + module Verification #:nodoc: + extend ActiveSupport::Concern + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include AbstractController::Callbacks, Session, Flash, Renderer + end + + # This module provides a class-level method for specifying that certain + # actions are guarded against being called without certain prerequisites + # being met. This is essentially a special kind of before_filter. + # + # An action may be guarded against being invoked without certain request + # parameters being set, or without certain session values existing. + # + # When a verification is violated, values may be inserted into the flash, and + # a specified redirection is triggered. If no specific action is configured, + # verification failures will by default result in a 400 Bad Request response. + # + # Usage: + # + # class GlobalController < ActionController::Base + # # Prevent the #update_settings action from being invoked unless + # # the 'admin_privileges' request parameter exists. The + # # settings action will be redirected to in current controller + # # if verification fails. + # verify :params => "admin_privileges", :only => :update_post, + # :redirect_to => { :action => "settings" } + # + # # Disallow a post from being updated if there was no information + # # submitted with the post, and if there is no active post in the + # # session, and if there is no "note" key in the flash. The route + # # named category_url will be redirected to if verification fails. + # + # verify :params => "post", :session => "post", "flash" => "note", + # :only => :update_post, + # :add_flash => { "alert" => "Failed to create your message" }, + # :redirect_to => :category_url + # + # Note that these prerequisites are not business rules. They do not examine + # the content of the session or the parameters. That level of validation should + # be encapsulated by your domain model or helper methods in the controller. + module ClassMethods + # Verify the given actions so that if certain prerequisites are not met, + # the user is redirected to a different action. The +options+ parameter + # is a hash consisting of the following key/value pairs: + # + # :params:: + # a single key or an array of keys that must be in the params + # hash in order for the action(s) to be safely called. + # :session:: + # a single key or an array of keys that must be in the session + # in order for the action(s) to be safely called. + # :flash:: + # a single key or an array of keys that must be in the flash in order + # for the action(s) to be safely called. + # :method:: + # a single key or an array of keys--any one of which must match the + # current request method in order for the action(s) to be safely called. + # (The key should be a symbol: :get or :post, for + # example.) + # :xhr:: + # true/false option to ensure that the request is coming from an Ajax + # call or not. + # :add_flash:: + # a hash of name/value pairs that should be merged into the session's + # flash if the prerequisites cannot be satisfied. + # :add_headers:: + # a hash of name/value pairs that should be merged into the response's + # headers hash if the prerequisites cannot be satisfied. + # :redirect_to:: + # the redirection parameters to be used when redirecting if the + # prerequisites cannot be satisfied. You can redirect either to named + # route or to the action in some controller. + # :render:: + # the render parameters to be used when the prerequisites cannot be satisfied. + # :only:: + # only apply this verification to the actions specified in the associated + # array (may also be a single value). + # :except:: + # do not apply this verification to the actions specified in the associated + # array (may also be a single value). + def verify(options={}) + before_filter :only => options[:only], :except => options[:except] do |c| + c.__send__ :verify_action, options + end + end + end + + private + + def verify_action(options) #:nodoc: + if prereqs_invalid?(options) + flash.update(options[:add_flash]) if options[:add_flash] + response.headers.merge!(options[:add_headers]) if options[:add_headers] + apply_remaining_actions(options) unless performed? + end + end + + def prereqs_invalid?(options) # :nodoc: + verify_presence_of_keys_in_hash_flash_or_params(options) || + verify_method(options) || + verify_request_xhr_status(options) + end + + def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: + [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || + [*options[:session]].find { |v| session[v].nil? } || + [*options[:flash] ].find { |v| flash[v].nil? } + end + + def verify_method(options) # :nodoc: + [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] + end + + def verify_request_xhr_status(options) # :nodoc: + request.xhr? != options[:xhr] unless options[:xhr].nil? + end + + def apply_redirect_to(redirect_to_option) # :nodoc: + (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option + end + + def apply_remaining_actions(options) # :nodoc: + case + when options[:render] ; render(options[:render]) + when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) + else head(:bad_request) + end + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_controller/legacy/layout.rb b/actionpack/lib/action_controller/legacy/layout.rb new file mode 100644 index 0000000000..3046e082d9 --- /dev/null +++ b/actionpack/lib/action_controller/legacy/layout.rb @@ -0,0 +1,259 @@ +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/class' +require 'active_support/core_ext/class/delegating_attributes' +require 'active_support/core_ext/class/inheritable_attributes' + +module ActionController #:nodoc: + # MegasuperultraHAX + # plz refactor ActionMailer + class Base + @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] + cattr_accessor :exempt_from_layout + end + + module Layout #:nodoc: + def self.included(base) + base.extend(ClassMethods) + 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 + # repeated setups. The inclusion pattern has pages that look like this: + # + # <%= render "shared/header" %> + # Hello World + # <%= render "shared/footer" %> + # + # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose + # and if you ever want to change the structure of these two includes, you'll have to change all the templates. + # + # With layouts, you can flip it around and have the common structure know where to insert changing content. This means + # that the header and footer are only mentioned in one place, like this: + # + # // The header part of this layout + # <%= yield %> + # // The footer part of this layout + # + # And then you have content pages that look like this: + # + # hello world + # + # At rendering time, the content page is computed and then inserted in the layout, like this: + # + # // The header part of this layout + # hello world + # // The footer part of this layout + # + # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance + # variable. The preferred notation now is to use yield, as documented above. + # + # == Accessing shared variables + # + # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with + # references that won't materialize before rendering time: + # + #

<%= @page_title %>

+ # <%= yield %> + # + # ...and content pages that fulfill these references _at_ rendering time: + # + # <% @page_title = "Welcome" %> + # Off-world colonies offers you a chance to start a new life + # + # The result after rendering is: + # + #

Welcome

+ # Off-world colonies offers you a chance to start a new life + # + # == Automatic layout assignment + # + # If there is a template in app/views/layouts/ with the same name as the current controller then it will be automatically + # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named + # app/views/layouts/weblog.erb or app/views/layouts/weblog.builder exists then it will be automatically set as + # the layout for your WeblogController. You can create a layout with the name application.erb or application.builder + # and this will be set as the default controller if there is no layout with the same name as the current controller and there is + # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout. + # assignment. So an Admin::WeblogController will look for a template named app/views/layouts/admin/weblog.erb. + # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set. + # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child + # class has a layout with the same name. + # + # == Inheritance for layouts + # + # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples: + # + # class BankController < ActionController::Base + # layout "bank_standard" + # + # class InformationController < BankController + # + # class VaultController < BankController + # layout :access_level_layout + # + # class EmployeeController < BankController + # layout nil + # + # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites + # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. + # + # == Types of layouts + # + # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes + # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can + # be done either by specifying a method reference as a symbol or using an inline method (as a proc). + # + # The method reference is the preferred approach to variable layouts and is used like this: + # + # class WeblogController < ActionController::Base + # layout :writers_and_readers + # + # def index + # # fetching posts + # end + # + # private + # def writers_and_readers + # logged_in? ? "writer_layout" : "reader_layout" + # end + # + # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing + # is logged in or not. + # + # If you want to use an inline method, such as a proc, do something like this: + # + # class WeblogController < ActionController::Base + # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } + # + # Of course, the most common way of specifying a layout is still just as a plain template name: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. + # Otherwise, it will be looked up relative to the template root. + # + # == Conditional layouts + # + # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering + # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The + # :only and :except options can be passed to the layout call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard", :except => :rss + # + # # ... + # + # end + # + # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout + # around the rendered view. + # + # Both the :only and :except condition can accept an arbitrary number of method references, so + # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. + # + # == Using a different layout in the action render call + # + # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. + # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. + # You can do this by passing a :layout option to the render call. For example: + # + # class WeblogController < ActionController::Base + # layout "weblog_standard" + # + # def help + # render :action => "help", :layout => "help" + # end + # end + # + # 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 yields. 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) + self.layout_name = template_name + end + + 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(*args) + memoized_default_layout(*args) + @_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new + @_memoized_default_layout[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 => formats}, prefix) + end + + def find_layout(*args) + @_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new + @_memoized_find_layout[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 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 add_layout_conditions(conditions) + # :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 + + 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 + + self.class.find_layout(layout_name, formats) + end + + def _pick_layout(layout_name = nil, 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 + if only = conditions[:only] + return only.include?(action_name) + elsif except = conditions[:except] + return !except.include?(action_name) + end + end + true + end + + end +end diff --git a/actionpack/lib/action_controller/old_base/base.rb b/actionpack/lib/action_controller/old_base/base.rb deleted file mode 100644 index 67369eb122..0000000000 --- a/actionpack/lib/action_controller/old_base/base.rb +++ /dev/null @@ -1,884 +0,0 @@ -require 'action_controller/deprecated' -require 'set' -require 'active_support/core_ext/class/inheritable_attributes' -require 'active_support/core_ext/module/attr_internal' - -module ActionController #:nodoc: - class ActionControllerError < StandardError #:nodoc: - end - - class SessionRestoreError < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class RoutingError < ActionControllerError #:nodoc: - attr_reader :failures - def initialize(message, failures=[]) - super(message) - @failures = failures - end - end - - class MethodNotAllowed < ActionControllerError #:nodoc: - attr_reader :allowed_methods - - def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - end - - class NotImplemented < MethodNotAllowed #:nodoc: - end - - class UnknownController < ActionControllerError #:nodoc: - end - - class UnknownAction < ActionControllerError #:nodoc: - end - - class MissingFile < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class SessionOverflowError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - class UnknownHttpMethod < ActionControllerError #:nodoc: - end - - # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed - # on request and then either render a template or redirect to another action. An action is defined as a public method - # on the controller, which will automatically be made accessible to the web-server through Rails Routes. - # - # A sample controller could look like this: - # - # class GuestBookController < ActionController::Base - # def index - # @entries = Entry.find(:all) - # end - # - # def sign - # Entry.create(params[:entry]) - # redirect_to :action => "index" - # end - # end - # - # Actions, by default, render a template in the app/views directory corresponding to the name of the controller and action - # after executing code in the action. For example, the +index+ action of the GuestBookController would render the - # template app/views/guestbook/index.erb by default after populating the @entries instance variable. - # - # Unlike index, the sign action will not render a template. After performing its main purpose (creating a - # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external - # "302 Moved" HTTP response that takes the user to the index action. - # - # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect. - # Most actions are variations of these themes. - # - # == Requests - # - # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters. - # This value should hold the name of the action to be performed. Once the action has been identified, the remaining - # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to - # the action through instance variables. Then the action is performed. - # - # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries - # are made by accessing the environment hash, like this: - # - # def server_ip - # location = request.env["SERVER_ADDR"] - # render :text => "This server hosted at #{location}" - # end - # - # == Parameters - # - # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method - # which returns a hash. For example, an action that was performed through /weblog/list?category=All&limit=5 will include - # { "category" => "All", "limit" => 5 } in params. - # - # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as: - # - # - # - # - # A request stemming from a form holding these inputs will include { "post" => { "name" => "david", "address" => "hyacintvej" } }. - # If the address input had been named "post[address][street]", the params would have included - # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting. - # - # == Sessions - # - # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted, - # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such - # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely - # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at. - # - # You can place objects in the session by using the session method, which accesses a hash: - # - # session[:person] = Person.authenticate(user_name, password) - # - # And retrieved again through the same hash: - # - # Hello #{session[:person]} - # - # For removing objects from the session, you can either assign a single key to +nil+: - # - # # removes :person from session - # session[:person] = nil - # - # or you can remove the entire session with +reset_session+. - # - # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted. - # This prevents the user from tampering with the session but also allows him to see its contents. - # - # Do not put secret information in cookie-based sessions! - # - # Other options for session storage are: - # - # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and, - # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set - # - # config.action_controller.session_store = :active_record_store - # - # in your config/environment.rb and run rake db:sessions:create. - # - # * MemCacheStore - Sessions are stored as entries in your memcached cache. - # Set the session store type in config/environment.rb: - # - # config.action_controller.session_store = :mem_cache_store - # - # This assumes that memcached has been installed and configured properly. - # See the MemCacheStore docs for more information. - # - # == Responses - # - # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response - # object is generated automatically through the use of renders and redirects and requires no user intervention. - # - # == Renders - # - # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering - # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured. - # The controller passes objects to the view by assigning instance variables: - # - # def show - # @post = Post.find(params[:id]) - # end - # - # Which are then automatically available to the view: - # - # Title: <%= @post.title %> - # - # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use - # the manual rendering methods: - # - # def search - # @results = Search.find(params[:query]) - # case @results - # when 0 then render :action => "no_results" - # when 1 then render :action => "show" - # when 2..10 then render :action => "show_many" - # end - # end - # - # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html. - # - # == Redirects - # - # Redirects are used to move from one action to another. For example, after a create action, which stores a blog entry to a database, - # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to) - # a show action that we'll assume has already been created. The code might look like this: - # - # def create - # @entry = Entry.new(params[:entry]) - # if @entry.save - # # The entry was saved correctly, redirect to show - # redirect_to :action => 'show', :id => @entry.id - # else - # # things didn't go so well, do something else - # end - # end - # - # In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed. - # - # == Calling multiple redirects or renders - # - # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError: - # - # def do_something - # redirect_to :action => "elsewhere" - # render :action => "overthere" # raises DoubleRenderError - # end - # - # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution. - # - # def do_something - # redirect_to(:action => "elsewhere") and return if monkeys.nil? - # render :action => "overthere" # won't be called if monkeys is nil - # end - # - class Base - - include ActionDispatch::StatusCodes - - cattr_reader :protected_instance_variables - # Controller specific instance variables which will not be accessible inside views. - @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller - @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params - @_flash @_response) - - # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, - # and images to a dedicated asset server away from the main web server. Example: - # ActionController::Base.asset_host = "http://assets.example.com" - cattr_accessor :asset_host - - # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors. - # When the application is ready to go public, this should be set to false, and the protected method local_request? - # should instead be implemented in the controller to determine when debugging screens should be shown. - @@consider_all_requests_local = true - cattr_accessor :consider_all_requests_local - - # Indicates whether to allow concurrent action processing. Your - # controller actions and any other code they call must also behave well - # when called from concurrent threads. Turned off by default. - @@allow_concurrency = false - cattr_accessor :allow_concurrency - - # Modern REST web services often need to submit complex data to the web application. - # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the - # params hash. These handlers are invoked for POST and PUT requests. - # - # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated - # in the params. This allows XML requests to mask themselves as regular form submissions, so you can have one - # action serve both regular forms and web service requests. - # - # Example of doing your own parser for a custom content type: - # - # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data| - # node = REXML::Document.new(post) - # { node.root.name => node.root } - # end - # - # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the - # root node for such requests. The new default is to keep the root, such that "David" results - # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can - # re-register XmlSimple as application/xml handler ike this: - # - # ActionController::Base.param_parsers[Mime::XML] = - # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) } - # - # A YAML parser is also available and can be turned on with: - # - # ActionController::Base.param_parsers[Mime::YAML] = :yaml - @@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. - @@default_charset = "utf-8" - cattr_accessor :default_charset - - # The logger is used for generating information on the action run-time (including benchmarking) if available. - # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers. - cattr_accessor :logger - - # Controls the resource action separator - @@resource_action_separator = "/" - cattr_accessor :resource_action_separator - - # Allow to override path names for default resources' actions - @@resources_path_names = { :new => 'new', :edit => 'edit' } - cattr_accessor :resources_path_names - - # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ - # sets it to :authenticity_token by default. - cattr_accessor :request_forgery_protection_token - - # Controls the IP Spoofing check when determining the remote IP. - @@ip_spoofing_check = true - cattr_accessor :ip_spoofing_check - - # Indicates whether or not optimise the generated named - # route helper methods - cattr_accessor :optimise_named_routes - self.optimise_named_routes = true - - # Indicates whether the response format should be determined by examining the Accept HTTP header, - # or by using the simpler params + ajax rules. - # - # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept - # header into account. If it is set to false then the request format will be determined solely - # by examining params[:format]. If params format is missing, the format will be either HTML or - # Javascript depending on whether the request is an AJAX request. - cattr_accessor :use_accept_header - self.use_accept_header = true - - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection - self.allow_forgery_protection = true - - # If you are deploying to a subdirectory, you will need to set - # config.action_controller.relative_url_root - # This defaults to ENV['RAILS_RELATIVE_URL_ROOT'] - cattr_accessor :relative_url_root - self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] - - # Holds the request object that's primarily used to get environment variables through access like - # request.env["REQUEST_URI"]. - attr_internal :request - - # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like params["post_id"] - # to get the post_id. No type casts are made, so all values are returned as strings. - attr_internal :params - - # Holds the response object that's primarily used to set additional HTTP headers through access like - # response.headers["Cache-Control"] = "no-cache". Can also be used to access the final body HTML after a template - # has been rendered through response.body -- useful for after_filters that wants to manipulate the output, - # such as a OutputCompressionFilter. - attr_internal :response - - # Holds a hash of objects in the session. Accessed like session[:person] to get the object tied to the "person" - # key. The session will hold any type of object as values, but the key should be a string or symbol. - def session - request.session - end - - # Holds a hash of header names and values. Accessed like headers["Cache-Control"] to get the value of the Cache-Control - # directive. Values should always be specified as strings. - attr_internal :headers - - # Returns the name of the action this controller is processing. - attr_accessor :action_name - - attr_reader :template - - def action(name, env) - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - self.action_name = name && name.to_s - process(request, response).to_a - end - - - class << self - def action(name = nil) - @actions ||= {} - @actions[name] ||= proc do |env| - new.action(name, env) - end - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". - def controller_class_name - @controller_class_name ||= name.demodulize - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". - def controller_name - @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". - def controller_path - @controller_path ||= name.gsub(/Controller$/, '').underscore - end - - # Return an array containing the names of public methods that have been marked hidden from the action processor. - # By default, all methods defined in ActionController::Base and included modules are hidden. - # More methods can be hidden using hide_action. - def hidden_actions - read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, []) - end - - # Hide each of the given methods from being callable as actions. - def hide_action(*names) - write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s }) - end - - # View load paths determine the bases from which template references can be made. So a call to - # render("test/template") will be looked up in the view load paths array and the closest match will be - # returned. - def view_paths - if defined? @view_paths - @view_paths - else - superclass.view_paths - end - end - - def view_paths=(value) - @view_paths = ActionView::Base.process_view_paths(value) if value - end - - # Adds a view_path to the front of the view_paths array. - # If the current class has no view paths, copy them from - # the superclass. This change will be visible for all future requests. - # - # ArticleController.prepend_view_path("views/default") - # ArticleController.prepend_view_path(["views/default", "views/custom"]) - # - def prepend_view_path(path) - @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil? - @view_paths.unshift(*path) - end - - # Adds a view_path to the end of the view_paths array. - # If the current class has no view paths, copy them from - # the superclass. This change will be visible for all future requests. - # - # ArticleController.append_view_path("views/default") - # ArticleController.append_view_path(["views/default", "views/custom"]) - # - def append_view_path(path) - @view_paths = superclass.view_paths.dup if @view_paths.nil? - @view_paths.push(*path) - end - - @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] - - def exempt_from_layout(*types) - types.each do |type| - @@exempt_from_layout << - ActionView::Template.handler_class_for_extension(type) - end - - @@exempt_from_layout - end - - end - - public - def call(env) - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - process(request, response).to_a - end - - # Extracts the action_name from the request parameters and performs that action. - def process(request, response, method = :perform_action, *arguments) #:nodoc: - response.request = request - - assign_shortcuts(request, response) - initialize_template_class(response) - initialize_current_url - - log_processing - send(method, *arguments) - - send_response - ensure - process_cleanup - end - - def send_response - response.prepare! - response - end - - # Returns a URL that has been rewritten according to the options hash and the defined routes. - # (For doing a complete redirect, use +redirect_to+). - # - # url_for is used to: - # - # All keys given to +url_for+ are forwarded to the Route module, save for the following: - # * :anchor - Specifies the anchor name to be appended to the path. For example, - # url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments' - # will produce "/posts/show/10#comments". - # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (false by default). - # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this - # is currently not recommended since it breaks caching. - # * :host - Overrides the default (current) host if provided. - # * :protocol - Overrides the default (current) protocol if provided. - # * :port - Optionally specify the port to connect to. - # * :user - Inline HTTP authentication (only plucked out if :password is also present). - # * :password - Inline HTTP authentication (only plucked out if :user is also present). - # * :skip_relative_url_root - If true, the url is not constructed using the +relative_url_root+ - # of the request so the path will include the web server relative installation directory. - # - # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the and a query string. - # Routes composes a query string as the key/value pairs not included in the . - # - # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with - # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs: - # - # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent' - # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts' - # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts' - # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10' - # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts' - # - # When generating a new URL, missing values may be filled in from the current request's parameters. For example, - # url_for :action => 'some_action' will retain the current controller, as expected. This behavior extends to - # other parameters, including :controller, :id, and any other parameters that are placed into a Route's - # path. - #   - # The URL helpers such as url_for have a limited form of memory: when generating a new URL, they can look for - # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be - # taken from the defaults. There are a few simple rules on how this is performed: - # - # * If the controller name begins with a slash no defaults are used: - # - # url_for :controller => '/home' - # - # In particular, a leading slash ensures no namespace is assumed. Thus, - # while url_for :controller => 'users' may resolve to - # Admin::UsersController if the current controller lives under - # that module, url_for :controller => '/users' ensures you link - # to ::UsersController no matter what. - # * If the controller changes, the action will default to index unless provided - # - # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the - # route given by map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'. - # - # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated - # from this page. - # - # * url_for :action => 'bio' -- During the generation of this URL, default values will be used for the first and - # last components, and the action shall change. The generated URL will be, "people/hh/david/bio". - # * url_for :first => 'davids-little-brother' This generates the URL 'people/hh/davids-little-brother' -- note - # that this URL leaves out the assumed action of 'bio'. - # - # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The - # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the - # value that appears in the slot for :first is not equal to default value for :first we stop using - # defaults. On its own, this rule can account for much of the typical Rails URL behavior. - #   - # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired. - # The default may be cleared by adding :name => nil to url_for's options. - # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the - # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is - # displayed on: - # - # url_for :controller => 'posts', :action => nil - # - # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the - # :overwrite_params options. Say for your posts you have different views for showing and printing them. - # Then, in the show view, you get the URL for the print view like this - # - # url_for :overwrite_params => { :action => 'print' } - # - # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print' - # would have slashed-off the path components after the changed action. - def url_for(options = {}) - options ||= {} - case options - when String - options - when Hash - @url.rewrite(rewrite_options(options)) - else - polymorphic_url(options) - end - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". - def controller_class_name - self.class.controller_class_name - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat". - def controller_name - self.class.controller_name - end - - # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat". - def controller_path - self.class.controller_path - end - - def session_enabled? - ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller) - end - - self.view_paths = [] - - # View load paths for controller. - def view_paths - @template.view_paths - end - - def view_paths=(value) - @template.view_paths = ActionView::Base.process_view_paths(value) - end - - # Adds a view_path to the front of the view_paths array. - # This change affects the current request only. - # - # self.prepend_view_path("views/default") - # self.prepend_view_path(["views/default", "views/custom"]) - # - def prepend_view_path(path) - @template.view_paths.unshift(*path) - end - - # Adds a view_path to the end of the view_paths array. - # This change affects the current request only. - # - # self.append_view_path("views/default") - # self.append_view_path(["views/default", "views/custom"]) - # - def append_view_path(path) - @template.view_paths.push(*path) - end - - def rewrite_options(options) #:nodoc: - if defaults = default_url_options(options) - defaults.merge(options) - else - options - end - end - - # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in - # the form of a hash, just like the one you would use for url_for directly. Example: - # - # def default_url_options(options) - # { :project => @project.active? ? @project.url_name : "unknown" } - # end - # - # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the - # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set - # by this method. - def default_url_options(options = nil) - 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, - # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # - # if stale?(:etag => @article, :last_modified => @article.created_at.utc) - # @statistics = @article.really_expensive_call - # respond_to do |format| - # # all the supported formats - # end - # end - # end - def stale?(options) - fresh_when(options) - !request.fresh?(response) - end - - # Sets the etag, last_modified, or both on the response and renders a - # "304 Not Modified" response if the request is already fresh. - # - # Parameters: - # * :etag - # * :last_modified - # * :public By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). - # - # Example: - # - # def show - # @article = Article.find(params[:id]) - # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) - # end - # - # This will render the show template if the request isn't sending a matching etag or - # If-Modified-Since header and just a "304 Not Modified" response if there's a match. - # - def fresh_when(options) - options.assert_valid_keys(:etag, :last_modified, :public) - - response.etag = options[:etag] if options[:etag] - response.last_modified = options[:last_modified] if options[:last_modified] - - if options[:public] - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - cache_control.delete("private") - cache_control.delete("no-cache") - cache_control << "public" - response.headers["Cache-Control"] = cache_control.join(', ') - end - - if request.fresh?(response) - head :not_modified - end - end - - # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that - # intermediate caches shouldn't cache the response. - # - # Examples: - # expires_in 20.minutes - # expires_in 3.hours, :public => true - # expires in 3.hours, 'max-stale' => 5.hours, :public => true - # - # This method will overwrite an existing Cache-Control header. - # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. - def expires_in(seconds, options = {}) #:doc: - cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } - - cache_control << "max-age=#{seconds}" - cache_control.delete("no-cache") - if options[:public] - cache_control.delete("private") - cache_control << "public" - else - cache_control << "private" - end - - # This allows for additional headers to be passed through like 'max-stale' => 5.hours - cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} - - response.headers["Cache-Control"] = cache_control.join(', ') - end - - # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or - # intermediate caches (like caching proxy servers). - def expires_now #:doc: - response.headers["Cache-Control"] = "no-cache" - end - - # Resets the session by clearing out all the objects stored within and initializing a new session object. - def reset_session #:doc: - request.reset_session - end - - private - def _process_options(options) - 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 - - response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE) - end - - def initialize_template_class(response) - @template = ActionView::Base.new(self.class.view_paths, {}, self, formats) - response.template = @template if response.respond_to?(:template=) - @template.helpers.send :include, self.class.master_helper_module - @performed_render = @performed_redirect = false - end - - def assign_shortcuts(request, response) - @_request, @_response, @_params = request, response, request.parameters - @_headers = @_response.headers - end - - def initialize_current_url - @url = UrlRewriter.new(request, params.clone) - end - - def log_processing - if logger && logger.info? - log_processing_for_request_id - log_processing_for_parameters - end - end - - def log_processing_for_request_id - request_id = "\n\nProcessing #{self.class.name}\##{action_name} " - request_id << "to #{params[:format]} " if params[:format] - request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]" - - logger.info(request_id) - end - - def default_render #:nodoc: - render - end - - def perform_action - 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 - view_paths.find_by_parts(action_name, {:formats => formats, :locales => [I18n.locale]}, controller_path) - rescue => e - raise UnknownAction, "No action responded to #{action_name}. Actions: " + - "#{action_methods.sort.to_sentence}", caller - end - - default_render - end - - # Returns true if a render or redirect has already been performed. - def performed? - @performed_render || @performed_redirect - end - - def reset_variables_added_to_assigns - @template.instance_variable_set("@assigns_added", nil) - end - - def request_origin - # this *needs* to be cached! - # otherwise you'd get different results if calling it more than once - @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" - end - - # Returns the request URI used to get to the current location - def complete_request_uri - "#{request.protocol}#{request.host}#{request.request_uri}" - end - - def default_template(action_name = self.action_name) - self.view_paths.find_template(default_template_name(action_name), default_template_format) - end - - def default_template_name(action_name = self.action_name) - if action_name - action_name = action_name.to_s - if action_name.include?('/') && template_path_includes_controller?(action_name) - action_name = strip_out_controller(action_name) - end - end - "#{controller_path}/#{action_name}" - end - - def strip_out_controller(path) - path.split('/', 2).last - end - - def template_path_includes_controller?(path) - self.controller_path.split('/')[-1] == path.split('/')[0] - end - - def process_cleanup - end - end - - Base.class_eval do - [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers, - Cookies, Caching, Verification, Streaming, SessionManagement, - HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier, - RequestForgeryProtection, Translation, FilterParameterLogging - ].each do |mod| - include mod - end - end -end diff --git a/actionpack/lib/action_controller/old_base/chained/benchmarking.rb b/actionpack/lib/action_controller/old_base/chained/benchmarking.rb deleted file mode 100644 index 57a1ac8314..0000000000 --- a/actionpack/lib/action_controller/old_base/chained/benchmarking.rb +++ /dev/null @@ -1,107 +0,0 @@ -require 'active_support/core_ext/benchmark' - -module ActionController #:nodoc: - # The benchmarking module times the performance of actions and reports to the logger. If the Active Record - # package has been included, a separate timing section for database calls will be added as well. - module Benchmarking #:nodoc: - def self.included(base) - base.extend(ClassMethods) - - base.class_eval do - alias_method_chain :perform_action, :benchmark - alias_method_chain :render, :benchmark - end - end - - module ClassMethods - # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it - # (unless use_silence is set to false). - # - # The benchmark is only recorded if the current level of the logger matches the log_level, which makes it - # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark - # will only be conducted if the log level is low enough. - def benchmark(title, log_level = Logger::DEBUG, use_silence = true) - if logger && logger.level >= log_level - result = nil - ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } - logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)") - result - else - yield - end - end - - # Silences the logger for the duration of the block. - def silence - old_logger_level, logger.level = logger.level, Logger::ERROR if logger - yield - ensure - logger.level = old_logger_level if logger - end - end - - protected - def render_with_benchmark(options = nil, extra_options = {}, &block) - if logger - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - db_runtime = ActiveRecord::Base.connection.reset_runtime - end - - render_output = nil - @view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) } - - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - @db_rt_before_render = db_runtime - @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime - @view_runtime -= @db_rt_after_render - end - - render_output - else - render_without_benchmark(options, extra_options, &block) - end - end - - private - def perform_action_with_benchmark - 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? - - log_message = 'Completed in %.0fms' % ms - - if logging_view || logging_active_record - log_message << " (" - log_message << view_runtime if logging_view - - if logging_active_record - log_message << ", " if logging_view - log_message << active_record_runtime + ")" - else - ")" - end - end - - log_message << " | #{response.status}" - log_message << " [#{complete_request_uri rescue "unknown"}]" - - logger.info(log_message) - response.headers["X-Runtime"] = "%.0f" % ms - else - perform_action_without_benchmark - end - end - - def view_runtime - "View: %.0f" % @view_runtime - end - - def active_record_runtime - db_runtime = ActiveRecord::Base.connection.reset_runtime - db_runtime += @db_rt_before_render if @db_rt_before_render - db_runtime += @db_rt_after_render if @db_rt_after_render - "DB: %.0f" % db_runtime - end - end -end diff --git a/actionpack/lib/action_controller/old_base/chained/filters.rb b/actionpack/lib/action_controller/old_base/chained/filters.rb deleted file mode 100644 index f528dd0686..0000000000 --- a/actionpack/lib/action_controller/old_base/chained/filters.rb +++ /dev/null @@ -1,670 +0,0 @@ -module ActionController #:nodoc: - module Filters #:nodoc: - extend ActiveSupport::Concern - - class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc: - def append_filter_to_chain(filters, filter_type, &block) - pos = find_filter_append_position(filters, filter_type) - update_filter_chain(filters, filter_type, pos, &block) - end - - def prepend_filter_to_chain(filters, filter_type, &block) - pos = find_filter_prepend_position(filters, filter_type) - update_filter_chain(filters, filter_type, pos, &block) - end - - def create_filters(filters, filter_type, &block) - filters, conditions = extract_options(filters, &block) - filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) } - filters - end - - def skip_filter_in_chain(*filters, &test) - filters, conditions = extract_options(filters) - filters.each do |filter| - if callback = find(filter) then delete(callback) end - end if conditions.empty? - update_filter_in_chain(filters, :skip => conditions, &test) - end - - private - def update_filter_chain(filters, filter_type, pos, &block) - new_filters = create_filters(filters, filter_type, &block) - insert(pos, new_filters).flatten! - end - - def find_filter_append_position(filters, filter_type) - # appending an after filter puts it at the end of the call chain - # before and around filters go before the first after filter in the chain - unless filter_type == :after - each_with_index do |f,i| - return i if f.after? - end - end - return -1 - end - - def find_filter_prepend_position(filters, filter_type) - # prepending a before or around filter puts it at the front of the call chain - # after filters go before the first after filter in the chain - if filter_type == :after - each_with_index do |f,i| - return i if f.after? - end - return -1 - end - return 0 - end - - def find_or_create_filter(filter, filter_type, options = {}) - update_filter_in_chain([filter], options) - - if found_filter = find(filter) { |f| f.type == filter_type } - found_filter - else - filter_kind = case - when filter.respond_to?(:before) && filter_type == :before - :before - when filter.respond_to?(:after) && filter_type == :after - :after - else - :filter - end - - case filter_type - when :before - BeforeFilter.new(filter_kind, filter, options) - when :after - AfterFilter.new(filter_kind, filter, options) - else - AroundFilter.new(filter_kind, filter, options) - end - end - end - - def update_filter_in_chain(filters, options, &test) - filters.map! { |f| block_given? ? find(f, &test) : find(f) } - filters.compact! - - map! do |filter| - if filters.include?(filter) - new_filter = filter.dup - new_filter.update_options!(options) - new_filter - else - filter - end - end - end - end - - class Filter < ActiveSupport::Callbacks::Callback #:nodoc: - def initialize(kind, method, options = {}) - super - update_options! options - end - - # override these to return true in appropriate subclass - def before? - false - end - - def after? - false - end - - def around? - false - end - - # Make sets of strings from :only/:except options - def update_options!(other) - if other - convert_only_and_except_options_to_sets_of_strings(other) - if other[:skip] - convert_only_and_except_options_to_sets_of_strings(other[:skip]) - end - end - - options.update(other) - end - - private - def should_not_skip?(controller) - if options[:skip] - !included_in_action?(controller, options[:skip]) - else - true - end - end - - def included_in_action?(controller, options) - if options[:only] - options[:only].include?(controller.action_name) - elsif options[:except] - !options[:except].include?(controller.action_name) - else - true - end - end - - def should_run_callback?(controller) - should_not_skip?(controller) && included_in_action?(controller, options) && super - end - - def convert_only_and_except_options_to_sets_of_strings(opts) - [:only, :except].each do |key| - if values = opts[key] - opts[key] = Array(values).map {|val| val.to_s }.to_set - end - end - end - end - - class AroundFilter < Filter #:nodoc: - def type - :around - end - - def around? - true - end - - def call(controller, &block) - if should_run_callback?(controller) - method = filter_responds_to_before_and_after? ? around_proc : self.method - - # For around_filter do |controller, action| - if method.is_a?(Proc) && method.arity == 2 - evaluate_method(method, controller, block) - else - evaluate_method(method, controller, &block) - end - else - block.call - end - end - - private - def filter_responds_to_before_and_after? - method.respond_to?(:before) && method.respond_to?(:after) - end - - def around_proc - Proc.new do |controller, action| - method.before(controller) - - if controller.__send__(:performed?) - controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) - else - begin - action.call - ensure - method.after(controller) - end - end - end - end - end - - class BeforeFilter < Filter #:nodoc: - def type - :before - end - - def before? - true - end - - def call(controller, &block) - super - if controller.__send__(:performed?) - controller.__send__(:halt_filter_chain, method, :rendered_or_redirected) - end - end - end - - class AfterFilter < Filter #:nodoc: - def type - :after - end - - def after? - true - end - end - - # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do - # authentication, caching, or auditing before the intended action is performed. Or to do localization or output - # compression after the action has been performed. Filters have access to the request, response, and all the instance - # variables set by other filters in the chain or by the action (in the case of after filters). - # - # == Filter inheritance - # - # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without - # affecting the superclass. For example: - # - # class BankController < ActionController::Base - # before_filter :audit - # - # private - # def audit - # # record the action and parameters in an audit log - # end - # end - # - # class VaultController < BankController - # before_filter :verify_credentials - # - # private - # def verify_credentials - # # make sure the user is allowed into the vault - # end - # end - # - # Now any actions performed on the BankController will have the audit method called before. On the VaultController, - # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then - # verify_credentials and the intended action are never called. - # - # == Filter types - # - # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first - # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of - # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form. - # - # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes - # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example: - # - # class OutputCompressionFilter - # def self.filter(controller) - # controller.response.body = compress(controller.response.body) - # end - # end - # - # class NewspaperController < ActionController::Base - # after_filter OutputCompressionFilter - # end - # - # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can - # manipulate them as it sees fit. - # - # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation. - # Or just as a quick test. It works like this: - # - # class WeblogController < ActionController::Base - # before_filter { |controller| head(400) if controller.params["stop_action"] } - # end - # - # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables. - # This means that the block has access to both the request and response objects complete with convenience methods for params, - # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call - # and returns 1 or -1 on arity will do (such as a Proc or an Method object). - # - # Please note that around_filters function a little differently than the normal before and after filters with regard to filter - # types. Please see the section dedicated to around_filters below. - # - # == Filter chain ordering - # - # Using before_filter and after_filter appends the specified filters to the existing chain. That's usually - # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you - # can use prepend_before_filter and prepend_after_filter. Filters added by these methods will be put at the - # beginning of their respective chain and executed before the rest. For example: - # - # class ShoppingController < ActionController::Base - # before_filter :verify_open_shop - # - # class CheckoutController < ShoppingController - # prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock - # - # The filter chain for the CheckoutController is now :ensure_items_in_cart, :ensure_items_in_stock, - # :verify_open_shop. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop - # is open or not. - # - # You may pass multiple filter arguments of each type as well as a filter block. - # If a block is given, it is treated as the last argument. - # - # == Around filters - # - # Around filters wrap an action, executing code both before and after. - # They may be declared as method references, blocks, or objects responding - # to +filter+ or to both +before+ and +after+. - # - # To use a method as an +around_filter+, pass a symbol naming the Ruby method. - # Yield (or block.call) within the method to run the action. - # - # around_filter :catch_exceptions - # - # private - # def catch_exceptions - # yield - # rescue => exception - # logger.debug "Caught exception! #{exception}" - # raise - # end - # - # To use a block as an +around_filter+, pass a block taking as args both - # the controller and the action block. You can't call yield directly from - # an +around_filter+ block; explicitly call the action block instead: - # - # around_filter do |controller, action| - # logger.debug "before #{controller.action_name}" - # action.call - # logger.debug "after #{controller.action_name}" - # end - # - # To use a filter object with +around_filter+, pass an object responding - # to :filter or both :before and :after. With a - # filter method, yield to the block as above: - # - # around_filter BenchmarkingFilter - # - # class BenchmarkingFilter - # def self.filter(controller, &block) - # Benchmark.measure(&block) - # end - # end - # - # With +before+ and +after+ methods: - # - # around_filter Authorizer.new - # - # class Authorizer - # # This will run before the action. Redirecting aborts the action. - # def before(controller) - # unless user.authorized? - # redirect_to(login_url) - # end - # end - # - # # This will run after the action if and only if before did not render or redirect. - # def after(controller) - # end - # end - # - # If the filter has +before+ and +after+ methods, the +before+ method will be - # called before the action. If +before+ renders or redirects, the filter chain is - # halted and +after+ will not be run. See Filter Chain Halting below for - # an example. - # - # == Filter chain skipping - # - # Declaring a filter on a base class conveniently applies to its subclasses, - # but sometimes a subclass should skip some of its superclass' filters: - # - # class ApplicationController < ActionController::Base - # before_filter :authenticate - # around_filter :catch_exceptions - # end - # - # class WeblogController < ApplicationController - # # Will run the :authenticate and :catch_exceptions filters. - # end - # - # class SignupController < ApplicationController - # # Skip :authenticate, run :catch_exceptions. - # skip_before_filter :authenticate - # end - # - # class ProjectsController < ApplicationController - # # Skip :catch_exceptions, run :authenticate. - # skip_filter :catch_exceptions - # end - # - # class ClientsController < ApplicationController - # # Skip :catch_exceptions and :authenticate unless action is index. - # skip_filter :catch_exceptions, :authenticate, :except => :index - # end - # - # == Filter conditions - # - # Filters may be limited to specific actions by declaring the actions to - # include or exclude. Both options accept single actions - # (:only => :index) or arrays of actions - # (:except => [:foo, :bar]). - # - # class Journal < ActionController::Base - # # Require authentication for edit and delete. - # before_filter :authorize, :only => [:edit, :delete] - # - # # Passing options to a filter with a block. - # around_filter(:except => :index) do |controller, action_block| - # results = Profiler.run(&action_block) - # controller.response.sub! "", "#{results}" - # end - # - # private - # def authorize - # # Redirect to login unless authenticated. - # end - # end - # - # == Filter Chain Halting - # - # before_filter and around_filter may halt the request - # before a controller action is run. This is useful, for example, to deny - # access to unauthenticated users or to redirect from HTTP to HTTPS. - # Simply call render or redirect. After filters will not be executed if the filter - # chain is halted. - # - # Around filters halt the request unless the action block is called. - # Given these filters - # after_filter :after - # around_filter :around - # before_filter :before - # - # The filter chain will look like: - # - # ... - # . \ - # . #around (code before yield) - # . . \ - # . . #before (actual filter code is run) - # . . . \ - # . . . execute controller action - # . . . / - # . . ... - # . . / - # . #around (code after yield) - # . / - # #after (actual filter code is run, unless the around filter does not yield) - # - # If +around+ returns before yielding, +after+ will still not be run. The +before+ - # filter and controller action will not be run. If +before+ renders or redirects, - # the second half of +around+ and will still run but +after+ and the - # action will not. If +around+ fails to yield, +after+ will not be run. - module ClassMethods - # The passed filters will be appended to the filter_chain and - # will execute before the action on this controller is performed. - def append_before_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :before, &block) - end - - # The passed filters will be prepended to the filter_chain and - # will execute before the action on this controller is performed. - def prepend_before_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :before, &block) - end - - # Shorthand for append_before_filter since it's the most common. - alias :before_filter :append_before_filter - - # The passed filters will be appended to the array of filters - # that run _after_ actions on this controller are performed. - def append_after_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :after, &block) - end - - # The passed filters will be prepended to the array of filters - # that run _after_ actions on this controller are performed. - def prepend_after_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :after, &block) - end - - # Shorthand for append_after_filter since it's the most common. - alias :after_filter :append_after_filter - - # If you append_around_filter A.new, B.new, the filter chain looks like - # - # B#before - # A#before - # # run the action - # A#after - # B#after - # - # With around filters which yield to the action block, +before+ and +after+ - # are the code before and after the yield. - def append_around_filter(*filters, &block) - filter_chain.append_filter_to_chain(filters, :around, &block) - end - - # If you prepend_around_filter A.new, B.new, the filter chain looks like: - # - # A#before - # B#before - # # run the action - # B#after - # A#after - # - # With around filters which yield to the action block, +before+ and +after+ - # are the code before and after the yield. - def prepend_around_filter(*filters, &block) - filter_chain.prepend_filter_to_chain(filters, :around, &block) - end - - # Shorthand for +append_around_filter+ since it's the most common. - alias :around_filter :append_around_filter - - # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference - # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out - # of many sub-controllers need a different hierarchy. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_before_filter(*filters) - filter_chain.skip_filter_in_chain(*filters, &:before?) - end - - # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference - # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out - # of many sub-controllers need a different hierarchy. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_after_filter(*filters) - filter_chain.skip_filter_in_chain(*filters, &:after?) - end - - # Removes the specified filters from the filter chain. This only works for method reference (symbol) - # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that - # it will match any before, after or yielding around filter. - # - # You can control the actions to skip the filter for with the :only and :except options, - # just like when you apply the filters. - def skip_filter(*filters) - filter_chain.skip_filter_in_chain(*filters) - end - - # Returns an array of Filter objects for this controller. - def filter_chain - read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new) - end - - # Returns all the before filters for this class and all its ancestors. - # This method returns the actual filter that was assigned in the controller to maintain existing functionality. - def before_filters #:nodoc: - filter_chain.select(&:before?).map(&:method) - end - - # Returns all the after filters for this class and all its ancestors. - # This method returns the actual filter that was assigned in the controller to maintain existing functionality. - def after_filters #:nodoc: - filter_chain.select(&:after?).map(&:method) - end - end - - module InstanceMethods # :nodoc: - def self.included(base) - base.class_eval do - alias_method_chain :perform_action, :filters - alias_method_chain :process, :filters - end - end - - protected - def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc: - @before_filter_chain_aborted = false - process_without_filters(request, response, method, *arguments) - end - - def perform_action_with_filters - call_filters(self.class.filter_chain, 0, 0) - end - - private - def call_filters(chain, index, nesting) - index = run_before_filters(chain, index, nesting) - aborted = @before_filter_chain_aborted - perform_action_without_filters unless performed? || aborted - return index if nesting != 0 || aborted - run_after_filters(chain, index) - end - - def run_before_filters(chain, index, nesting) - while chain[index] - filter, index = chain[index], index - break unless filter # end of call chain reached - - case filter - when BeforeFilter - filter.call(self) # invoke before filter - index = index.next - break if @before_filter_chain_aborted - when AroundFilter - yielded = false - - filter.call(self) do - yielded = true - # all remaining before and around filters will be run in this call - index = call_filters(chain, index.next, nesting.next) - end - - halt_filter_chain(filter, :did_not_yield) unless yielded - - break - else - break # no before or around filters left - end - end - - index - end - - def run_after_filters(chain, index) - seen_after_filter = false - - while chain[index] - filter, index = chain[index], index - break unless filter # end of call chain reached - - case filter - when AfterFilter - seen_after_filter = true - filter.call(self) # invoke after filter - else - # implementation error or someone has mucked with the filter chain - raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter - end - - index = index.next - end - - index.next - end - - def halt_filter_chain(filter, reason) - @before_filter_chain_aborted = true - logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger - end - end - end -end diff --git a/actionpack/lib/action_controller/old_base/chained/flash.rb b/actionpack/lib/action_controller/old_base/chained/flash.rb deleted file mode 100644 index 42c6e430ca..0000000000 --- a/actionpack/lib/action_controller/old_base/chained/flash.rb +++ /dev/null @@ -1,196 +0,0 @@ -module ActionController #:nodoc: - # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed - # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create - # action that sets flash[:notice] = "Successfully created" before redirecting to a display action that can - # then expose the flash to its template. Actually, that exposure is automatically done. Example: - # - # class PostsController < ActionController::Base - # def create - # # save post - # flash[:notice] = "Successfully created post" - # redirect_to posts_path(@post) - # end - # - # def show - # # doesn't need to assign the flash notice to the template, that's done automatically - # end - # end - # - # show.html.erb - # <% if flash[:notice] %> - #
<%= flash[:notice] %>
- # <% end %> - # - # This example just places a string in the flash, but you can put any object in there. And of course, you can put as - # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed. - # - # See docs on the FlashHash class for more details about the flash. - module Flash - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - include Session if defined?(ActionController::Http) - - included do - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include InstanceMethodsForNewBase - else - include InstanceMethodsForBase - - alias_method_chain :perform_action, :flash - alias_method_chain :reset_session, :flash - end - end - - class FlashNow #:nodoc: - def initialize(flash) - @flash = flash - end - - def []=(k, v) - @flash[k] = v - @flash.discard(k) - v - end - - def [](k) - @flash[k] - end - end - - class FlashHash < Hash - def initialize #:nodoc: - super - @used = {} - end - - def []=(k, v) #:nodoc: - keep(k) - super - end - - def update(h) #:nodoc: - h.keys.each { |k| keep(k) } - super - end - - alias :merge! :update - - def replace(h) #:nodoc: - @used = {} - super - end - - # Sets a flash that will not be available to the next action, only to the current. - # - # flash.now[:message] = "Hello current action" - # - # This method enables you to use the flash as a central messaging system in your app. - # When you need to pass an object to the next action, you use the standard flash assign ([]=). - # When you need to pass an object to the current action, you use now, and your object will - # vanish when the current action is done. - # - # Entries set via now are accessed the same way as standard entries: flash['my-key']. - def now - FlashNow.new(self) - end - - # Keeps either the entire current flash or a specific flash entry available for the next action: - # - # flash.keep # keeps the entire flash - # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded - def keep(k = nil) - use(k, false) - end - - # Marks the entire flash or a single flash entry to be discarded by the end of the current action: - # - # flash.discard # discard the entire flash at the end of the current action - # flash.discard(:warning) # discard only the "warning" entry at the end of the current action - def discard(k = nil) - use(k) - end - - # Mark for removal entries that were kept, and delete unkept ones. - # - # This method is called automatically by filters, so you generally don't need to care about it. - def sweep #:nodoc: - keys.each do |k| - unless @used[k] - use(k) - else - delete(k) - @used.delete(k) - end - end - - # clean up after keys that could have been left over by calling reject! or shift on the flash - (@used.keys - keys).each{ |k| @used.delete(k) } - end - - def store(session, key = "flash") - return if self.empty? - session[key] = self - end - - private - # Used internally by the keep and discard methods - # use() # marks the entire flash as used - # use('msg') # marks the "msg" entry as used - # use(nil, false) # marks the entire flash as unused (keeps it around for one more action) - # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action) - # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself - # if no key is passed. - def use(key = nil, used = true) - Array(key || keys).each { |k| @used[k] = used } - return key ? self[key] : self - end - end - - module InstanceMethodsForBase #:nodoc: - protected - def perform_action_with_flash - perform_action_without_flash - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session_with_flash - reset_session_without_flash - remove_instance_variable(:@_flash) if defined?(@_flash) - end - end - - module InstanceMethodsForNewBase #:nodoc: - protected - def process_action(method_name) - super - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session - super - remove_instance_variable(:@_flash) if defined?(@_flash) - end - end - - protected - # Access the contents of the flash. Use flash["notice"] to - # read a notice you put there or flash["notice"] = "hello" - # to put a new one. - def flash #:doc: - if !defined?(@_flash) - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end - - @_flash - end - end -end diff --git a/actionpack/lib/action_controller/old_base/cookies.rb b/actionpack/lib/action_controller/old_base/cookies.rb deleted file mode 100644 index d4806623c3..0000000000 --- a/actionpack/lib/action_controller/old_base/cookies.rb +++ /dev/null @@ -1,94 +0,0 @@ -module ActionController #:nodoc: - # Cookies are read and written through ActionController#cookies. - # - # The cookies being read are the ones received along with the request, the cookies - # being written will be sent out with the response. Reading a cookie does not get - # the cookie object itself back, just the value it holds. - # - # Examples for writing: - # - # # Sets a simple session cookie. - # cookies[:user_name] = "david" - # - # # Sets a cookie that expires in 1 hour. - # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now } - # - # Examples for reading: - # - # cookies[:user_name] # => "david" - # cookies.size # => 2 - # - # Example for deleting: - # - # cookies.delete :user_name - # - # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie: - # - # cookies[:key] = { - # :value => 'a yummy cookie', - # :expires => 1.year.from_now, - # :domain => 'domain.com' - # } - # - # cookies.delete(:key, :domain => 'domain.com') - # - # The option symbols for setting cookies are: - # - # * :value - The cookie's value or list of values (as an array). - # * :path - The path for which this cookie applies. Defaults to the root - # of the application. - # * :domain - The domain for which this cookie applies. - # * :expires - The time at which this cookie expires, as a Time object. - # * :secure - Whether this cookie is a only transmitted to HTTPS servers. - # Default is +false+. - # * :httponly - Whether this cookie is accessible via scripting or - # only HTTP. Defaults to +false+. - module Cookies - def self.included(base) - base.helper_method :cookies - end - - protected - # Returns the cookie container, which operates as described above. - def cookies - @cookies ||= CookieJar.new(self) - end - end - - class CookieJar < Hash #:nodoc: - def initialize(controller) - @controller, @cookies = controller, controller.request.cookies - super() - update(@cookies) - end - - # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. - def [](name) - super(name.to_s) - end - - # Sets the cookie named +name+. The second argument may be the very cookie - # value, or a hash of options as documented above. - def []=(key, options) - if options.is_a?(Hash) - options.symbolize_keys! - else - options = { :value => options } - end - - options[:path] = "/" unless options.has_key?(:path) - super(key.to_s, options[:value]) - @controller.response.set_cookie(key, options) - end - - # Removes the cookie on the client machine by setting the value to an empty string - # and setting its expiration date into the past. Like []=, you can pass in - # an options hash to delete cookies with extra data such as a :path. - def delete(key, options = {}) - options.symbolize_keys! - options[:path] = "/" unless options.has_key?(:path) - super(key.to_s) - @controller.response.delete_cookie(key, options) - end - end -end diff --git a/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb b/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb deleted file mode 100644 index 8370ba6fc0..0000000000 --- a/actionpack/lib/action_controller/old_base/filter_parameter_logging.rb +++ /dev/null @@ -1,95 +0,0 @@ -module ActionController - module FilterParameterLogging - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Logger - end - - included do - include InstanceMethodsForNewBase - end - - module ClassMethods - # Replace sensitive parameter data from the request log. - # Filters parameters that have any of the arguments as a substring. - # Looks in all subhashes of the param hash for keys to filter. - # If a block is given, each key and value of the parameter hash and all - # subhashes is passed to it, the value or key - # can be replaced using String#replace or similar method. - # - # Examples: - # filter_parameter_logging - # => Does nothing, just slows the logging process down - # - # filter_parameter_logging :password - # => replaces the value to all keys matching /password/i with "[FILTERED]" - # - # filter_parameter_logging :foo, "bar" - # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - # - # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i - # - # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i, and - # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - def filter_parameter_logging(*filter_words, &block) - parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 - - define_method(:filter_parameters) do |unfiltered_parameters| - filtered_parameters = {} - - unfiltered_parameters.each do |key, value| - if key =~ parameter_filter - filtered_parameters[key] = '[FILTERED]' - elsif value.is_a?(Hash) - filtered_parameters[key] = filter_parameters(value) - elsif block_given? - key = key.dup - value = value.dup if value - yield key, value - filtered_parameters[key] = value - else - filtered_parameters[key] = value - end - end - - filtered_parameters - end - protected :filter_parameters - end - end - - module InstanceMethodsForNewBase - # TODO : Fix the order of information inside such that it's exactly same as the old base - def process(*) - ret = super - - if logger - parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup - parameters = parameters.except!(:controller, :action, :format, :_method, :only_path) - - unless parameters.empty? - # TODO : Move DelayedLog to AS - log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" } - logger.info(log) - end - end - - ret - end - end - - private - - # TODO : This method is not needed for the new base - def log_processing_for_parameters - parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup - parameters = parameters.except!(:controller, :action, :format, :_method) - - logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? - end - end -end diff --git a/actionpack/lib/action_controller/old_base/helpers.rb b/actionpack/lib/action_controller/old_base/helpers.rb deleted file mode 100644 index f74158bc13..0000000000 --- a/actionpack/lib/action_controller/old_base/helpers.rb +++ /dev/null @@ -1,221 +0,0 @@ -require 'active_support/dependencies' - -# FIXME: helper { ... } is broken on Ruby 1.9 -module ActionController #:nodoc: - module Helpers #:nodoc: - extend ActiveSupport::Concern - - included do - # Initialize the base module to aggregate its helpers. - class_inheritable_accessor :master_helper_module - self.master_helper_module = Module.new - - # Set the default directory for helpers - class_inheritable_accessor :helpers_dir - self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - - class << self - alias_method_chain :inherited, :helper - end - end - - # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, - # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates - # by default. - # - # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to - # extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will - # include a helper whose name matches that of the controller, e.g., MyController will automatically - # include MyHelper. - # - # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any - # controller which inherits from it. - # - # ==== Examples - # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if - # the Time object is blank: - # - # module FormattedTimeHelper - # def format_time(time, format=:long, blank_message=" ") - # time.blank? ? blank_message : time.to_s(format) - # end - # end - # - # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: - # - # class EventsController < ActionController::Base - # helper FormattedTimeHelper - # def index - # @events = Event.find(:all) - # end - # end - # - # Then, in any view rendered by EventController, the format_time method can be called: - # - # <% @events.each do |event| -%> - #

- # <% format_time(event.time, :short, "N/A") %> | <%= event.name %> - #

- # <% end -%> - # - # Finally, assuming we have two event instances, one which has a time and one which does not, - # the output might look like this: - # - # 23 Aug 11:30 | Carolina Railhawks Soccer Match - # N/A | Carolina Railhaws Training Workshop - # - module ClassMethods - # Makes all the (instance) methods in the helper module available to templates rendered through this controller. - # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules - # available to the templates. - def add_template_helper(helper_module) #:nodoc: - master_helper_module.module_eval { include helper_module } - end - - # The +helper+ class method can take a series of helper module names, a block, or both. - # - # * *args: One or more modules, strings or symbols, or the special symbol :all. - # * &block: A block defining helper methods. - # - # ==== Examples - # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file - # and include the module in the template class. The second form illustrates how to include custom helpers - # when working with namespaced controllers, or other cases where the file containing the helper definition is not - # in one of Rails' standard load paths: - # helper :foo # => requires 'foo_helper' and includes FooHelper - # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper - # - # When the argument is a module it will be included directly in the template class. - # helper FooHelper # => includes FooHelper - # - # When the argument is the symbol :all, the controller will include all helpers beneath - # ActionController::Base.helpers_dir (defaults to app/helpers/**/*.rb under RAILS_ROOT). - # helper :all - # - # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available - # to the template. - # # One line - # helper { def hello() "Hello, world!" end } - # # Multi-line - # helper do - # def foo(bar) - # "#{bar} is the very best" - # end - # end - # - # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of - # +symbols+, +strings+, +modules+ and blocks. - # helper(:three, BlindHelper) { def mice() 'mice' end } - # - def helper(*args, &block) - args.flatten.each do |arg| - case arg - when Module - add_template_helper(arg) - when :all - helper(all_application_helpers) - when String, Symbol - file_name = arg.to_s.underscore + '_helper' - class_name = file_name.camelize - - begin - require_dependency(file_name) - rescue LoadError => load_error - requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] - if requiree == file_name - msg = "Missing helper file helpers/#{file_name}.rb" - raise LoadError.new(msg).copy_blame!(load_error) - else - raise - end - end - - add_template_helper(class_name.constantize) - else - raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})" - end - end - - # Evaluate block in template class if given. - master_helper_module.module_eval(&block) if block_given? - end - - # Declare a controller method as a helper. For example, the following - # makes the +current_user+ controller method available to the view: - # class ApplicationController < ActionController::Base - # helper_method :current_user, :logged_in? - # - # def current_user - # @current_user ||= User.find_by_id(session[:user]) - # end - # - # def logged_in? - # current_user != nil - # end - # end - # - # In a view: - # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> - def helper_method(*methods) - methods.flatten.each do |method| - master_helper_module.module_eval <<-end_eval - def #{method}(*args, &block) # def current_user(*args, &block) - controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block) - end # end - end_eval - end - end - - # Declares helper accessors for controller attributes. For example, the - # following adds new +name+ and name= instance methods to a - # controller and makes them available to the view: - # helper_attr :name - # attr_accessor :name - def helper_attr(*attrs) - attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } - end - - # Provides a proxy to access helpers methods from outside the view. - def helpers - unless @helper_proxy - @helper_proxy = ActionView::Base.new - @helper_proxy.extend master_helper_module - else - @helper_proxy - end - end - - private - def default_helper_module! - unless name.blank? - module_name = name.sub(/Controller$|$/, 'Helper') - module_path = module_name.split('::').map { |m| m.underscore }.join('/') - require_dependency module_path - helper module_name.constantize - end - rescue MissingSourceFile => e - raise unless e.is_missing? module_path - rescue NameError => e - raise unless e.missing_name? module_name - end - - def inherited_with_helper(child) - inherited_without_helper(child) - - begin - child.master_helper_module = Module.new - child.master_helper_module.__send__ :include, master_helper_module - child.__send__ :default_helper_module! - rescue MissingSourceFile => e - raise unless e.is_missing?("helpers/#{child.controller_path}_helper") - end - end - - # Extract helper names from files in app/helpers/**/*.rb - def all_application_helpers - extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ - Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } - end - end - end -end diff --git a/actionpack/lib/action_controller/old_base/http_authentication.rb b/actionpack/lib/action_controller/old_base/http_authentication.rb deleted file mode 100644 index 2519f55269..0000000000 --- a/actionpack/lib/action_controller/old_base/http_authentication.rb +++ /dev/null @@ -1,309 +0,0 @@ -require 'active_support/base64' - -module ActionController - module HttpAuthentication - # Makes it dead easy to do HTTP Basic authentication. - # - # Simple Basic example: - # - # class PostsController < ApplicationController - # USER_NAME, PASSWORD = "dhh", "secret" - # - # before_filter :authenticate, :except => [ :index ] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_basic do |user_name, password| - # user_name == USER_NAME && password == PASSWORD - # end - # end - # end - # - # - # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, - # the regular HTML interface is protected by a session approach: - # - # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate - # - # protected - # def set_account - # @account = Account.find_by_url_name(request.subdomains.first) - # end - # - # def authenticate - # case request.format - # when Mime::XML, Mime::ATOM - # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } - # @current_user = user - # else - # request_http_basic_authentication - # end - # else - # if session_authenticated? - # @current_user = @account.users.find(session[:authenticated][:user_id]) - # else - # redirect_to(login_url) and return false - # end - # end - # end - # end - # - # In your integration tests, you can do something like this: - # - # def test_access_granted_from_xml - # get( - # "/notes/1.xml", nil, - # :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) - # ) - # - # assert_equal 200, status - # end - # - # Simple Digest example: - # - # require 'digest/md5' - # class PostsController < ApplicationController - # REALM = "SuperSecret" - # USERS = {"dhh" => "secret", #plain text password - # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password - # - # before_filter :authenticate, :except => [:index] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_digest(REALM) do |username| - # USERS[username] - # end - # end - # end - # - # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately - # hash to check the user's credentials. Returning +nil+ will cause authentication to fail. - # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If - # the password file or database is compromised, the attacker would be able to use the ha1 hash to - # authenticate as the user at this +realm+, but would not have the user's password to try using at - # other sites. - # - # On shared hosts, Apache sometimes doesn't pass authentication headers to - # FCGI instances. If your environment matches this description and you cannot - # authenticate, try this rule in your Apache setup: - # - # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] - module Basic - extend self - - module ControllerMethods - def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) - authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) - end - - def authenticate_with_http_basic(&login_procedure) - HttpAuthentication::Basic.authenticate(self, &login_procedure) - end - - def request_http_basic_authentication(realm = "Application") - HttpAuthentication::Basic.authentication_request(self, realm) - end - end - - def authenticate(controller, &login_procedure) - unless authorization(controller.request).blank? - login_procedure.call(*user_name_and_password(controller.request)) - end - end - - def user_name_and_password(request) - decode_credentials(request).split(/:/, 2) - end - - def authorization(request) - request.env['HTTP_AUTHORIZATION'] || - request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] || - request.env['REDIRECT_X_HTTP_AUTHORIZATION'] - end - - def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') - end - - def encode_credentials(user_name, password) - "Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}" - end - - def authentication_request(controller, realm) - controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}") - controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized - end - end - - module Digest - extend self - - module ControllerMethods - def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) - authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) - end - - # Authenticate with HTTP Digest, returns true or false - def authenticate_with_http_digest(realm = "Application", &password_procedure) - HttpAuthentication::Digest.authenticate(self, realm, &password_procedure) - end - - # Render output including the HTTP Digest authentication header - def request_http_digest_authentication(realm = "Application", message = nil) - HttpAuthentication::Digest.authentication_request(self, realm, message) - end - end - - # Returns false on a valid response, true otherwise - def authenticate(controller, realm, &password_procedure) - authorization(controller.request) && validate_digest_response(controller.request, realm, &password_procedure) - end - - def authorization(request) - request.env['HTTP_AUTHORIZATION'] || - request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] || - request.env['REDIRECT_X_HTTP_AUTHORIZATION'] - end - - # Returns false unless the request credentials response value matches the expected value. - # First try the password as a ha1 digest password. If this fails, then try it as a plain - # text password. - def validate_digest_response(request, realm, &password_procedure) - credentials = decode_credentials_header(request) - valid_nonce = validate_nonce(request, credentials[:nonce]) - - if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] - password = password_procedure.call(credentials[:username]) - return false unless password - - method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] - - [true, false].any? do |password_is_ha1| - expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) - expected == credentials[:response] - end - end - end - - # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ - # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead - # of a plain-text password. - def expected_response(http_method, uri, credentials, password, password_is_ha1=true) - ha1 = password_is_ha1 ? password : ha1(credentials, password) - ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) - ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) - end - - def ha1(credentials, password) - ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) - end - - def encode_credentials(http_method, credentials, password, password_is_ha1) - credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) - "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') - end - - def decode_credentials_header(request) - decode_credentials(authorization(request)) - end - - def decode_credentials(header) - header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| - key, value = pair.split('=', 2) - hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') - hash - end - end - - def authentication_header(controller, realm) - controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") - end - - def authentication_request(controller, realm, message = nil) - message ||= "HTTP Digest: Access denied.\n" - authentication_header(controller, realm) - controller.__send__ :render, :text => message, :status => :unauthorized - end - - # Uses an MD5 digest based on time to generate a value to be used only once. - # - # A server-specified data string which should be uniquely generated each time a 401 response is made. - # It is recommended that this string be base64 or hexadecimal data. - # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. - # - # The contents of the nonce are implementation dependent. - # The quality of the implementation depends on a good choice. - # A nonce might, for example, be constructed as the base 64 encoding of - # - # => time-stamp H(time-stamp ":" ETag ":" private-key) - # - # where time-stamp is a server-generated time or other non-repeating value, - # ETag is the value of the HTTP ETag header associated with the requested entity, - # and private-key is data known only to the server. - # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and - # reject the request if it did not match the nonce from that header or - # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. - # The inclusion of the ETag prevents a replay request for an updated version of the resource. - # (Note: including the IP address of the client in the nonce would appear to offer the server the ability - # to limit the reuse of the nonce to the same client that originally got it. - # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. - # Also, IP address spoofing is not that hard.) - # - # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to - # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for - # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 - # of this document. - # - # The nonce is opaque to the client. Composed of Time, and hash of Time with secret - # key from the Rails session secret generated upon creation of project. Ensures - # the time cannot be modifed by client. - def nonce(time = Time.now) - t = time.to_i - hashed = [t, secret_key] - digest = ::Digest::MD5.hexdigest(hashed.join(":")) - ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') - end - - # Might want a shorter timeout depending on whether the request - # is a PUT or POST, and if client is browser or web service. - # Can be much shorter if the Stale directive is implemented. This would - # allow a user to use new nonce without prompting user again for their - # username and password. - def validate_nonce(request, value, seconds_to_timeout=5*60) - t = ActiveSupport::Base64.decode64(value).split(":").first.to_i - nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout - end - - # Opaque based on random generation - but changing each request? - def opaque() - ::Digest::MD5.hexdigest(secret_key) - end - - # Set in /initializers/session_store.rb, and loaded even if sessions are not in use. - def secret_key - ActionController::Base.session_options[:secret] - end - - end - end -end diff --git a/actionpack/lib/action_controller/old_base/layout.rb b/actionpack/lib/action_controller/old_base/layout.rb deleted file mode 100644 index 3046e082d9..0000000000 --- a/actionpack/lib/action_controller/old_base/layout.rb +++ /dev/null @@ -1,259 +0,0 @@ -require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/class' -require 'active_support/core_ext/class/delegating_attributes' -require 'active_support/core_ext/class/inheritable_attributes' - -module ActionController #:nodoc: - # MegasuperultraHAX - # plz refactor ActionMailer - class Base - @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] - cattr_accessor :exempt_from_layout - end - - module Layout #:nodoc: - def self.included(base) - base.extend(ClassMethods) - 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 - # repeated setups. The inclusion pattern has pages that look like this: - # - # <%= render "shared/header" %> - # Hello World - # <%= render "shared/footer" %> - # - # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose - # and if you ever want to change the structure of these two includes, you'll have to change all the templates. - # - # With layouts, you can flip it around and have the common structure know where to insert changing content. This means - # that the header and footer are only mentioned in one place, like this: - # - # // The header part of this layout - # <%= yield %> - # // The footer part of this layout - # - # And then you have content pages that look like this: - # - # hello world - # - # At rendering time, the content page is computed and then inserted in the layout, like this: - # - # // The header part of this layout - # hello world - # // The footer part of this layout - # - # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance - # variable. The preferred notation now is to use yield, as documented above. - # - # == Accessing shared variables - # - # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with - # references that won't materialize before rendering time: - # - #

<%= @page_title %>

- # <%= yield %> - # - # ...and content pages that fulfill these references _at_ rendering time: - # - # <% @page_title = "Welcome" %> - # Off-world colonies offers you a chance to start a new life - # - # The result after rendering is: - # - #

Welcome

- # Off-world colonies offers you a chance to start a new life - # - # == Automatic layout assignment - # - # If there is a template in app/views/layouts/ with the same name as the current controller then it will be automatically - # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named - # app/views/layouts/weblog.erb or app/views/layouts/weblog.builder exists then it will be automatically set as - # the layout for your WeblogController. You can create a layout with the name application.erb or application.builder - # and this will be set as the default controller if there is no layout with the same name as the current controller and there is - # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout. - # assignment. So an Admin::WeblogController will look for a template named app/views/layouts/admin/weblog.erb. - # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set. - # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child - # class has a layout with the same name. - # - # == Inheritance for layouts - # - # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples: - # - # class BankController < ActionController::Base - # layout "bank_standard" - # - # class InformationController < BankController - # - # class VaultController < BankController - # layout :access_level_layout - # - # class EmployeeController < BankController - # layout nil - # - # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites - # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all. - # - # == Types of layouts - # - # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes - # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can - # be done either by specifying a method reference as a symbol or using an inline method (as a proc). - # - # The method reference is the preferred approach to variable layouts and is used like this: - # - # class WeblogController < ActionController::Base - # layout :writers_and_readers - # - # def index - # # fetching posts - # end - # - # private - # def writers_and_readers - # logged_in? ? "writer_layout" : "reader_layout" - # end - # - # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing - # is logged in or not. - # - # If you want to use an inline method, such as a proc, do something like this: - # - # class WeblogController < ActionController::Base - # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" } - # - # Of course, the most common way of specifying a layout is still just as a plain template name: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # If no directory is specified for the template name, the template will by default be looked for in app/views/layouts/. - # Otherwise, it will be looked up relative to the template root. - # - # == Conditional layouts - # - # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering - # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The - # :only and :except options can be passed to the layout call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard", :except => :rss - # - # # ... - # - # end - # - # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout - # around the rendered view. - # - # Both the :only and :except condition can accept an arbitrary number of method references, so - # #:except => [ :rss, :text_only ] is valid, as is :except => :rss. - # - # == Using a different layout in the action render call - # - # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above. - # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller. - # You can do this by passing a :layout option to the render call. For example: - # - # class WeblogController < ActionController::Base - # layout "weblog_standard" - # - # def help - # render :action => "help", :layout => "help" - # end - # end - # - # 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 yields. 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) - self.layout_name = template_name - end - - 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(*args) - memoized_default_layout(*args) - @_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new - @_memoized_default_layout[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 => formats}, prefix) - end - - def find_layout(*args) - @_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new - @_memoized_find_layout[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 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 add_layout_conditions(conditions) - # :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 - - 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 - - self.class.find_layout(layout_name, formats) - end - - def _pick_layout(layout_name = nil, 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 - if only = conditions[:only] - return only.include?(action_name) - elsif except = conditions[:except] - return !except.include?(action_name) - end - end - true - end - - end -end diff --git a/actionpack/lib/action_controller/old_base/mime_responds.rb b/actionpack/lib/action_controller/old_base/mime_responds.rb deleted file mode 100644 index 5c7218691e..0000000000 --- a/actionpack/lib/action_controller/old_base/mime_responds.rb +++ /dev/null @@ -1,188 +0,0 @@ -module ActionController #:nodoc: - module MimeResponds #:nodoc: - # Without web-service support, an action which collects the data for displaying a list of people - # might look something like this: - # - # def index - # @people = Person.find(:all) - # end - # - # Here's the same action, with web-service support baked in: - # - # def index - # @people = Person.find(:all) - # - # respond_to do |format| - # format.html - # format.xml { render :xml => @people.to_xml } - # end - # end - # - # What that says is, "if the client wants HTML in response to this action, just respond as we - # would have before, but if the client wants XML, return them the list of people in XML format." - # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) - # - # Supposing you have an action that adds a new person, optionally creating their company - # (by name) if it does not already exist, without web-services, it might look like this: - # - # def create - # @company = Company.find_or_create_by_name(params[:company][:name]) - # @person = @company.people.create(params[:person]) - # - # redirect_to(person_list_url) - # end - # - # Here's the same action, with web-service support baked in: - # - # def create - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # @person = @company.people.create(params[:person]) - # - # respond_to do |format| - # format.html { redirect_to(person_list_url) } - # format.js - # format.xml { render :xml => @person.to_xml(:include => @company) } - # end - # end - # - # If the client wants HTML, we just redirect them back to the person list. If they want Javascript - # (format.js), then it is an RJS request and we render the RJS template associated with this action. - # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also - # include the person's company in the rendered XML, so you get something like this: - # - # - # ... - # ... - # - # ... - # ... - # ... - # - # - # - # Note, however, the extra bit at the top of that action: - # - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # - # This is because the incoming XML document (if a web-service request is in process) can only contain a - # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): - # - # person[name]=...&person[company][name]=...&... - # - # And, like this (xml-encoded): - # - # - # ... - # - # ... - # - # - # - # In other words, we make the request so that it operates on a single entity's person. Then, in the action, - # we extract the company data from the request, find or create the company, and then create the new person - # with the remaining data. - # - # Note that you can define your own XML parameter parser which would allow you to describe multiple entities - # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow - # and accept Rails' defaults, life will be much easier. - # - # If you need to use a MIME type which isn't supported by default, you can register your own handlers in - # environment.rb as follows. - # - # Mime::Type.register "image/jpg", :jpg - def respond_to(*types, &block) - raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block - block ||= lambda { |responder| types.each { |type| responder.send(type) } } - responder = Responder.new(self) - block.call(responder) - responder.respond - end - - class Responder #:nodoc: - - def initialize(controller) - @controller = controller - @request = controller.request - @response = controller.response - - @mime_type_priority = @request.formats - - @order = [] - @responses = {} - end - - def custom(mime_type, &block) - mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) - - @order << mime_type - - @responses[mime_type] ||= Proc.new do - # TODO: Remove this when new base is merged in - if defined?(Http) - @controller.formats = [mime_type.to_sym] - end - - @controller.template.formats = [mime_type.to_sym] - @response.content_type = mime_type - - block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) - end - end - - def any(*args, &block) - if args.any? - args.each { |type| send(type, &block) } - else - custom(@mime_type_priority.first, &block) - end - end - - def self.generate_method_for_mime(mime) - sym = mime.is_a?(Symbol) ? mime : mime.to_sym - const = sym.to_s.upcase - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{sym}(&block) # def html(&block) - custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) - end # end - RUBY - end - - Mime::SET.each do |mime| - generate_method_for_mime(mime) - end - - def method_missing(symbol, &block) - mime_constant = Mime.const_get(symbol.to_s.upcase) - - if Mime::SET.include?(mime_constant) - self.class.generate_method_for_mime(mime_constant) - send(symbol, &block) - else - super - end - end - - def respond - for priority in @mime_type_priority - if priority == Mime::ALL - @responses[@order.first].call - return - else - if @responses[priority] - @responses[priority].call - return # mime type match found, be happy and return - end - end - end - - if @order.include?(Mime::ALL) - @responses[Mime::ALL].call - else - @controller.send :head, :not_acceptable - end - end - end - end -end diff --git a/actionpack/lib/action_controller/old_base/redirect.rb b/actionpack/lib/action_controller/old_base/redirect.rb deleted file mode 100644 index 7e10f614e2..0000000000 --- a/actionpack/lib/action_controller/old_base/redirect.rb +++ /dev/null @@ -1,89 +0,0 @@ -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: - # - # * Hash - The URL will be generated by calling url_for with the +options+. - # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. - # * String not containing a protocol - The current protocol and host is prepended to the string. - # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for redirect_to(request.env["HTTP_REFERER"]) - # - # 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 redirect_to :back, 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 - - 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? - logger.info("Redirected to #{url}") if logger && logger.info? - response.status = interpret_status(status) - response.location = url.gsub(/[\r\n]/, '') - response.body = "You are being redirected." - @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.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/old_base/render.rb b/actionpack/lib/action_controller/old_base/render.rb deleted file mode 100644 index cc0d878e01..0000000000 --- a/actionpack/lib/action_controller/old_base/render.rb +++ /dev/null @@ -1,403 +0,0 @@ -require 'action_controller/abstract/renderer' - -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 :text 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 'David' - # render :xml => {:name => "David"}.to_xml - # - # It's not necessary to call to_xml on the object you want to render, since render will - # automatically do that for you: - # - # # Also renders 'David' - # 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 to_json on the object you want to render, since render 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 :callback 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 "

Good seeing you!

" 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 :update 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 :status and :location 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? - - # This handles render "string", render :symbol, and render object - # render string and symbol are handled by render_for_name - # render object becomes render :partial => object - unless options.is_a?(Hash) - if options.is_a?(String) || options.is_a?(Symbol) - original, options = options, extra_options - else - extra_options[:partial], options = options, extra_options - end - end - - 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 options.include?(:json) - json = options[:json] - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - 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 - - # Same rules as render, but returns a Rack-compatible body - # instead of sending the response. - def render_to_body(options = nil, &block) #:doc: - render(options, &block) - response.body - ensure - response.content_type = nil - erase_render_results - reset_variables_added_to_assigns - end - - def render_to_string(options = {}) - AbstractController::Renderer.body_to_s(render_to_body(options)) - end - - # Clears the rendered results, allowing for another render to be performed. - def erase_render_results #:nodoc: - response.body = [] - @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 - - # ==== Arguments - # parts:: - # Example: ["show", [:html, :xml], "users", false] - def render_for_parts(parts, layout_details, options = {}) - parts[1] = {:formats => parts[1], :locales => [I18n.locale]} - - tmp = view_paths.find_by_parts(*parts) - - layout = _pick_layout(*layout_details) unless - self.class.exempt_from_layout.include?(tmp.handler) - - 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 diff --git a/actionpack/lib/action_controller/old_base/request_forgery_protection.rb b/actionpack/lib/action_controller/old_base/request_forgery_protection.rb deleted file mode 100644 index a470c8eec1..0000000000 --- a/actionpack/lib/action_controller/old_base/request_forgery_protection.rb +++ /dev/null @@ -1,123 +0,0 @@ -module ActionController #:nodoc: - class InvalidAuthenticityToken < ActionControllerError #:nodoc: - end - - module RequestForgeryProtection - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Helpers, Session - end - - included do - if defined?(ActionController::Http) - # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ - # sets it to :authenticity_token by default. - cattr_accessor :request_forgery_protection_token - - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection - self.allow_forgery_protection = true - end - - helper_method :form_authenticity_token - helper_method :protect_against_forgery? - end - - # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a - # forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all - # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only - # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication - # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway. - # - # This is turned on with the protect_from_forgery method, which will check the token and raise an - # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in - # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0 - # applications. - # - # The token parameter is named authenticity_token by default. If you are generating an HTML form manually (without the - # use of Rails' form_for, form_tag or other helpers), you have to include a hidden field named like that and - # set its value to what is returned by form_authenticity_token. Same applies to manually constructed Ajax requests. To - # make the token available through a global variable to scripts on a certain page, you could add something like this to a view: - # - # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %> - # - # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to - # config/environments/test.rb: - # - # # Disable request forgery protection in test environment - # config.action_controller.allow_forgery_protection = false - # - # == Learn more about CSRF (Cross-Site Request Forgery) attacks - # - # Here are some resources: - # * http://isc.sans.org/diary.html?storyid=1750 - # * http://en.wikipedia.org/wiki/Cross-site_request_forgery - # - # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. - # There are a few guidelines you should follow: - # - # * Keep your GET requests safe and idempotent. More reading material: - # * http://www.xml.com/pub/a/2002/04/24/deviant.html - # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 - # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session" - # - module ClassMethods - # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked. - # - # Example: - # - # class FooController < ApplicationController - # protect_from_forgery :except => :index - # - # # you can disable csrf protection on controller-by-controller basis: - # skip_before_filter :verify_authenticity_token - # end - # - # Valid Options: - # - # * :only/:except - Passed to the before_filter call. Set which actions are verified. - def protect_from_forgery(options = {}) - self.request_forgery_protection_token ||= :authenticity_token - before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except) - if options[:secret] || options[:digest] - ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller) - end - end - end - - protected - # The actual before_filter that is used. Modify this to change how you handle unverified requests. - def verify_authenticity_token - verified_request? || raise(ActionController::InvalidAuthenticityToken) - end - - # Returns true or false if a request is verified. Checks: - # - # * is the format restricted? By default, only HTML requests are checked. - # * is it a GET request? Gets should be safe and idempotent - # * Does the form_authenticity_token match the given token value from the params? - def verified_request? - !protect_against_forgery? || - request.method == :get || - request.xhr? || - !verifiable_request_format? || - form_authenticity_token == params[request_forgery_protection_token] - end - - def verifiable_request_format? - !request.content_type.nil? && request.content_type.verify_request? - end - - # Sets the token value for the current session. Pass a :secret option - # in +protect_from_forgery+ to add a custom salt to the hash. - def form_authenticity_token - session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32) - end - - def protect_against_forgery? - allow_forgery_protection && request_forgery_protection_token - end - end -end diff --git a/actionpack/lib/action_controller/old_base/rescue.rb b/actionpack/lib/action_controller/old_base/rescue.rb deleted file mode 100644 index 2717a06a37..0000000000 --- a/actionpack/lib/action_controller/old_base/rescue.rb +++ /dev/null @@ -1,50 +0,0 @@ -module ActionController #:nodoc: - # Actions that fail to perform as expected throw exceptions. These - # exceptions can either be rescued for the public view (with a nice - # user-friendly explanation) or for the developers view (with tons of - # debugging information). The developers view is already implemented by - # the Action Controller, but the public view should be tailored to your - # specific application. - # - # The default behavior for public exceptions is to render a static html - # file with the name of the error code thrown. If no such file exists, an - # empty response is sent with the correct status code. - # - # You can override what constitutes a local request by overriding the - # local_request? method in your own controller. Custom rescue - # behavior is achieved by overriding the rescue_action_in_public - # and rescue_action_locally methods. - module Rescue - def self.included(base) #:nodoc: - base.send :include, ActiveSupport::Rescuable - base.extend(ClassMethods) - - base.class_eval do - alias_method_chain :perform_action, :rescue - end - end - - module ClassMethods - def rescue_action(env) - exception = env.delete('action_dispatch.rescue.exception') - request = ActionDispatch::Request.new(env) - response = ActionDispatch::Response.new - new.process(request, response, :rescue_action, exception).to_a - end - end - - protected - # Exception handler called when the performance of an action raises - # an exception. - def rescue_action(exception) - rescue_with_handler(exception) || raise(exception) - end - - private - def perform_action_with_rescue - perform_action_without_rescue - rescue Exception => exception - rescue_action(exception) - end - end -end diff --git a/actionpack/lib/action_controller/old_base/responder.rb b/actionpack/lib/action_controller/old_base/responder.rb deleted file mode 100644 index 1aee980da6..0000000000 --- a/actionpack/lib/action_controller/old_base/responder.rb +++ /dev/null @@ -1,43 +0,0 @@ -module ActionController - module Responder - def self.included(klass) - klass.extend ClassMethods - end - - private - def render_for_text(text) #:nodoc: - @performed_render = true - - case text - when Proc - response.body = text - when nil - # Safari 2 doesn't pass response headers if the response is zero-length - if response.body_parts.empty? - response.body_parts << ' ' - end - else - response.body_parts << text - end - end - - # Returns a set of the methods defined as actions in your controller - 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 diff --git a/actionpack/lib/action_controller/old_base/session_management.rb b/actionpack/lib/action_controller/old_base/session_management.rb deleted file mode 100644 index ffce8e1bd1..0000000000 --- a/actionpack/lib/action_controller/old_base/session_management.rb +++ /dev/null @@ -1,54 +0,0 @@ -module ActionController #:nodoc: - module SessionManagement #:nodoc: - def self.included(base) - base.class_eval do - extend ClassMethods - end - end - - module ClassMethods - # Set the session store to be used for keeping the session data between requests. - # By default, sessions are stored in browser cookies (:cookie_store), - # but you can also specify one of the other included stores (:active_record_store, - # :mem_cache_store, or your own custom class. - def session_store=(store) - if store == :active_record_store - self.session_store = ActiveRecord::SessionStore - else - @@session_store = store.is_a?(Symbol) ? - Session.const_get(store.to_s.camelize) : - store - end - end - - # Returns the session store class currently used. - def session_store - if defined? @@session_store - @@session_store - else - ActionDispatch::Session::CookieStore - end - end - - def session=(options = {}) - self.session_store = nil if options.delete(:disabled) - session_options.merge!(options) - end - - # Returns the hash used to configure the session. Example use: - # - # ActionController::Base.session_options[:secure] = true # session only available over HTTPS - def session_options - @session_options ||= {} - end - - def session(*args) - ActiveSupport::Deprecation.warn( - "Disabling sessions for a single controller has been deprecated. " + - "Sessions are now lazy loaded. So if you don't access them, " + - "consider them off. You can still modify the session cookie " + - "options with request.session_options.", caller) - end - end - end -end diff --git a/actionpack/lib/action_controller/old_base/streaming.rb b/actionpack/lib/action_controller/old_base/streaming.rb deleted file mode 100644 index 5c72fc9ad9..0000000000 --- a/actionpack/lib/action_controller/old_base/streaming.rb +++ /dev/null @@ -1,188 +0,0 @@ -module ActionController #:nodoc: - # Methods for sending arbitrary data and for streaming files to the browser, - # instead of rendering. - module Streaming - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include ActionController::Renderer - end - - DEFAULT_SEND_FILE_OPTIONS = { - :type => 'application/octet-stream'.freeze, - :disposition => 'attachment'.freeze, - :stream => true, - :buffer_size => 4096, - :x_sendfile => false - }.freeze - - X_SENDFILE_HEADER = 'X-Sendfile'.freeze - - protected - # Sends the file, by default streaming it 4096 bytes at a time. This way the - # whole file doesn't need to be read into memory at once. This makes it - # feasible to send even large files. You can optionally turn off streaming - # and send the whole file at once. - # - # Be careful to sanitize the path parameter if it is coming from a web - # page. send_file(params[:path]) allows a malicious user to - # download any file on your server. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # Defaults to File.basename(path). - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :length - used to manually override the length (in bytes) of the content that - # is going to be sent to the client. Defaults to File.size(path). - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :stream - whether to send the file to the user agent as it is read (+true+) - # or to read the entire file before sending (+false+). Defaults to +true+. - # * :buffer_size - specifies size (in bytes) of the buffer used to stream the file. - # Defaults to 4096. - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # * :url_based_filename - set to +true+ if you want the browser guess the filename from - # the URL, which is necessary for i18n filenames on certain browsers - # (setting :filename overrides this option). - # * :x_sendfile - uses X-Sendfile to send the file when set to +true+. This is currently - # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this - # uses the web server to send the file, this may lower memory consumption on your server and - # it will not block your application for further requests. - # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and - # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+. - # - # The default Content-Type and Content-Disposition headers are - # set to download arbitrary binary files in as many browsers as - # possible. IE versions 4, 5, 5.5, and 6 are all known to have - # a variety of quirks (especially when downloading over SSL). - # - # Simple download: - # - # send_file '/path/to.zip' - # - # Show a JPEG in the browser: - # - # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' - # - # Show a 404 page in the browser: - # - # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 - # - # Read about the other Content-* HTTP headers if you'd like to - # provide the user with more information (such as Content-Description) in - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. - # - # Also be aware that the document may be cached by proxies and browsers. - # The Pragma and Cache-Control headers declare how the file may be cached - # by intermediaries. They default to require clients to validate with - # the server before releasing cached responses. See - # http://www.mnot.net/cache_docs/ for an overview of web caching and - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - # for the Cache-Control header spec. - def send_file(path, options = {}) #:doc: - raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) - - options[:length] ||= File.size(path) - options[:filename] ||= File.basename(path) unless options[:url_based_filename] - send_file_headers! options - - @performed_render = false - - if options[:x_sendfile] - logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger - head options[:status], X_SENDFILE_HEADER => path - else - if options[:stream] - # TODO : Make render :text => proc {} work with the new base - render :status => options[:status], :text => Proc.new { |response, output| - logger.info "Streaming file #{path}" unless logger.nil? - len = options[:buffer_size] || 4096 - File.open(path, 'rb') do |file| - while buf = file.read(len) - output.write(buf) - end - end - } - else - logger.info "Sending file #{path}" unless logger.nil? - File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read } - end - end - end - - # Sends the given binary data to the browser. This method is similar to - # render :text => data, but also allows you to specify whether - # the browser should display the response as a file attachment (i.e. in a - # download dialog) or as inline data. You may also set the content type, - # the apparent file name, and other things. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # - # Generic data download: - # - # send_data buffer - # - # Download a dynamically-generated tarball: - # - # send_data generate_tgz('dir'), :filename => 'dir.tgz' - # - # Display an image Active Record in the browser: - # - # send_data image.data, :type => image.content_type, :disposition => 'inline' - # - # See +send_file+ for more information on HTTP Content-* headers and caching. - # - # Tip: if you want to stream large amounts of on-the-fly generated - # data to the browser, then use render :text => proc { ... } - # instead. See ActionController::Base#render for more information. - def send_data(data, options = {}) #:doc: - logger.info "Sending data #{options[:filename]}" if logger - send_file_headers! options.merge(:length => data.size) - @performed_render = false - render :status => options[:status], :text => data - end - - private - def send_file_headers!(options) - options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) - [:length, :type, :disposition].each do |arg| - raise ArgumentError, ":#{arg} option required" if options[arg].nil? - end - - disposition = options[:disposition].dup || 'attachment' - - disposition <<= %(; filename="#{options[:filename]}") if options[:filename] - - content_type = options[:type] - - if content_type.is_a?(Symbol) - raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s) - self.content_type = Mime::Type.lookup_by_extension(content_type.to_s) - else - self.content_type = content_type - end - - headers.merge!( - 'Content-Length' => options[:length], - 'Content-Disposition' => disposition, - 'Content-Transfer-Encoding' => 'binary' - ) - - # Fix a problem with IE 6.0 on opening downloaded files: - # If Cache-Control: no-cache is set (which Rails does by default), - # IE removes the file it just downloaded from its cache immediately - # after it displays the "open/save" dialog, which means that if you - # hit "open" the file isn't there anymore when the application that - # is called for handling the download is run, so let's workaround that - headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache' - end - end -end diff --git a/actionpack/lib/action_controller/old_base/verification.rb b/actionpack/lib/action_controller/old_base/verification.rb deleted file mode 100644 index d87b348ed4..0000000000 --- a/actionpack/lib/action_controller/old_base/verification.rb +++ /dev/null @@ -1,133 +0,0 @@ -module ActionController #:nodoc: - module Verification #:nodoc: - extend ActiveSupport::Concern - - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Callbacks, Session, Flash, Renderer - end - - # This module provides a class-level method for specifying that certain - # actions are guarded against being called without certain prerequisites - # being met. This is essentially a special kind of before_filter. - # - # An action may be guarded against being invoked without certain request - # parameters being set, or without certain session values existing. - # - # When a verification is violated, values may be inserted into the flash, and - # a specified redirection is triggered. If no specific action is configured, - # verification failures will by default result in a 400 Bad Request response. - # - # Usage: - # - # class GlobalController < ActionController::Base - # # Prevent the #update_settings action from being invoked unless - # # the 'admin_privileges' request parameter exists. The - # # settings action will be redirected to in current controller - # # if verification fails. - # verify :params => "admin_privileges", :only => :update_post, - # :redirect_to => { :action => "settings" } - # - # # Disallow a post from being updated if there was no information - # # submitted with the post, and if there is no active post in the - # # session, and if there is no "note" key in the flash. The route - # # named category_url will be redirected to if verification fails. - # - # verify :params => "post", :session => "post", "flash" => "note", - # :only => :update_post, - # :add_flash => { "alert" => "Failed to create your message" }, - # :redirect_to => :category_url - # - # Note that these prerequisites are not business rules. They do not examine - # the content of the session or the parameters. That level of validation should - # be encapsulated by your domain model or helper methods in the controller. - module ClassMethods - # Verify the given actions so that if certain prerequisites are not met, - # the user is redirected to a different action. The +options+ parameter - # is a hash consisting of the following key/value pairs: - # - # :params:: - # a single key or an array of keys that must be in the params - # hash in order for the action(s) to be safely called. - # :session:: - # a single key or an array of keys that must be in the session - # in order for the action(s) to be safely called. - # :flash:: - # a single key or an array of keys that must be in the flash in order - # for the action(s) to be safely called. - # :method:: - # a single key or an array of keys--any one of which must match the - # current request method in order for the action(s) to be safely called. - # (The key should be a symbol: :get or :post, for - # example.) - # :xhr:: - # true/false option to ensure that the request is coming from an Ajax - # call or not. - # :add_flash:: - # a hash of name/value pairs that should be merged into the session's - # flash if the prerequisites cannot be satisfied. - # :add_headers:: - # a hash of name/value pairs that should be merged into the response's - # headers hash if the prerequisites cannot be satisfied. - # :redirect_to:: - # the redirection parameters to be used when redirecting if the - # prerequisites cannot be satisfied. You can redirect either to named - # route or to the action in some controller. - # :render:: - # the render parameters to be used when the prerequisites cannot be satisfied. - # :only:: - # only apply this verification to the actions specified in the associated - # array (may also be a single value). - # :except:: - # do not apply this verification to the actions specified in the associated - # array (may also be a single value). - def verify(options={}) - before_filter :only => options[:only], :except => options[:except] do |c| - c.__send__ :verify_action, options - end - end - end - - private - - def verify_action(options) #:nodoc: - if prereqs_invalid?(options) - flash.update(options[:add_flash]) if options[:add_flash] - response.headers.merge!(options[:add_headers]) if options[:add_headers] - apply_remaining_actions(options) unless performed? - end - end - - def prereqs_invalid?(options) # :nodoc: - verify_presence_of_keys_in_hash_flash_or_params(options) || - verify_method(options) || - verify_request_xhr_status(options) - end - - def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: - [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || - [*options[:session]].find { |v| session[v].nil? } || - [*options[:flash] ].find { |v| flash[v].nil? } - end - - def verify_method(options) # :nodoc: - [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] - end - - def verify_request_xhr_status(options) # :nodoc: - request.xhr? != options[:xhr] unless options[:xhr].nil? - end - - def apply_redirect_to(redirect_to_option) # :nodoc: - (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option - end - - def apply_remaining_actions(options) # :nodoc: - case - when options[:render] ; render(options[:render]) - when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) - else head(:bad_request) - end - end - end -end \ No newline at end of file -- cgit v1.2.3 From 3c15cba17519e7a4acc3958662f8f3693837c179 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 17:32:10 -0700 Subject: Whoops, I guess we broke layouts ;) --- actionpack/lib/action_controller/abstract/layouts.rb | 6 ++++++ actionpack/lib/action_controller/base/base.rb | 6 ------ actionpack/test/abstract_controller/abstract_controller_test.rb | 5 ----- actionpack/test/abstract_controller/layouts_test.rb | 9 --------- actionpack/test/new_base/render_text_test.rb | 4 +--- actionpack/test/new_base/test_helper.rb | 9 --------- 6 files changed, 7 insertions(+), 32 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 9ff8e9beb1..2ac4e6068a 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -9,6 +9,11 @@ module AbstractController end module ClassMethods + def inherited(klass) + super + klass._write_layout_method + end + # Specify the layout to use for this class. # # If the specified layout is a: @@ -78,6 +83,7 @@ module AbstractController end ruby_eval end + self.class_eval { private :_layout } end end diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb index e8fc153578..e541d24e31 100644 --- a/actionpack/lib/action_controller/base/base.rb +++ b/actionpack/lib/action_controller/base/base.rb @@ -71,12 +71,6 @@ module ActionController @subclasses ||= [] end - def self.app_loaded! - @subclasses.each do |subclass| - subclass.constantize._write_layout_method - end - end - def _normalize_options(action = nil, options = {}, &blk) if action.is_a?(Hash) options, action = action, nil diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract_controller/abstract_controller_test.rb index 05b55216c8..56ec6a6a31 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract_controller/abstract_controller_test.rb @@ -136,11 +136,6 @@ module AbstractController class WithLayouts < PrefixedViews include Layouts - def self.inherited(klass) - klass._write_layout_method - super - end - private def self.layout(formats) begin diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 64c435abb7..37eb55ec98 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -141,15 +141,6 @@ module AbstractControllerTests end end - # TODO Move to bootloader - AbstractController::Base.descendants.each do |klass| - klass = klass.constantize - next unless klass < AbstractController::Layouts - klass.class_eval do - _write_layout_method - end - end - class TestBase < ActiveSupport::TestCase test "when no layout is specified, and no default is available, render without a layout" do result = Blank.new.process(:index) diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/new_base/render_text_test.rb index ffc149283b..4a90eaac40 100644 --- a/actionpack/test/new_base/render_text_test.rb +++ b/actionpack/test/new_base/render_text_test.rb @@ -134,6 +134,4 @@ module RenderText assert_status 200 end end -end - -ActionController::Base.app_loaded! +end \ No newline at end of file diff --git a/actionpack/test/new_base/test_helper.rb b/actionpack/test/new_base/test_helper.rb index 722dddcfc2..9271b2dd59 100644 --- a/actionpack/test/new_base/test_helper.rb +++ b/actionpack/test/new_base/test_helper.rb @@ -41,15 +41,6 @@ class Rack::TestCase < ActionController::IntegrationTest end ActionController::Routing.use_controllers!(controllers) - - # Move into a bootloader - ActionController::Base.subclasses.each do |klass| - klass = klass.constantize - next unless klass < AbstractController::Layouts - klass.class_eval do - _write_layout_method - end - end end def app -- cgit v1.2.3 From 1fbfa3e705c37656c308436f21d42b09591ba60e Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 15 Jun 2009 17:33:25 -0700 Subject: More _write_layout_method removal --- actionpack/test/controller/layout_test.rb | 9 --------- actionpack/test/controller/mime_responds_test.rb | 5 ----- 2 files changed, 14 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index cb9bdf57bb..c3d7b0778d 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -38,15 +38,6 @@ end class MultipleExtensions < LayoutTest end -if defined?(ActionController::Http) - LayoutTest._write_layout_method - ProductController._write_layout_method - ItemController._write_layout_method - ThirdPartyTemplateLibraryController._write_layout_method - MultipleExtensions._write_layout_method - ControllerNameSpace::NestedController._write_layout_method -end - class LayoutAutoDiscoveryTest < ActionController::TestCase def setup super diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 56b49251c6..0c6822a5b1 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -512,11 +512,6 @@ class SuperPostController < PostController end end -if defined?(ActionController::Http) - PostController._write_layout_method - SuperPostController._write_layout_method -end - class MimeControllerLayoutsTest < ActionController::TestCase tests PostController -- cgit v1.2.3 From 18c3b77b2381e9f7072d595fa9f599b0994852a7 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 10:46:03 -0700 Subject: Merge process2 into process to people's regular tests run :P --- .../lib/action_controller/testing/integration.rb | 2 +- .../lib/action_controller/testing/process.rb | 51 ++++----------- .../lib/action_controller/testing/process2.rb | 74 ---------------------- 3 files changed, 12 insertions(+), 115 deletions(-) delete mode 100644 actionpack/lib/action_controller/testing/process2.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb index 37ca93f22c..5cb0f48f82 100644 --- a/actionpack/lib/action_controller/testing/integration.rb +++ b/actionpack/lib/action_controller/testing/integration.rb @@ -245,7 +245,7 @@ module ActionController path = location.query ? "#{location.path}?#{location.query}" : location.path end - [ControllerCapture, ActionController::ProcessWithTest].each do |mod| + [ControllerCapture, ActionController::Testing].each do |mod| unless ActionController::Base < mod ActionController::Base.class_eval { include mod } end diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index ea5752d4f5..7634290ea1 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -124,30 +124,24 @@ module ActionController #:nodoc: @request.recycle! @response.recycle! + @controller.response_body = nil + @controller.formats = nil + @controller.params = nil @html_document = nil - @request.request_method = http_method + @request.env['REQUEST_METHOD'] = http_method parameters ||= {} @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters) @request.session = ActionController::TestSession.new(session) unless session.nil? @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash - build_request_uri(action, parameters) - - Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest - - env = @request.env - app = @controller - - # TODO: Enable Lint - # app = Rack::Lint.new(app) - status, headers, body = app.action(action, env) - response = Rack::MockResponse.new(status, headers, body) - - @response.request, @response.template = @request, @controller.template - @response.status, @response.headers, @response.body = response.status, response.headers, response.body + @controller.request = @request + @controller.params.merge!(parameters) + build_request_uri(action, parameters) + Base.class_eval { include Testing } + @controller.process_with_new_base_test(@request, @response) @response end @@ -167,7 +161,7 @@ module ActionController #:nodoc: next if ActionController::Base.protected_instance_variables.include?(ivar) assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) end - + key.nil? ? assigns : assigns[key.to_s] end @@ -263,27 +257,4 @@ module ActionController #:nodoc: ActionController::Routing.const_set(:Routes, real_routes) if real_routes end end - - module ProcessWithTest #:nodoc: - def self.included(base) - base.class_eval { - attr_reader :assigns - alias_method_chain :process, :test - } - end - - def process_with_test(*args) - process_without_test(*args).tap { set_test_assigns } - end - - private - def set_test_assigns - @assigns = {} - (instance_variable_names - self.class.protected_instance_variables).each do |var| - name, value = var[1..-1], instance_variable_get(var) - @assigns[name] = value - @template.assigns[name] = value if response - end - end - end -end +end \ No newline at end of file diff --git a/actionpack/lib/action_controller/testing/process2.rb b/actionpack/lib/action_controller/testing/process2.rb deleted file mode 100644 index 1c6fd2d80a..0000000000 --- a/actionpack/lib/action_controller/testing/process2.rb +++ /dev/null @@ -1,74 +0,0 @@ -require "action_controller/testing/process" - -module ActionController - module TestProcess - - # Executes a request simulating GET HTTP method and set/volley the response - def get(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "GET") - end - - # Executes a request simulating POST HTTP method and set/volley the response - def post(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "POST") - end - - # Executes a request simulating PUT HTTP method and set/volley the response - def put(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "PUT") - end - - # Executes a request simulating DELETE HTTP method and set/volley the response - def delete(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "DELETE") - end - - # Executes a request simulating HEAD HTTP method and set/volley the response - def head(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "HEAD") - end - - def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') - # Sanity check for required instance variables so we can give an - # understandable error message. - %w(@controller @request @response).each do |iv_name| - if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? - raise "#{iv_name} is nil: make sure you set it in your test's setup method." - end - end - - @request.recycle! - @response.recycle! - @controller.response_body = nil - @controller.formats = nil - @controller.params = nil - - @html_document = nil - @request.env['REQUEST_METHOD'] = http_method - - parameters ||= {} - @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters) - - @request.session = ActionController::TestSession.new(session) unless session.nil? - @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash - - @controller.request = @request - @controller.params.merge!(parameters) - build_request_uri(action, parameters) - # Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest - @controller.process_with_new_base_test(@request, @response) - @response - end - - def build_request_uri(action, parameters) - unless @request.env['REQUEST_URI'] - options = @controller.__send__(:rewrite_options, parameters) - options.update(:only_path => true, :action => action) - - url = ActionController::UrlRewriter.new(@request, parameters) - @request.request_uri = url.rewrite(options) - end - end - - end -end \ No newline at end of file -- cgit v1.2.3 From a6e803bee124073f5d82d3264b3cdbcbf4d957d5 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 11:20:28 -0700 Subject: Remove exceptions from AbstractUnit so they work in real life --- actionpack/lib/action_controller.rb | 12 ++++++++ actionpack/test/abstract_unit.rb | 59 +------------------------------------ 2 files changed, 13 insertions(+), 58 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 073600f69b..e822a11d14 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -40,6 +40,18 @@ module ActionController autoload :Translation, 'action_controller/translation' autoload :Cookies, 'action_controller/base/cookies' + autoload :ActionControllerError, 'action_controller/base/exceptions' + autoload :SessionRestoreError, 'action_controller/base/exceptions' + autoload :RenderError, 'action_controller/base/exceptions' + autoload :RoutingError, 'action_controller/base/exceptions' + autoload :MethodNotAllowed, 'action_controller/base/exceptions' + autoload :NotImplemented, 'action_controller/base/exceptions' + autoload :UnknownController, 'action_controller/base/exceptions' + autoload :MissingFile, 'action_controller/base/exceptions' + autoload :RenderError, 'action_controller/base/exceptions' + autoload :SessionOverflowError, 'action_controller/base/exceptions' + autoload :UnknownHttpMethod, 'action_controller/base/exceptions' + require 'action_controller/routing' end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index a7957ed944..1333a9d71a 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -15,7 +15,7 @@ require 'active_support/test_case' require 'action_controller/abstract' require 'action_controller' require 'fixture_template' -require 'action_controller/testing/process2' +require 'action_controller/testing/process' require 'action_view/test_case' require 'action_controller/testing/integration' require 'active_support/dependencies' @@ -59,63 +59,6 @@ module ActionController } Base.session_store = nil - class ActionControllerError < StandardError #:nodoc: - end - - class SessionRestoreError < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class RoutingError < ActionControllerError #:nodoc: - attr_reader :failures - def initialize(message, failures=[]) - super(message) - @failures = failures - end - end - - class MethodNotAllowed < ActionControllerError #:nodoc: - attr_reader :allowed_methods - - def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - - def handle_response!(response) - response.headers['Allow'] ||= allowed_methods_header - end - end - - class NotImplemented < MethodNotAllowed #:nodoc: - end - - class UnknownController < ActionControllerError #:nodoc: - end - - class MissingFile < ActionControllerError #:nodoc: - end - - class RenderError < ActionControllerError #:nodoc: - end - - class SessionOverflowError < ActionControllerError #:nodoc: - DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - class UnknownHttpMethod < ActionControllerError #:nodoc: - end - class Base include ActionController::Testing end -- cgit v1.2.3 From 01a4bc84b8787df74d54147a0cf564df75e87970 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 11:32:38 -0700 Subject: Exceptions now in an exceptions module --- .../lib/action_controller/base/exceptions.rb | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 actionpack/lib/action_controller/base/exceptions.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/exceptions.rb b/actionpack/lib/action_controller/base/exceptions.rb new file mode 100644 index 0000000000..d0811254cb --- /dev/null +++ b/actionpack/lib/action_controller/base/exceptions.rb @@ -0,0 +1,58 @@ +module ActionController + class ActionControllerError < StandardError #:nodoc: + end + + class SessionRestoreError < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class RoutingError < ActionControllerError #:nodoc: + attr_reader :failures + def initialize(message, failures=[]) + super(message) + @failures = failures + end + end + + class MethodNotAllowed < ActionControllerError #:nodoc: + attr_reader :allowed_methods + + def initialize(*allowed_methods) + super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") + @allowed_methods = allowed_methods + end + + def allowed_methods_header + allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' + end + + def handle_response!(response) + response.headers['Allow'] ||= allowed_methods_header + end + end + + class NotImplemented < MethodNotAllowed #:nodoc: + end + + class UnknownController < ActionControllerError #:nodoc: + end + + class MissingFile < ActionControllerError #:nodoc: + end + + class RenderError < ActionControllerError #:nodoc: + end + + class SessionOverflowError < ActionControllerError #:nodoc: + DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + class UnknownHttpMethod < ActionControllerError #:nodoc: + end +end \ No newline at end of file -- cgit v1.2.3 From dd58f4021da30af7f40ee139e7487b887cac44e9 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 13:44:51 -0700 Subject: An exception is raised if a layout is missing only if the layout is missing for all mimes --- actionpack/lib/action_controller/abstract/layouts.rb | 2 ++ actionpack/lib/action_controller/base/layouts.rb | 4 ++-- actionpack/lib/action_controller/base/mime_responds.rb | 2 +- actionpack/test/controller/mime_responds_test.rb | 8 -------- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 2ac4e6068a..6aa196e807 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -140,6 +140,8 @@ module AbstractController rescue NameError => e raise NoMethodError, "You specified #{@_layout.inspect} as the layout, but no such method was found" + rescue ActionView::MissingTemplate + _find_by_parts(_layout({}), {}) end end diff --git a/actionpack/lib/action_controller/base/layouts.rb b/actionpack/lib/action_controller/base/layouts.rb index ace4b148c9..5c55e9745e 100644 --- a/actionpack/lib/action_controller/base/layouts.rb +++ b/actionpack/lib/action_controller/base/layouts.rb @@ -176,7 +176,7 @@ module ActionController super return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout) - layout = options.key?(:layout) ? options[:layout] : :none + layout = options.key?(:layout) ? options[:layout] : :default options[:_layout] = _layout_for_option(layout, options[:_template].details) end @@ -184,7 +184,7 @@ module ActionController case name when String then _layout_for_name(name, details) when true then _default_layout(details, true) - when :none then _default_layout(details, false) + when :default then _default_layout(details, false) when false, nil then nil else raise ArgumentError, diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb index 5c7218691e..c0aa58a6c2 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -124,8 +124,8 @@ module ActionController #:nodoc: @controller.formats = [mime_type.to_sym] end + @controller.content_type = mime_type @controller.template.formats = [mime_type.to_sym] - @response.content_type = mime_type block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 0c6822a5b1..04d6c4173a 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -467,14 +467,6 @@ class MimeControllerTest < ActionController::TestCase assert_equal '
Hello iPhone future from iPhone!
', @response.body assert_equal "text/html", @response.content_type end - - def test_format_with_custom_response_type_and_request_headers_with_only_one_layout_present - get :iphone_with_html_response_type_without_layout - assert_equal '
Hello future from Firefox!
', @response.body - - @request.accept = "text/iphone" - assert_raise(ActionView::MissingTemplate) { get :iphone_with_html_response_type_without_layout } - end end class AbstractPostController < ActionController::Base -- cgit v1.2.3 From 315147fcb5be2a937dcceb785de04a97616c49d0 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 13:54:55 -0700 Subject: Move the exceptions for missing template into Compatibility in prep for moving it into a "not-production" module --- actionpack/lib/action_controller/abstract/layouts.rb | 2 -- actionpack/lib/action_controller/base/compatibility.rb | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 6aa196e807..2ac4e6068a 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -140,8 +140,6 @@ module AbstractController rescue NameError => e raise NoMethodError, "You specified #{@_layout.inspect} as the layout, but no such method was found" - rescue ActionView::MissingTemplate - _find_by_parts(_layout({}), {}) end end diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/base/compatibility.rb index 29ba43a879..085db284e4 100644 --- a/actionpack/lib/action_controller/base/compatibility.rb +++ b/actionpack/lib/action_controller/base/compatibility.rb @@ -118,6 +118,13 @@ module ActionController details[:prefix] = nil if name =~ /\blayouts/ super end + + # Move this into a "don't run in production" module + def _default_layout(details, require_layout = false) + super + rescue ActionView::MissingTemplate + _find_by_parts(_layout({}), {}) + end def performed? response_body -- cgit v1.2.3 From 1c4d28ba314c8cdd0039becf3bc9e678219b8f46 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Jun 2009 10:37:39 -0500 Subject: Move model naming into ActiveModel --- .../activerecord/render_partial_with_record_identification_test.rb | 1 + actionpack/test/controller/record_identifier_test.rb | 2 ++ actionpack/test/controller/redirect_test.rb | 1 + actionpack/test/lib/controller/fake_models.rb | 4 ++++ actionpack/test/template/atom_feed_helper_test.rb | 1 + actionpack/test/template/prototype_helper_test.rb | 3 +++ actionpack/test/template/record_tag_helper_test.rb | 1 + actionpack/test/template/test_test.rb | 1 + actionpack/test/template/url_helper_test.rb | 2 ++ 9 files changed, 16 insertions(+) (limited to 'actionpack') diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index 0a596c7ae0..2a31f3be44 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -126,6 +126,7 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base end class Game < Struct.new(:name, :id) + extend ActiveModel::Naming def to_param id.to_s end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 12c1eaea69..28bc608d47 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -1,6 +1,8 @@ require 'abstract_unit' class Comment + extend ActiveModel::Naming + attr_reader :id def save; @id = 1 end def new_record?; @id.nil? end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 13247f2d08..453a77e7bc 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -4,6 +4,7 @@ class WorkshopsController < ActionController::Base end class Workshop + extend ActiveModel::Naming attr_accessor :id, :new_record def initialize(id, new_record) diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 0b30c79b10..5e63204bad 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -1,4 +1,6 @@ class Customer < Struct.new(:name, :id) + extend ActiveModel::Naming + def to_param id.to_s end @@ -12,6 +14,8 @@ end module Quiz class Question < Struct.new(:name, :id) + extend ActiveModel::Naming + def to_param id.to_s end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index bd97caf5d7..6f1179f359 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) +Scroll.extend ActiveModel::Naming class ScrollsController < ActionController::Base FEEDS = {} diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index f9f418aec9..02b1d137f5 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -1,8 +1,10 @@ require 'abstract_unit' Bunny = Struct.new(:Bunny, :id) +Bunny.extend ActiveModel::Naming class Author + extend ActiveModel::Naming attr_reader :id def save; @id = 1 end def new_record?; @id.nil? end @@ -12,6 +14,7 @@ class Author end class Article + extend ActiveModel::Naming attr_reader :id attr_reader :author_id def save; @id = 1; @author_id = 1 end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 809ed6d6af..5b840d123b 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' class Post + extend ActiveModel::Naming def id 45 end diff --git a/actionpack/test/template/test_test.rb b/actionpack/test/template/test_test.rb index dd07a6d438..f32d0b3d42 100644 --- a/actionpack/test/template/test_test.rb +++ b/actionpack/test/template/test_test.rb @@ -41,6 +41,7 @@ class PeopleHelperTest < ActionView::TestCase def test_link_to_person person = mock(:name => "David") + person.class.extend ActiveModel::Naming expects(:mocha_mock_path).with(person).returns("/people/1") assert_equal 'David', link_to_person(person) end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index f3d2f87b4a..f0364fd660 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -494,6 +494,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase end class Workshop + extend ActiveModel::Naming attr_accessor :id, :new_record def initialize(id, new_record) @@ -510,6 +511,7 @@ class Workshop end class Session + extend ActiveModel::Naming attr_accessor :id, :workshop_id, :new_record def initialize(id, new_record) -- cgit v1.2.3 From de388ba864ab47cbb3d92b3a36af254540788ed5 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 16 Jun 2009 16:46:56 -0700 Subject: Fix pending test about content-type --- .../test/controller/action_pack_assertions_test.rb | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 24686ab4b6..ecbaba39d1 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -13,6 +13,18 @@ class ActionPackAssertionsController < ActionController::Base # a standard template def hello_xml_world() render :template => "test/hello_xml_world"; end + # a standard template rendering PDF + def hello_xml_world_pdf + self.content_type = "application/pdf" + render :template => "test/hello_xml_world" + end + + # a standard template rendering PDF + def hello_xml_world_pdf_header + response.headers["Content-Type"] = "application/pdf; charset=utf-8" + render :template => "test/hello_xml_world" + end + # a standard partial def partial() render :partial => 'test/partial'; end @@ -537,11 +549,13 @@ class ActionPackHeaderTest < ActionController::TestCase end def test_rendering_xml_respects_content_type - pending do - @response.headers['type'] = 'application/pdf' - process :hello_xml_world - assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type']) - end + process :hello_xml_world_pdf + assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type']) + end + + def test_rendering_xml_respects_content_type_when_set_in_the_header + process :hello_xml_world_pdf_header + assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type']) end def test_render_text_with_custom_content_type -- cgit v1.2.3 From 4fad953f90f82e860a69d67745887b40d5b15475 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 12:00:23 -0700 Subject: Fixing pending tests and fixed some formats / partial rendering semantics --- actionpack/lib/action_dispatch/http/mime_type.rb | 14 ++++++- actionpack/lib/action_dispatch/http/request.rb | 10 ++--- actionpack/lib/action_view/base.rb | 2 + .../lib/action_view/template/handlers/rjs.rb | 1 - actionpack/lib/action_view/template/path.rb | 3 +- actionpack/lib/action_view/template/template.rb | 2 +- actionpack/lib/action_view/template/text.rb | 2 +- actionpack/test/controller/layout_test.rb | 22 ++--------- actionpack/test/dispatch/mime_type_test.rb | 4 +- actionpack/test/lib/fixture_template.rb | 2 +- actionpack/test/new_base/render_rjs_test.rb | 46 ++++++++++++++++++++++ actionpack/test/template/render_test.rb | 23 ----------- 12 files changed, 74 insertions(+), 57 deletions(-) create mode 100644 actionpack/test/new_base/render_rjs_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 25156a4c75..dda6604bdd 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -2,7 +2,19 @@ require 'set' require 'active_support/core_ext/class/attribute_accessors' module Mime - SET = [] + class Mimes < Array + def symbols + @symbols ||= map {|m| m.to_sym } + end + + %w(<< concat shift unshift push pop []= clear compact! collect! + delete delete_at delete_if flatten! map! insert reject! reverse! + replace slice! sort! uniq!).each do |method| + define_method(method) { @symbols = nil; super } + end + end + + SET = Mimes.new EXTENSION_LOOKUP = {} LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? } diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 140feb9a68..3f23a5af7a 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -180,12 +180,10 @@ module ActionDispatch else accepts.dup end.tap do |ret| - if defined?(ActionController::Http) - if ret == ONLY_ALL - ret.replace Mime::SET - elsif all = ret.index(Mime::ALL) - ret.delete_at(all) && ret.insert(all, *Mime::SET) - end + if ret == ONLY_ALL + ret.replace Mime::SET + elsif all = ret.index(Mime::ALL) + ret.delete_at(all) && ret.insert(all, *Mime::SET) end end else diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 4ab568b44c..2d8f51300a 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -258,9 +258,11 @@ module ActionView #:nodoc: def with_template(current_template) last_template, self.template = template, current_template + old_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols yield ensure self.template = last_template + self.formats = old_formats end def punctuate_body!(part) diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index a36744c2b7..b1d15dc209 100644 --- a/actionpack/lib/action_view/template/handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -6,7 +6,6 @@ module ActionView self.default_format = Mime::JS def compile(template) - "@formats = [:html];" + "controller.response.content_type ||= Mime::JS;" + "update_page do |page|;#{template.source}\nend" end diff --git a/actionpack/lib/action_view/template/path.rb b/actionpack/lib/action_view/template/path.rb index 478bf96c9a..c3837a9f07 100644 --- a/actionpack/lib/action_view/template/path.rb +++ b/actionpack/lib/action_view/template/path.rb @@ -47,8 +47,7 @@ module ActionView def formats_glob @formats_glob ||= begin - formats = Mime::SET.map { |m| m.symbol } - '{' + formats.map { |l| ".#{l}," }.join + '}' + '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' end end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index e7ea42c2eb..53aaa3dded 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -20,7 +20,7 @@ module ActionView handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" end @mime_type = Mime::Type.lookup_by_extension(format.to_s) - @details[:formats] = Array.wrap(format && format.to_sym) + @details[:formats] = Array.wrap(format.to_sym) end def render(view, locals, &blk) diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index fd57b1677e..81944ff546 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -3,7 +3,7 @@ module ActionView #:nodoc: def initialize(string, content_type = Mime[:html]) super(string.to_s) - @content_type = Mime[content_type] + @content_type = Mime[content_type] || content_type end def details diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index c3d7b0778d..feb2f81cc1 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -165,26 +165,10 @@ class LayoutSetInResponseTest < ActionController::TestCase assert_nil @controller.template.layout end - for_tag(:old_base) do - # exempt_from_layout is deprecated - def test_exempt_from_layout_honored_by_render_template - ActionController::Base.exempt_from_layout :erb - @controller = RenderWithTemplateOptionController.new - - get :hello - assert_equal "alt/hello.rhtml", @response.body.strip - - ensure - ActionController::Base.exempt_from_layout.delete(ERB) - end - end - def test_layout_is_picked_from_the_controller_instances_view_path - pending do - @controller = PrependsViewPathController.new - get :hello - assert_equal 'layouts/alt', @controller.template.layout - end + @controller = PrependsViewPathController.new + get :hello + assert @controller.template.layout =~ /layouts\/alt\.\w+/ end def test_absolute_pathed_layout diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 27bdd10ee5..4ea0fedb8f 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -56,7 +56,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "type convenience methods" do # Don't test Mime::ALL, since it Mime::ALL#html? == true - types = Mime::SET.to_a.map{|m| m.to_sym }.uniq - [:all] + types = Mime::SET.symbols.uniq - [:all] # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) } @@ -76,7 +76,7 @@ class MimeTypeTest < ActiveSupport::TestCase end test "verifiable mime types" do - all_types = Mime::SET.to_a.map{|m| m.to_sym} + all_types = Mime::SET.symbols all_types.uniq! # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE all_types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) } diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb index 59fb6819ed..4ea451879c 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/test/lib/fixture_template.rb @@ -22,7 +22,7 @@ class Template def formats_regexp @formats_regexp ||= begin - formats = Mime::SET.map { |m| m.symbol } + formats = Mime::SET.symbols '(?:' + formats.map { |l| "\\.#{Regexp.escape(l.to_s)}" }.join('|') + ')?' end end diff --git a/actionpack/test/new_base/render_rjs_test.rb b/actionpack/test/new_base/render_rjs_test.rb new file mode 100644 index 0000000000..fdf3556e8e --- /dev/null +++ b/actionpack/test/new_base/render_rjs_test.rb @@ -0,0 +1,46 @@ +require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") + +module RenderRjs + + class BasicController < ActionController::Base + + self.view_paths = [ActionView::Template::FixturePath.new( + "render_rjs/basic/index.js.rjs" => "page[:customer].replace_html render(:partial => 'customer')", + "render_rjs/basic/index_html.js.rjs" => "page[:customer].replace_html :partial => 'customer'", + "render_rjs/basic/_customer.js.erb" => "JS Partial", + "render_rjs/basic/_customer.html.erb" => "HTML Partial", + "render_rjs/basic/index_locale.js.rjs" => "page[:customer].replace_html :partial => 'customer'", + "render_rjs/basic/_customer.da.html.erb" => "Danish HTML Partial", + "render_rjs/basic/_customer.da.js.erb" => "Danish JS Partial" + )] + + def index + render + end + + def index_locale + old_locale, I18n.locale = I18n.locale, :da + end + + end + + class TestBasic < SimpleRouteCase + testing BasicController + + test "rendering a partial in an RJS template should pick the JS template over the HTML one" do + get :index + assert_response("$(\"customer\").update(\"JS Partial\");") + end + + test "replacing an element with a partial in an RJS template should pick the HTML template over the JS one" do + get :index_html + assert_response("$(\"customer\").update(\"HTML Partial\");") + end + + test "replacing an element with a partial in an RJS template with a locale should pick the localed HTML template" do + get :index_locale, :format => :js + assert_response("$(\"customer\").update(\"Danish HTML Partial\");") + end + + end +end \ No newline at end of file diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 2ed11aa3c0..45e3dc6f15 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -46,29 +46,6 @@ module RenderTestCases I18n.locale = old_locale end - def test_render_implicit_html_template_from_xhr_request - old_format = @view.formats - pending do - @view.formats = [:js] - assert_equal "Hello HTML!", @view.render(:file => "test/render_implicit_html_template_from_xhr_request") - end - ensure - @view.formats = old_format - end - - def test_render_implicit_html_template_from_xhr_request_with_localization - old_locale = I18n.locale - old_format = @view.formats - pending do - I18n.locale = :da - @view.formats = [:js] - assert_equal "Hey HTML!\n", @view.render(:file => "test/render_implicit_html_template_from_xhr_request") - end - ensure - I18n.locale = old_locale - @view.formats = old_format - end - def test_render_file_at_top_level assert_equal 'Elastica', @view.render(:file => '/shared') end -- cgit v1.2.3 From 487312515c09c5dbc7306329805f2eebfed1d630 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 12:36:15 -0700 Subject: RJS doesn't render with an HTML layout by default --- actionpack/lib/action_controller/base/compatibility.rb | 1 + actionpack/test/controller/mime_responds_test.rb | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/base/compatibility.rb index 085db284e4..cd4b72b1c6 100644 --- a/actionpack/lib/action_controller/base/compatibility.rb +++ b/actionpack/lib/action_controller/base/compatibility.rb @@ -124,6 +124,7 @@ module ActionController super rescue ActionView::MissingTemplate _find_by_parts(_layout({}), {}) + nil end def performed? diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 04d6c4173a..c9994ee013 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -375,11 +375,9 @@ class MimeControllerTest < ActionController::TestCase end def test_rjs_type_skips_layout - pending(:new_base) do - @request.accept = "text/javascript" - get :all_types_with_layout - assert_equal 'RJS for all_types_with_layout', @response.body - end + @request.accept = "text/javascript" + get :all_types_with_layout + assert_equal 'RJS for all_types_with_layout', @response.body end def test_html_type_with_layout -- cgit v1.2.3 From 6927f1119b65852807f146da3d0a6354cdede02f Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 12:47:34 -0700 Subject: Finish making pending tests pass --- actionpack/test/controller/render_test.rb | 14 +++++++------- .../test/render_implicit_js_template_without_layout.js.erb | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb (limited to 'actionpack') diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9e42d1738a..9934639d21 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -301,6 +301,9 @@ class TestController < ActionController::Base def render_implicit_html_template_from_xhr_request end + def render_implicit_js_template_without_layout + end + def formatted_html_erb end @@ -928,16 +931,13 @@ class RenderTest < ActionController::TestCase end def test_should_implicitly_render_html_template_from_xhr_request - pending - # xhr :get, :render_implicit_html_template_from_xhr_request - # assert_equal "XHR!\nHello HTML!", @response.body + xhr :get, :render_implicit_html_template_from_xhr_request + assert_equal "XHR!\nHello HTML!", @response.body end def test_should_implicitly_render_js_template_without_layout - pending do - get :render_implicit_js_template_without_layout, :format => :js - assert_no_match %r{}, @response.body - end + get :render_implicit_js_template_without_layout, :format => :js + assert_no_match %r{}, @response.body end def test_should_render_formatted_template diff --git a/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb b/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb new file mode 100644 index 0000000000..892ae5eca2 --- /dev/null +++ b/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb @@ -0,0 +1 @@ +alert('hello'); \ No newline at end of file -- cgit v1.2.3 From d8f352e970ec86e8b791f9994465a3678a44281f Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 15:32:55 -0700 Subject: Rename ActionView::Template::Path ActionView::Resolver --- actionpack/lib/action_view.rb | 28 +-- actionpack/lib/action_view/paths.rb | 2 +- actionpack/lib/action_view/template/path.rb | 249 ++++++++++----------- .../test/abstract_controller/layouts_test.rb | 2 +- actionpack/test/lib/fixture_template.rb | 39 +--- actionpack/test/new_base/content_type_test.rb | 2 +- actionpack/test/new_base/etag_test.rb | 2 +- actionpack/test/new_base/render_action_test.rb | 8 +- .../test/new_base/render_implicit_action_test.rb | 2 +- actionpack/test/new_base/render_layout_test.rb | 6 +- actionpack/test/new_base/render_partial_test.rb | 2 +- actionpack/test/new_base/render_rjs_test.rb | 2 +- actionpack/test/new_base/render_template_test.rb | 6 +- actionpack/test/new_base/render_test.rb | 2 +- actionpack/test/new_base/render_text_test.rb | 4 +- actionpack/test/new_base/render_xml_test.rb | 2 +- .../test/template/compiled_templates_test.rb | 2 +- actionpack/test/template/render_test.rb | 6 +- 18 files changed, 164 insertions(+), 202 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 94138097e3..b4eddca378 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -33,21 +33,21 @@ module ActionView [Base, InlineTemplate, TemplateError] end - autoload :Base, 'action_view/base' - autoload :Helpers, 'action_view/helpers' - autoload :InlineTemplate, 'action_view/template/inline' - autoload :Partials, 'action_view/render/partials' - autoload :Path, 'action_view/template/path' - autoload :PathSet, 'action_view/paths' - autoload :Rendering, 'action_view/render/rendering' - autoload :Renderable, 'action_view/template/renderable' + autoload :Base, 'action_view/base' + autoload :Helpers, 'action_view/helpers' + autoload :InlineTemplate, 'action_view/template/inline' + autoload :Partials, 'action_view/render/partials' + autoload :Resolver, 'action_view/template/path' + autoload :PathSet, 'action_view/paths' + 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 :TextTemplate, 'action_view/template/text' - autoload :Helpers, 'action_view/helpers' + 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 :TextTemplate, 'action_view/template/text' + autoload :Helpers, 'action_view/helpers' end class ERB diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 95c56faf9c..074b475819 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -3,7 +3,7 @@ module ActionView #:nodoc: def self.type_cast(obj) if obj.is_a?(String) cache = !defined?(Rails) || !Rails.respond_to?(:configuration) || Rails.configuration.cache_classes - Template::FileSystemPathWithFallback.new(obj, :cache => cache) + FileSystemResolverWithFallback.new(obj, :cache => cache) else obj end diff --git a/actionpack/lib/action_view/template/path.rb b/actionpack/lib/action_view/template/path.rb index c3837a9f07..d15f53a11b 100644 --- a/actionpack/lib/action_view/template/path.rb +++ b/actionpack/lib/action_view/template/path.rb @@ -1,151 +1,150 @@ require "pathname" +require "action_view/template/template" module ActionView - class Template - # Abstract super class - class Path - def initialize(options) - @cache = options[:cache] - @cached = {} - end - - # Normalizes the arguments and passes it on to find_template - def find_by_parts(*args) - find_all_by_parts(*args).first - end - - def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) - details[:locales] = [I18n.locale] - name = name.to_s.gsub(handler_matcher, '').split("/") - find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) - end + # Abstract superclass + class Resolver + def initialize(options) + @cache = options[:cache] + @cached = {} + end + + # Normalizes the arguments and passes it on to find_template + def find_by_parts(*args) + find_all_by_parts(*args).first + end - private - - # This is what child classes implement. No defaults are needed - # because Path guarentees that the arguments are present and - # normalized. - def find_templates(name, details, prefix, partial) - raise NotImplementedError - end - - def valid_handlers - @valid_handlers ||= TemplateHandlers.extensions - end - - def handler_matcher - @handler_matcher ||= begin - e = valid_handlers.join('|') - /\.(?:#{e})$/ - end - end - - def handler_glob - e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join - "{#{e}}" - end - - def formats_glob - @formats_glob ||= begin - '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' - end - end - - def cached(key) - return yield unless @cache - return @cached[key] if @cached.key?(key) - @cached[key] = yield - end + def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) + details[:locales] = [I18n.locale] + name = name.to_s.gsub(handler_matcher, '').split("/") + find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) end - class FileSystemPath < Path + private + + # This is what child classes implement. No defaults are needed + # because Resolver guarantees that the arguments are present and + # normalized. + def find_templates(name, details, prefix, partial) + raise NotImplementedError + end - def initialize(path, options = {}) - raise ArgumentError, "path already is a Path class" if path.is_a?(Path) - super(options) - @path = Pathname.new(path).expand_path + def valid_handlers + @valid_handlers ||= TemplateHandlers.extensions + end + + def handler_matcher + @handler_matcher ||= begin + e = valid_handlers.join('|') + /\.(?:#{e})$/ end + end - # TODO: This is the currently needed API. Make this suck less - # ==== - attr_reader :path + def handler_glob + e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join + "{#{e}}" + end - def to_s - path.to_s + def formats_glob + @formats_glob ||= begin + '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' end + end - def to_str - path.to_s - end + def cached(key) + return yield unless @cache + return @cached[key] if @cached.key?(key) + @cached[key] = yield + end + end - def ==(path) - to_str == path.to_str - end + class FileSystemResolver < Resolver - def eql?(path) - to_str == path.to_str - end - # ==== - - def find_templates(name, details, prefix, partial, root = "#{@path}/") - if glob = details_to_glob(name, details, prefix, partial, root) - cached(glob) do - Dir[glob].map do |path| - next if File.directory?(path) - source = File.read(path) - identifier = Pathname.new(path).expand_path.to_s - - Template.new(source, identifier, *path_to_details(path)) - end.compact - end + def initialize(path, options = {}) + raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) + super(options) + @path = Pathname.new(path).expand_path + end + + # TODO: This is the currently needed API. Make this suck less + # ==== + attr_reader :path + + def to_s + path.to_s + end + + def to_str + path.to_s + end + + def ==(path) + to_str == path.to_str + end + + def eql?(path) + to_str == path.to_str + end + # ==== + + def find_templates(name, details, prefix, partial, root = "#{@path}/") + if glob = details_to_glob(name, details, prefix, partial, root) + cached(glob) do + Dir[glob].map do |path| + next if File.directory?(path) + source = File.read(path) + identifier = Pathname.new(path).expand_path.to_s + + Template.new(source, identifier, *path_to_details(path)) + end.compact end end - - private + end - # :api: plugin - def details_to_glob(name, details, prefix, partial, root) - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - - extensions = "" - [:locales, :formats].each do |k| - extensions << if exts = details[k] - '{' + exts.map {|e| ".#{e},"}.join + '}' - else - k == :formats ? formats_glob : '' - end + private + + # :api: plugin + def details_to_glob(name, details, prefix, partial, root) + path = "" + path << "#{prefix}/" unless prefix.empty? + path << (partial ? "_#{name}" : name) + + extensions = "" + [:locales, :formats].each do |k| + extensions << if exts = details[k] + '{' + exts.map {|e| ".#{e},"}.join + '}' + else + k == :formats ? formats_glob : '' end - - "#{root}#{path}#{extensions}#{handler_glob}" end - # TODO: fix me - # :api: plugin - def path_to_details(path) - # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') - partial = m[1] == '_' - details = (m[2]||"").split('.').reject { |e| e.empty? } - handler = Template.handler_class_for_extension(m[3]) - - format = Mime[details.last] && details.pop.to_sym - locale = details.last && details.pop.to_sym - - return handler, :format => format, :locale => locale, :partial => partial - end - end + "#{root}#{path}#{extensions}#{handler_glob}" end - class FileSystemPathWithFallback < FileSystemPath - - def find_templates(name, details, prefix, partial) - templates = super - return super(name, details, prefix, partial, '') if templates.empty? - templates + # TODO: fix me + # :api: plugin + def path_to_details(path) + # [:erb, :format => :html, :locale => :en, :partial => true/false] + if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + partial = m[1] == '_' + details = (m[2]||"").split('.').reject { |e| e.empty? } + handler = Template.handler_class_for_extension(m[3]) + + format = Mime[details.last] && details.pop.to_sym + locale = details.last && details.pop.to_sym + + return handler, :format => format, :locale => locale, :partial => partial end - end end + + class FileSystemResolverWithFallback < FileSystemResolver + + def find_templates(name, details, prefix, partial) + templates = super + return super(name, details, prefix, partial, '') if templates.empty? + templates + end + + end end \ No newline at end of file diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 37eb55ec98..b28df7743f 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -9,7 +9,7 @@ module AbstractControllerTests include AbstractController::Renderer include AbstractController::Layouts - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "layouts/hello.erb" => "With String <%= yield %>", "layouts/hello_override.erb" => "With Override <%= yield %>", "layouts/abstract_controller_tests/layouts/with_string_implied_child.erb" => diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb index 4ea451879c..5cf414a1c6 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/test/lib/fixture_template.rb @@ -1,6 +1,5 @@ module ActionView #:nodoc: -class Template - class FixturePath < Path + class FixtureResolver < Resolver def initialize(hash = {}, options = {}) super(options) @hash = hash @@ -65,40 +64,4 @@ class Template end end end - - - # class FixtureTemplate < Template - # class FixturePath < Template::Path - # def initialize(hash = {}) - # @hash = {} - # - # hash.each do |k, v| - # @hash[k.sub(/\.\w+$/, '')] = FixtureTemplate.new(v, k.split("/").last, self) - # end - # - # super("fixtures://root") - # end - # - # def find_template(path) - # @hash[path] - # end - # end - # - # def initialize(body, *args) - # @body = body - # super(*args) - # end - # - # def source - # @body - # end - # - # private - # - # def find_full_path(path, load_paths) - # return '/', path - # end - # - # end -end end \ No newline at end of file diff --git a/actionpack/test/new_base/content_type_test.rb b/actionpack/test/new_base/content_type_test.rb index 82b817a5a3..cfc03a3024 100644 --- a/actionpack/test/new_base/content_type_test.rb +++ b/actionpack/test/new_base/content_type_test.rb @@ -19,7 +19,7 @@ module ContentType class ImpliedController < ActionController::Base # Template's mime type is used if no content_type is specified - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "content_type/implied/i_am_html_erb.html.erb" => "Hello world!", "content_type/implied/i_am_xml_erb.xml.erb" => "Hello world!", "content_type/implied/i_am_html_builder.html.builder" => "xml.p 'Hello'", diff --git a/actionpack/test/new_base/etag_test.rb b/actionpack/test/new_base/etag_test.rb index a40d3c936a..3a69e7dac4 100644 --- a/actionpack/test/new_base/etag_test.rb +++ b/actionpack/test/new_base/etag_test.rb @@ -2,7 +2,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module Etags class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "etags/basic/base.html.erb" => "Hello from without_layout.html.erb", "layouts/etags.html.erb" => "teh <%= yield %> tagz" )] diff --git a/actionpack/test/new_base/render_action_test.rb b/actionpack/test/new_base/render_action_test.rb index 4402eadf42..dfa7cc2141 100644 --- a/actionpack/test/new_base/render_action_test.rb +++ b/actionpack/test/new_base/render_action_test.rb @@ -3,7 +3,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module RenderAction # This has no layout and it works class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render_action/basic/hello_world.html.erb" => "Hello world!" )] @@ -117,7 +117,7 @@ module RenderActionWithApplicationLayout # # ==== Render actions with layouts ==== class BasicController < ::ApplicationController # Set the view path to an application view structure with layouts - self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = self.view_paths = [ActionView::FixtureResolver.new( "render_action_with_application_layout/basic/hello_world.html.erb" => "Hello World!", "render_action_with_application_layout/basic/hello.html.builder" => "xml.p 'Omg'", "layouts/application.html.erb" => "OHAI <%= yield %> KTHXBAI", @@ -202,7 +202,7 @@ end module RenderActionWithControllerLayout class BasicController < ActionController::Base - self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = self.view_paths = [ActionView::FixtureResolver.new( "render_action_with_controller_layout/basic/hello_world.html.erb" => "Hello World!", "layouts/render_action_with_controller_layout/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI" )] @@ -263,7 +263,7 @@ end module RenderActionWithBothLayouts class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new({ + self.view_paths = [ActionView::FixtureResolver.new({ "render_action_with_both_layouts/basic/hello_world.html.erb" => "Hello World!", "layouts/application.html.erb" => "OHAI <%= yield %> KTHXBAI", "layouts/render_action_with_both_layouts/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI" diff --git a/actionpack/test/new_base/render_implicit_action_test.rb b/actionpack/test/new_base/render_implicit_action_test.rb index 2846df48da..fd96e1955f 100644 --- a/actionpack/test/new_base/render_implicit_action_test.rb +++ b/actionpack/test/new_base/render_implicit_action_test.rb @@ -2,7 +2,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module RenderImplicitAction class SimpleController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!" )] diff --git a/actionpack/test/new_base/render_layout_test.rb b/actionpack/test/new_base/render_layout_test.rb index f32c60d683..279b807a5f 100644 --- a/actionpack/test/new_base/render_layout_test.rb +++ b/actionpack/test/new_base/render_layout_test.rb @@ -2,7 +2,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module ControllerLayouts class ImplicitController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "layouts/application.html.erb" => "OMG <%= yield %> KTHXBAI", "layouts/override.html.erb" => "Override! <%= yield %>", "basic.html.erb" => "Hello world!", @@ -26,7 +26,7 @@ module ControllerLayouts end class ImplicitNameController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "layouts/controller_layouts/implicit_name.html.erb" => "OMGIMPLICIT <%= yield %> KTHXBAI", "basic.html.erb" => "Hello world!" )] @@ -68,7 +68,7 @@ module ControllerLayouts end class MismatchFormatController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "layouts/application.html.erb" => "<%= yield %>", "controller_layouts/mismatch_format/index.js.rjs" => "page[:test].omg", "controller_layouts/mismatch_format/implicit.rjs" => "page[:test].omg" diff --git a/actionpack/test/new_base/render_partial_test.rb b/actionpack/test/new_base/render_partial_test.rb index 3a300afe5c..bbb98a0c01 100644 --- a/actionpack/test/new_base/render_partial_test.rb +++ b/actionpack/test/new_base/render_partial_test.rb @@ -4,7 +4,7 @@ module RenderPartial class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render_partial/basic/_basic.html.erb" => "OMG!", "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>" )] diff --git a/actionpack/test/new_base/render_rjs_test.rb b/actionpack/test/new_base/render_rjs_test.rb index fdf3556e8e..bd4c87b3bf 100644 --- a/actionpack/test/new_base/render_rjs_test.rb +++ b/actionpack/test/new_base/render_rjs_test.rb @@ -4,7 +4,7 @@ module RenderRjs class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render_rjs/basic/index.js.rjs" => "page[:customer].replace_html render(:partial => 'customer')", "render_rjs/basic/index_html.js.rjs" => "page[:customer].replace_html :partial => 'customer'", "render_rjs/basic/_customer.js.erb" => "JS Partial", diff --git a/actionpack/test/new_base/render_template_test.rb b/actionpack/test/new_base/render_template_test.rb index face5b7571..94ea38fc7b 100644 --- a/actionpack/test/new_base/render_template_test.rb +++ b/actionpack/test/new_base/render_template_test.rb @@ -3,7 +3,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module RenderTemplate class WithoutLayoutController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "test/basic.html.erb" => "Hello from basic.html.erb", "shared.html.erb" => "Elastica", "locals.html.erb" => "The secret is <%= secret %>", @@ -79,7 +79,7 @@ module RenderTemplate end class WithLayoutController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "test/basic.html.erb" => "Hello from basic.html.erb", "shared.html.erb" => "Elastica", "layouts/application.html.erb" => "<%= yield %>, I'm here!", @@ -148,7 +148,7 @@ module RenderTemplate module Compatibility class WithoutLayoutController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "test/basic.html.erb" => "Hello from basic.html.erb", "shared.html.erb" => "Elastica" )] diff --git a/actionpack/test/new_base/render_test.rb b/actionpack/test/new_base/render_test.rb index ed3d50fa0b..5783b4766a 100644 --- a/actionpack/test/new_base/render_test.rb +++ b/actionpack/test/new_base/render_test.rb @@ -2,7 +2,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module Render class BlankRenderController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render/blank_render/index.html.erb" => "Hello world!", "render/blank_render/access_request.html.erb" => "The request: <%= request.method.to_s.upcase %>", "render/blank_render/access_action_name.html.erb" => "Action Name: <%= action_name %>", diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/new_base/render_text_test.rb index 4a90eaac40..84f77432c9 100644 --- a/actionpack/test/new_base/render_text_test.rb +++ b/actionpack/test/new_base/render_text_test.rb @@ -2,7 +2,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module RenderText class SimpleController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new] + self.view_paths = [ActionView::FixtureResolver.new] def index render :text => "hello david" @@ -10,7 +10,7 @@ module RenderText end class WithLayoutController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "layouts/application.html.erb" => "<%= yield %>, I'm here!", "layouts/greetings.html.erb" => "<%= yield %>, I wish thee well.", "layouts/ivar.html.erb" => "<%= yield %>, <%= @ivar %>" diff --git a/actionpack/test/new_base/render_xml_test.rb b/actionpack/test/new_base/render_xml_test.rb index e6c40b1533..a3890ddfb2 100644 --- a/actionpack/test/new_base/render_xml_test.rb +++ b/actionpack/test/new_base/render_xml_test.rb @@ -4,7 +4,7 @@ module RenderXml # This has no layout and it works class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( + self.view_paths = [ActionView::FixtureResolver.new( "render_xml/basic/with_render_erb" => "Hello world!" )] end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index b29b03f99d..9c268aef27 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -41,7 +41,7 @@ class CompiledTemplatesTest < Test::Unit::TestCase end def render_without_cache(*args) - path = ActionView::Template::FileSystemPathWithFallback.new(FIXTURE_LOAD_PATH) + path = ActionView::FileSystemResolverWithFallback.new(FIXTURE_LOAD_PATH) view_paths = ActionView::Base.process_view_paths(path) ActionView::Base.new(view_paths, {}).render(*args) end diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 45e3dc6f15..7f30ae88a1 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -255,7 +255,7 @@ class CachedViewRenderTest < ActiveSupport::TestCase # Ensure view path cache is primed def setup view_paths = ActionController::Base.view_paths - assert_equal ActionView::Template::FileSystemPathWithFallback, view_paths.first.class + assert_equal ActionView::FileSystemResolverWithFallback, view_paths.first.class setup_view(view_paths) end end @@ -266,9 +266,9 @@ class LazyViewRenderTest < ActiveSupport::TestCase # Test the same thing as above, but make sure the view path # is not eager loaded def setup - path = ActionView::Template::FileSystemPathWithFallback.new(FIXTURE_LOAD_PATH) + path = ActionView::FileSystemResolverWithFallback.new(FIXTURE_LOAD_PATH) view_paths = ActionView::Base.process_view_paths(path) - assert_equal ActionView::Template::FileSystemPathWithFallback, view_paths.first.class + assert_equal ActionView::FileSystemResolverWithFallback, view_paths.first.class setup_view(view_paths) end end -- cgit v1.2.3 From 251a6e492c72d8aea713b7dabd245d68301e455d Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 15:39:41 -0700 Subject: Rename path.rb to resolver.rb --- actionpack/lib/action_view.rb | 2 +- actionpack/lib/action_view/template/path.rb | 150 ------------------------ actionpack/lib/action_view/template/resolver.rb | 150 ++++++++++++++++++++++++ actionpack/lib/action_view/template/template.rb | 2 +- 4 files changed, 152 insertions(+), 152 deletions(-) delete mode 100644 actionpack/lib/action_view/template/path.rb create mode 100644 actionpack/lib/action_view/template/resolver.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index b4eddca378..3e3b68c71d 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -37,7 +37,7 @@ module ActionView autoload :Helpers, 'action_view/helpers' autoload :InlineTemplate, 'action_view/template/inline' autoload :Partials, 'action_view/render/partials' - autoload :Resolver, 'action_view/template/path' + autoload :Resolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' autoload :Rendering, 'action_view/render/rendering' autoload :Renderable, 'action_view/template/renderable' diff --git a/actionpack/lib/action_view/template/path.rb b/actionpack/lib/action_view/template/path.rb deleted file mode 100644 index d15f53a11b..0000000000 --- a/actionpack/lib/action_view/template/path.rb +++ /dev/null @@ -1,150 +0,0 @@ -require "pathname" -require "action_view/template/template" - -module ActionView - # Abstract superclass - class Resolver - def initialize(options) - @cache = options[:cache] - @cached = {} - end - - # Normalizes the arguments and passes it on to find_template - def find_by_parts(*args) - find_all_by_parts(*args).first - end - - def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) - details[:locales] = [I18n.locale] - name = name.to_s.gsub(handler_matcher, '').split("/") - find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) - end - - private - - # This is what child classes implement. No defaults are needed - # because Resolver guarantees that the arguments are present and - # normalized. - def find_templates(name, details, prefix, partial) - raise NotImplementedError - end - - def valid_handlers - @valid_handlers ||= TemplateHandlers.extensions - end - - def handler_matcher - @handler_matcher ||= begin - e = valid_handlers.join('|') - /\.(?:#{e})$/ - end - end - - def handler_glob - e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join - "{#{e}}" - end - - def formats_glob - @formats_glob ||= begin - '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' - end - end - - def cached(key) - return yield unless @cache - return @cached[key] if @cached.key?(key) - @cached[key] = yield - end - end - - class FileSystemResolver < Resolver - - def initialize(path, options = {}) - raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) - super(options) - @path = Pathname.new(path).expand_path - end - - # TODO: This is the currently needed API. Make this suck less - # ==== - attr_reader :path - - def to_s - path.to_s - end - - def to_str - path.to_s - end - - def ==(path) - to_str == path.to_str - end - - def eql?(path) - to_str == path.to_str - end - # ==== - - def find_templates(name, details, prefix, partial, root = "#{@path}/") - if glob = details_to_glob(name, details, prefix, partial, root) - cached(glob) do - Dir[glob].map do |path| - next if File.directory?(path) - source = File.read(path) - identifier = Pathname.new(path).expand_path.to_s - - Template.new(source, identifier, *path_to_details(path)) - end.compact - end - end - end - - private - - # :api: plugin - def details_to_glob(name, details, prefix, partial, root) - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - - extensions = "" - [:locales, :formats].each do |k| - extensions << if exts = details[k] - '{' + exts.map {|e| ".#{e},"}.join + '}' - else - k == :formats ? formats_glob : '' - end - end - - "#{root}#{path}#{extensions}#{handler_glob}" - end - - # TODO: fix me - # :api: plugin - def path_to_details(path) - # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') - partial = m[1] == '_' - details = (m[2]||"").split('.').reject { |e| e.empty? } - handler = Template.handler_class_for_extension(m[3]) - - format = Mime[details.last] && details.pop.to_sym - locale = details.last && details.pop.to_sym - - return handler, :format => format, :locale => locale, :partial => partial - end - end - end - - class FileSystemResolverWithFallback < FileSystemResolver - - def find_templates(name, details, prefix, partial) - templates = super - return super(name, details, prefix, partial, '') if templates.empty? - templates - end - - end -end \ No newline at end of file diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb new file mode 100644 index 0000000000..d15f53a11b --- /dev/null +++ b/actionpack/lib/action_view/template/resolver.rb @@ -0,0 +1,150 @@ +require "pathname" +require "action_view/template/template" + +module ActionView + # Abstract superclass + class Resolver + def initialize(options) + @cache = options[:cache] + @cached = {} + end + + # Normalizes the arguments and passes it on to find_template + def find_by_parts(*args) + find_all_by_parts(*args).first + end + + def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) + details[:locales] = [I18n.locale] + name = name.to_s.gsub(handler_matcher, '').split("/") + find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) + end + + private + + # This is what child classes implement. No defaults are needed + # because Resolver guarantees that the arguments are present and + # normalized. + def find_templates(name, details, prefix, partial) + raise NotImplementedError + end + + def valid_handlers + @valid_handlers ||= TemplateHandlers.extensions + end + + def handler_matcher + @handler_matcher ||= begin + e = valid_handlers.join('|') + /\.(?:#{e})$/ + end + end + + def handler_glob + e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join + "{#{e}}" + end + + def formats_glob + @formats_glob ||= begin + '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' + end + end + + def cached(key) + return yield unless @cache + return @cached[key] if @cached.key?(key) + @cached[key] = yield + end + end + + class FileSystemResolver < Resolver + + def initialize(path, options = {}) + raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) + super(options) + @path = Pathname.new(path).expand_path + end + + # TODO: This is the currently needed API. Make this suck less + # ==== + attr_reader :path + + def to_s + path.to_s + end + + def to_str + path.to_s + end + + def ==(path) + to_str == path.to_str + end + + def eql?(path) + to_str == path.to_str + end + # ==== + + def find_templates(name, details, prefix, partial, root = "#{@path}/") + if glob = details_to_glob(name, details, prefix, partial, root) + cached(glob) do + Dir[glob].map do |path| + next if File.directory?(path) + source = File.read(path) + identifier = Pathname.new(path).expand_path.to_s + + Template.new(source, identifier, *path_to_details(path)) + end.compact + end + end + end + + private + + # :api: plugin + def details_to_glob(name, details, prefix, partial, root) + path = "" + path << "#{prefix}/" unless prefix.empty? + path << (partial ? "_#{name}" : name) + + extensions = "" + [:locales, :formats].each do |k| + extensions << if exts = details[k] + '{' + exts.map {|e| ".#{e},"}.join + '}' + else + k == :formats ? formats_glob : '' + end + end + + "#{root}#{path}#{extensions}#{handler_glob}" + end + + # TODO: fix me + # :api: plugin + def path_to_details(path) + # [:erb, :format => :html, :locale => :en, :partial => true/false] + if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + partial = m[1] == '_' + details = (m[2]||"").split('.').reject { |e| e.empty? } + handler = Template.handler_class_for_extension(m[3]) + + format = Mime[details.last] && details.pop.to_sym + locale = details.last && details.pop.to_sym + + return handler, :format => format, :locale => locale, :partial => partial + end + end + end + + class FileSystemResolverWithFallback < FileSystemResolver + + def find_templates(name, details, prefix, partial) + templates = super + return super(name, details, prefix, partial, '') if templates.empty? + templates + end + + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 53aaa3dded..fac50cd692 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -2,7 +2,7 @@ # This is so that templates compiled in this file are UTF-8 require 'set' -require "action_view/template/path" +require "action_view/template/resolver" module ActionView class Template -- cgit v1.2.3 From 8fdf3d7890d2e58508e028c93d3797f21a774dbc Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 16:14:05 -0700 Subject: Remove some defined?(Http) checks --- .../base/filter_parameter_logging.rb | 5 +--- actionpack/lib/action_controller/base/flash.rb | 27 ++-------------------- .../lib/action_controller/base/mime_responds.rb | 5 +--- .../base/request_forgery_protection.rb | 18 ++++++--------- .../lib/action_controller/base/verification.rb | 4 +--- .../lib/action_controller/caching/actions.rb | 9 +------- actionpack/test/abstract_unit.rb | 1 + actionpack/test/lib/controller/fake_models.rb | 2 ++ 8 files changed, 16 insertions(+), 55 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb index 8370ba6fc0..26cd03f277 100644 --- a/actionpack/lib/action_controller/base/filter_parameter_logging.rb +++ b/actionpack/lib/action_controller/base/filter_parameter_logging.rb @@ -2,10 +2,7 @@ module ActionController module FilterParameterLogging extend ActiveSupport::Concern - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Logger - end + include AbstractController::Logger included do include InstanceMethodsForNewBase diff --git a/actionpack/lib/action_controller/base/flash.rb b/actionpack/lib/action_controller/base/flash.rb index 42c6e430ca..cac14175d9 100644 --- a/actionpack/lib/action_controller/base/flash.rb +++ b/actionpack/lib/action_controller/base/flash.rb @@ -33,14 +33,7 @@ module ActionController #:nodoc: included do # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include InstanceMethodsForNewBase - else - include InstanceMethodsForBase - - alias_method_chain :perform_action, :flash - alias_method_chain :reset_session, :flash - end + include InstanceMethods end class FlashNow #:nodoc: @@ -148,23 +141,7 @@ module ActionController #:nodoc: end end - module InstanceMethodsForBase #:nodoc: - protected - def perform_action_with_flash - perform_action_without_flash - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session_with_flash - reset_session_without_flash - remove_instance_variable(:@_flash) if defined?(@_flash) - end - end - - module InstanceMethodsForNewBase #:nodoc: + module InstanceMethods #:nodoc: protected def process_action(method_name) super diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb index c0aa58a6c2..ed0d58dba1 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -120,10 +120,7 @@ module ActionController #:nodoc: @responses[mime_type] ||= Proc.new do # TODO: Remove this when new base is merged in - if defined?(Http) - @controller.formats = [mime_type.to_sym] - end - + @controller.formats = [mime_type.to_sym] @controller.content_type = mime_type @controller.template.formats = [mime_type.to_sym] diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb index a470c8eec1..6ba86cd0be 100644 --- a/actionpack/lib/action_controller/base/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb @@ -6,20 +6,16 @@ module ActionController #:nodoc: extend ActiveSupport::Concern # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Helpers, Session - end + include AbstractController::Helpers, Session included do - if defined?(ActionController::Http) - # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ - # sets it to :authenticity_token by default. - cattr_accessor :request_forgery_protection_token + # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ + # sets it to :authenticity_token by default. + cattr_accessor :request_forgery_protection_token - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection - self.allow_forgery_protection = true - end + # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + class_inheritable_accessor :allow_forgery_protection + self.allow_forgery_protection = true helper_method :form_authenticity_token helper_method :protect_against_forgery? diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb index d87b348ed4..7190b6b106 100644 --- a/actionpack/lib/action_controller/base/verification.rb +++ b/actionpack/lib/action_controller/base/verification.rb @@ -3,9 +3,7 @@ module ActionController #:nodoc: extend ActiveSupport::Concern # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include AbstractController::Callbacks, Session, Flash, Renderer - end + include AbstractController::Callbacks, Session, Flash, Renderer # This module provides a class-level method for specifying that certain # actions are guarded against being called without certain prerequisites diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 54148b55d8..4420678df6 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -62,14 +62,7 @@ module ActionController #:nodoc: cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options) - # TODO: Remove this once new base is swapped in. - if defined?(ActionController::Http) - around_filter cache_filter, filter_options - else - around_filter(filter_options) do |controller, action| - cache_filter.filter(controller, action) - end - end + around_filter cache_filter, filter_options end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 1333a9d71a..30e795a7a2 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,5 +1,6 @@ $:.unshift(File.dirname(__FILE__) + '/../lib') $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') +$:.unshift(File.dirname(__FILE__) + '/../../activemodel/lib') $:.unshift(File.dirname(__FILE__) + '/lib') $:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 5e63204bad..9e6f14d373 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -1,3 +1,5 @@ +require "active_model" + class Customer < Struct.new(:name, :id) extend ActiveModel::Naming -- cgit v1.2.3 From 55ee0ba7f5b6b5d2023eb5dcc030946ed589fe53 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 16:23:11 -0700 Subject: Cleaning up if defined?(ActionController::Http) blocks from the pre new base era. --- actionpack/lib/action_controller/base/flash.rb | 53 +++++++++------------- actionpack/lib/action_controller/base/streaming.rb | 5 +- .../lib/action_controller/base/verification.rb | 1 - .../lib/action_controller/caching/actions.rb | 17 ++----- actionpack/lib/action_view/base.rb | 52 ++++++--------------- actionpack/lib/action_view/render/rendering.rb | 1 - .../lib/action_view/template/handlers/builder.rb | 3 +- 7 files changed, 42 insertions(+), 90 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/flash.rb b/actionpack/lib/action_controller/base/flash.rb index cac14175d9..590f9be3ac 100644 --- a/actionpack/lib/action_controller/base/flash.rb +++ b/actionpack/lib/action_controller/base/flash.rb @@ -28,13 +28,7 @@ module ActionController #:nodoc: module Flash extend ActiveSupport::Concern - # TODO : Remove the defined? check when new base is the main base - include Session if defined?(ActionController::Http) - - included do - # TODO : Remove the defined? check when new base is the main base - include InstanceMethods - end + include Session class FlashNow #:nodoc: def initialize(flash) @@ -141,33 +135,30 @@ module ActionController #:nodoc: end end - module InstanceMethods #:nodoc: - protected - def process_action(method_name) - super - if defined? @_flash - @_flash.store(session) - remove_instance_variable(:@_flash) - end - end - - def reset_session - super - remove_instance_variable(:@_flash) if defined?(@_flash) - end + protected + def process_action(method_name) + super + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end end - protected - # Access the contents of the flash. Use flash["notice"] to - # read a notice you put there or flash["notice"] = "hello" - # to put a new one. - def flash #:doc: - if !defined?(@_flash) - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end + def reset_session + super + remove_instance_variable(:@_flash) if defined?(@_flash) + end - @_flash + # Access the contents of the flash. Use flash["notice"] to + # read a notice you put there or flash["notice"] = "hello" + # to put a new one. + def flash #:doc: + if !defined?(@_flash) + @_flash = session["flash"] || FlashHash.new + @_flash.sweep end + + @_flash + end end end diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb index 5c72fc9ad9..70a97ccfec 100644 --- a/actionpack/lib/action_controller/base/streaming.rb +++ b/actionpack/lib/action_controller/base/streaming.rb @@ -4,10 +4,7 @@ module ActionController #:nodoc: module Streaming extend ActiveSupport::Concern - # TODO : Remove the defined? check when new base is the main base - if defined?(ActionController::Http) - include ActionController::Renderer - end + include ActionController::Renderer DEFAULT_SEND_FILE_OPTIONS = { :type => 'application/octet-stream'.freeze, diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb index 7190b6b106..951ae1bee1 100644 --- a/actionpack/lib/action_controller/base/verification.rb +++ b/actionpack/lib/action_controller/base/verification.rb @@ -2,7 +2,6 @@ module ActionController #:nodoc: module Verification #:nodoc: extend ActiveSupport::Concern - # TODO : Remove the defined? check when new base is the main base include AbstractController::Callbacks, Session, Flash, Renderer # This module provides a class-level method for specifying that certain diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 4420678df6..d8a1662acc 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -84,19 +84,10 @@ module ActionController #:nodoc: @options = options end - # TODO: Remove once New Base is merged - if defined?(ActionController::Http) - def filter(controller) - should_continue = before(controller) - yield if should_continue - after(controller) - end - else - def filter(controller, action) - should_continue = before(controller) - action.call if should_continue - after(controller) - end + def filter(controller) + should_continue = before(controller) + yield if should_continue + after(controller) end def before(controller) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 2d8f51300a..1ca6f839a8 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -230,39 +230,24 @@ module ActionView #: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 @controller = controller @helpers = ProxyModule.new(self) self.view_paths = view_paths - - @_first_render = nil - @_current_render = nil end + attr_internal :template attr_reader :view_paths def view_paths=(paths) @view_paths = self.class.process_view_paths(paths) end - # Access the current template being rendered. - # Returns a ActionView::Template object. - def template - @_current_render - end - - def template=(template) #:nodoc: - @_first_render ||= template - @_current_render = template - end - def with_template(current_template) last_template, self.template = template, current_template - old_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols + last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols yield ensure - self.template = last_template - self.formats = old_formats + self.template, self.formats = last_template, last_formats end def punctuate_body!(part) @@ -273,30 +258,21 @@ module ActionView #:nodoc: # Evaluates the local assigns and controller ivars, pushes them to the view. def _evaluate_assigns_and_ivars #:nodoc: - unless @assigns_added - @assigns.each { |key, value| instance_variable_set("@#{key}", value) } - _copy_ivars_from_controller - @assigns_added = true - end + return if @assigns_added + @assigns.each { |key, value| instance_variable_set("@#{key}", value) } + _copy_ivars_from_controller + @assigns_added = true end - private + private - def _copy_ivars_from_controller #:nodoc: - if @controller - variables = @controller.instance_variable_names - variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) - variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) } - end + def _copy_ivars_from_controller #:nodoc: + if @controller + variables = @controller.instance_variable_names + variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) + variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) } end + end - def _set_controller_content_type(content_type) #:nodoc: - # TODO: Remove this method when new base is switched - unless defined?(ActionController::Http) - if controller.respond_to?(:response) - controller.response.content_type ||= content_type - end - end - end end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index fe785e7b20..d120631e5d 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -64,7 +64,6 @@ module ActionView def _render_template(template, local_assigns = {}) with_template(template) do _evaluate_assigns_and_ivars - _set_controller_content_type(template.mime_type) if template.respond_to?(:mime_type) template.render(self, local_assigns) do |*names| if !instance_variable_defined?(:"@content_for_#{names.first}") && diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index f412228752..abe140af0b 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -8,8 +8,7 @@ module ActionView self.default_format = Mime::XML def compile(template) - "_set_controller_content_type(Mime::XML);" + - "xml = ::Builder::XmlMarkup.new(:indent => 2);" + + "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + ";xml.target!;" -- cgit v1.2.3 From 65102c8f1a55c8d73e44b62eed5e689017ace7cd Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 16:51:51 -0700 Subject: Cleaning up more tests and code that needed to work in both old and new base --- actionpack/lib/action_view.rb | 1 + actionpack/test/controller/base_test.rb | 6 +-- actionpack/test/controller/filters_test.rb | 68 +++++++----------------- actionpack/test/controller/helper_test.rb | 19 ++----- actionpack/test/controller/mime_responds_test.rb | 16 +++--- actionpack/test/controller/send_file_test.rb | 4 +- actionpack/test/template/body_parts_test.rb | 13 ++--- 7 files changed, 37 insertions(+), 90 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 3e3b68c71d..27a06db5bb 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -48,6 +48,7 @@ module ActionView autoload :TemplateHandlers, 'action_view/template/handlers' autoload :TextTemplate, 'action_view/template/text' autoload :Helpers, 'action_view/helpers' + autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' end class ERB diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 03fd98a85c..8877057070 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -146,11 +146,7 @@ class PerformActionTest < ActionController::TestCase def test_method_missing_is_not_an_action_name use_controller MethodMissingController - if defined?(ActionController::Http) - assert ! @controller.__send__(:action_method?, 'method_missing') - else - assert ! @controller.__send__(:action_methods).include?('method_missing') - end + assert ! @controller.__send__(:action_method?, 'method_missing') get :method_missing assert_response :success diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index b930ff4997..2da97a9d86 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -9,24 +9,20 @@ class ActionController::Base end unless method_defined?(pending) end - if defined?(ActionController::Http) - def before_filters - filters = _process_action_callbacks.select { |c| c.kind == :before } - filters.map! { |c| c.instance_variable_get(:@raw_filter) } - end + def before_filters + filters = _process_action_callbacks.select { |c| c.kind == :before } + filters.map! { |c| c.instance_variable_get(:@raw_filter) } end end - if defined?(ActionController::Http) - def assigns(key = nil) - assigns = {} - instance_variable_names.each do |ivar| - next if ActionController::Base.protected_instance_variables.include?(ivar) - assigns[ivar[1..-1]] = instance_variable_get(ivar) - end - - key.nil? ? assigns : assigns[key.to_s] + def assigns(key = nil) + assigns = {} + instance_variable_names.each do |ivar| + next if ActionController::Base.protected_instance_variables.include?(ivar) + assigns[ivar[1..-1]] = instance_variable_get(ivar) end + + key.nil? ? assigns : assigns[key.to_s] end end @@ -598,22 +594,11 @@ class FilterTest < ActionController::TestCase assert_equal "before and after", assigns["execution_log"] end - for_tag(:old_base) do - def test_prepending_and_appending_around_filter - controller = test_process(MixedFilterController) - assert_equal " before aroundfilter before procfilter before appended aroundfilter " + - " after appended aroundfilter after aroundfilter after procfilter ", - MixedFilterController.execution_log - end - end - - for_tag(:new_base) do - def test_prepending_and_appending_around_filter - controller = test_process(MixedFilterController) - assert_equal " before aroundfilter before procfilter before appended aroundfilter " + - " after appended aroundfilter after procfilter after aroundfilter ", - MixedFilterController.execution_log - end + def test_prepending_and_appending_around_filter + controller = test_process(MixedFilterController) + assert_equal " before aroundfilter before procfilter before appended aroundfilter " + + " after appended aroundfilter after procfilter after aroundfilter ", + MixedFilterController.execution_log end def test_rendering_breaks_filtering_chain @@ -876,14 +861,6 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert_raise(After) { test_process(controller,'raises_after') } end - for_tag(:old_base) do - def test_with_method - controller = ControllerWithFilterMethod - assert_nothing_raised { test_process(controller,'no_raise') } - assert_raise(After) { test_process(controller,'raises_after') } - end - end - def test_with_proc test_process(ControllerWithProcFilter,'no_raise') assert assigns['before'] @@ -906,18 +883,9 @@ class YieldingAroundFiltersTest < ActionController::TestCase end end - for_tag(:old_base) do - def test_filter_order_with_all_filter_types - test_process(ControllerWithAllTypesOfFilters,'no_raise') - assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after', assigns['ran_filter'].join(' ') - end - end - - for_tag(:new_base) do - def test_filter_order_with_all_filter_types - test_process(ControllerWithAllTypesOfFilters,'no_raise') - assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ') - end + def test_filter_order_with_all_filter_types + test_process(ControllerWithAllTypesOfFilters,'no_raise') + assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ') end def test_filter_order_with_skip_filter_method diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 515c4c9f52..23149fee27 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -127,11 +127,7 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = if defined?(ActionController::Http) - AllHelpersController._helpers.instance_methods.map {|m| m.to_s} - else - AllHelpersController.master_helper_module.instance_methods.map {|m| m.to_s} - end + methods = AllHelpersController._helpers.instance_methods.map {|m| m.to_s} # abc_helper.rb assert methods.include?('bare_a') @@ -147,12 +143,7 @@ class HelperTest < Test::Unit::TestCase @controller_class.helpers_dir = File.dirname(__FILE__) + '/../fixtures/alternate_helpers' # Reload helpers - if defined?(ActionController::Http) - @controller_class._helpers = Module.new - else - @controller_class.master_helper_module = Module.new - end - + @controller_class._helpers = Module.new @controller_class.helper :all # helpers/abc_helper.rb should not be included @@ -184,11 +175,7 @@ class HelperTest < Test::Unit::TestCase end def master_helper_methods - if defined?(ActionController::Http) - @controller_class._helpers.instance_methods.map {|m| m.to_s } - else - @controller_class.master_helper_module.instance_methods.map {|m| m.to_s } - end + @controller_class._helpers.instance_methods.map {|m| m.to_s } end def missing_methods diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index c9994ee013..93ca34c41c 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -519,16 +519,14 @@ class MimeControllerLayoutsTest < ActionController::TestCase assert_equal 'Hello iPhone', @response.body end - for_tag(:old_base) do - def test_format_with_inherited_layouts - @controller = SuperPostController.new + def test_format_with_inherited_layouts + @controller = SuperPostController.new - get :index - assert_equal 'Super Firefox', @response.body + get :index + assert_equal '
Super Firefox
', @response.body - @request.accept = "text/iphone" - get :index - assert_equal '
Super iPhone
', @response.body - end + @request.accept = "text/iphone" + get :index + assert_equal '
Super iPhone
', @response.body end end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 4134da3b9e..d88d5c5dac 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -45,8 +45,8 @@ class SendFileTest < ActionController::TestCase assert_equal file_data, response.body end - for_tag(:old_base) do - def test_file_stream + def test_file_stream + pending do response = nil assert_nothing_raised { response = process('file') } assert_not_nil response diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb index 4e7aa63f96..bac67c1a7d 100644 --- a/actionpack/test/template/body_parts_test.rb +++ b/actionpack/test/template/body_parts_test.rb @@ -4,9 +4,8 @@ class BodyPartsTest < ActionController::TestCase RENDERINGS = [Object.new, Object.new, Object.new] class TestController < ActionController::Base - def performed? - defined?(ActionController::Http) ? true : super - end + def performed?() true end + def index RENDERINGS.each do |rendering| @template.punctuate_body! rendering @@ -19,11 +18,9 @@ class BodyPartsTest < ActionController::TestCase def test_body_parts get :index - pending(:old_base) do - # TestProcess buffers body_parts into body - # TODO: Rewrite test w/o going through process - assert_equal RENDERINGS, @response.body_parts - end + # TestProcess buffers body_parts into body + # TODO: Rewrite test w/o going through process + assert_equal RENDERINGS, @response.body_parts assert_equal RENDERINGS.join, @response.body end end -- cgit v1.2.3 From a9ad763c86e110c280be0b7a763496f9e1204de0 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 18:08:45 -0700 Subject: Drive the final stake through @content_for_*'s heart! --- actionpack/lib/action_controller/legacy/layout.rb | 3 -- actionpack/lib/action_view/base.rb | 4 ++- .../lib/action_view/helpers/capture_helper.rb | 5 ++-- actionpack/lib/action_view/render/partials.rb | 7 ----- actionpack/lib/action_view/render/rendering.rb | 32 ++++++++++------------ actionpack/test/fixtures/layouts/builder.builder | 2 +- actionpack/test/fixtures/layouts/standard.html.erb | 2 +- .../test/fixtures/layouts/talk_from_action.erb | 4 +-- 8 files changed, 23 insertions(+), 36 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/legacy/layout.rb b/actionpack/lib/action_controller/legacy/layout.rb index 3046e082d9..3f3d20b95f 100644 --- a/actionpack/lib/action_controller/legacy/layout.rb +++ b/actionpack/lib/action_controller/legacy/layout.rb @@ -44,9 +44,6 @@ module ActionController #:nodoc: # hello world # // The footer part of this layout # - # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance - # variable. The preferred notation now is to use yield, as documented above. - # # == Accessing shared variables # # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 1ca6f839a8..b994c7b141 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -170,12 +170,13 @@ module ActionView #:nodoc: attr_accessor :base_path, :assigns, :template_extension, :formats attr_accessor :controller + attr_internal :captures attr_accessor :output_buffer class << self delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' - delegate :logger, :to => 'ActionController::Base' + delegate :logger, :to => 'ActionController::Base', :allow_nil => true end @@debug_rjs = false @@ -232,6 +233,7 @@ module ActionView #:nodoc: @assigns = assigns_for_first_render @controller = controller @helpers = ProxyModule.new(self) + @_content_for = Hash.new {|h,k| h[k] = "" } self.view_paths = view_paths end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index b4197479a0..1c29eb3b81 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -116,10 +116,9 @@ module ActionView # named @content_for_#{name_of_the_content_block}. The preferred usage is now # <%= yield :footer %>. def content_for(name, content = nil, &block) - ivar = "@content_for_#{name}" content = capture(&block) if block_given? - instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}") - nil + return @_content_for[name] << content if content + @_content_for[name] end # Use an alternate output buffer for the duration of the block. diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index eacf117bea..87314fff67 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -245,13 +245,6 @@ module ActionView 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 diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index d120631e5d..a720012d1c 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -51,13 +51,12 @@ module ActionView end begin - original_content_for_layout = @content_for_layout if defined?(@content_for_layout) - @content_for_layout = content + old_content, @_content_for[:layout] = @_content_for[:layout], content - @cached_content_for_layout = @content_for_layout + @cached_content_for_layout = @_content_for[:layout] _render_template(layout, locals) ensure - @content_for_layout = original_content_for_layout + @_content_for[:layout] = old_content end end @@ -66,12 +65,11 @@ module ActionView _evaluate_assigns_and_ivars template.render(self, 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 + if !@_content_for.key?(names.first) && @_proc_for_layout + capture(*names, &@_proc_for_layout) + elsif content = @_content_for[names.first || :layout] + content + end end end rescue Exception => e @@ -100,20 +98,18 @@ module ActionView end def _render_template_with_layout(template, layout = nil, options = {}, partial = false) - if controller && logger - logger.info("Rendering #{template.identifier}" + - (options[:status] ? " (#{options[:status]})" : '')) - end - + logger && logger.info("Rendering #{template.identifier}#{' (#{options[:status]})' if options[:status]}") + + locals = options[:locals] || {} + content = if partial object = partial unless partial == true _render_partial_object(template, options, object) else - _render_template(template, options[:locals] || {}) + _render_template(template, locals) end - return content unless layout - _render_content_with_layout(content, layout, options[:locals] || {}) + layout ? _render_content_with_layout(content, layout, locals) : content end end end \ No newline at end of file diff --git a/actionpack/test/fixtures/layouts/builder.builder b/actionpack/test/fixtures/layouts/builder.builder index 729af4b8bc..7c7d4b2dd1 100644 --- a/actionpack/test/fixtures/layouts/builder.builder +++ b/actionpack/test/fixtures/layouts/builder.builder @@ -1,3 +1,3 @@ xml.wrapper do - xml << @content_for_layout + xml << yield end \ No newline at end of file diff --git a/actionpack/test/fixtures/layouts/standard.html.erb b/actionpack/test/fixtures/layouts/standard.html.erb index 368764e6f4..5e6c24fe39 100644 --- a/actionpack/test/fixtures/layouts/standard.html.erb +++ b/actionpack/test/fixtures/layouts/standard.html.erb @@ -1 +1 @@ -<%= @content_for_layout %><%= @variable_for_layout %> \ No newline at end of file +<%= yield %><%= @variable_for_layout %> \ No newline at end of file diff --git a/actionpack/test/fixtures/layouts/talk_from_action.erb b/actionpack/test/fixtures/layouts/talk_from_action.erb index 187aab07a2..bf53fdb785 100644 --- a/actionpack/test/fixtures/layouts/talk_from_action.erb +++ b/actionpack/test/fixtures/layouts/talk_from_action.erb @@ -1,2 +1,2 @@ -<%= @title || @content_for_title %> -<%= @content_for_layout -%> \ No newline at end of file +<%= @title || yield(:title) %> +<%= yield -%> \ No newline at end of file -- cgit v1.2.3 From ca8f8fb0316fe9a2f3a4756a781c9bf4cdcf465c Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 18:15:41 -0700 Subject: Remove one more note about deprecated behavior --- actionpack/lib/action_controller/base/layouts.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/layouts.rb b/actionpack/lib/action_controller/base/layouts.rb index 5c55e9745e..365351b421 100644 --- a/actionpack/lib/action_controller/base/layouts.rb +++ b/actionpack/lib/action_controller/base/layouts.rb @@ -26,9 +26,6 @@ module ActionController # hello world # // The footer part of this layout # - # NOTE: The old notation for rendering the view from a layout was to expose the magic @content_for_layout instance - # variable. The preferred notation now is to use yield, as documented above. - # # == Accessing shared variables # # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with -- cgit v1.2.3 From b4a91db441fa9583df24fb8d3cf0d6906e8359db Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 17 Jun 2009 18:55:10 -0700 Subject: Extract the layout proc into a method, and write documentation explaining what the proc does in various cases. --- .../lib/action_view/helpers/capture_helper.rb | 4 -- actionpack/lib/action_view/render/rendering.rb | 43 +++++++++++++++++++--- 2 files changed, 38 insertions(+), 9 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 1c29eb3b81..a8b5a9dbb9 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -111,10 +111,6 @@ module ActionView # # WARNING: content_for is ignored in caches. So you shouldn't use it # for elements that will be fragment cached. - # - # The deprecated way of accessing a content_for block is to use an instance variable - # named @content_for_#{name_of_the_content_block}. The preferred usage is now - # <%= yield :footer %>. def content_for(name, content = nil, &block) content = capture(&block) if block_given? return @_content_for[name] << content if content diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index a720012d1c..588a64a652 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -60,16 +60,49 @@ module ActionView end end + # You can think of a layout as a method that is called with a block. This method + # returns the block that the layout is called with. If the user calls yield :some_name, + # the block, by default, returns content_for(:some_name). If the user calls yield, + # the default block returns content_for(:layout). + # + # The user can override this default by passing a block to the layout. + # + # ==== Example + # + # # The template + # <% render :layout => "my_layout" do %>Content<% end %> + # + # # The layout + # <% yield %> + # + # In this case, instead of the default block, which would return content_for(:layout), + # this method returns the block that was passed in to render layout, and the response + # would be Content. + # + # Finally, the block can take block arguments, which can be passed in by yield. + # + # ==== Example + # + # # The template + # <% render :layout => "my_layout" do |name| %>Hello <%= customer.name %><% end %> + # + # # The layout + # <% yield Struct.new(:name).new("David") %> + # + # In this case, the layout would receive the block passed into render :layout, + # and the Struct specified in the layout would be passed into the block. The result + # would be Hello David. + def layout_proc(name) + @_default_layout ||= proc { |*names| @_content_for[names.first || :layout] } + !@_content_for.key?(name) && @_proc_for_layout || @_default_layout + end + def _render_template(template, local_assigns = {}) with_template(template) do _evaluate_assigns_and_ivars template.render(self, local_assigns) do |*names| - if !@_content_for.key?(names.first) && @_proc_for_layout - capture(*names, &@_proc_for_layout) - elsif content = @_content_for[names.first || :layout] - content - end + capture(*names, &layout_proc(names.first)) end end rescue Exception => e -- cgit v1.2.3 From 5267addd4f986c89df3d31f35e046abc3b1fbe26 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 17 Jun 2009 19:37:08 -0700 Subject: Use errors[field] instead of errors.on(field) --- .../action_view/helpers/active_record_helper.rb | 59 ++++------------------ .../test/template/active_record_helper_test.rb | 14 ++--- actionpack/test/template/form_helper_test.rb | 4 +- 3 files changed, 20 insertions(+), 57 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 8b70200654..75cc651968 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -122,9 +122,9 @@ module ActionView options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && - (errors = obj.errors.on(method)) + (errors = obj.errors[method]) content_tag("div", - "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}", + "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}", :class => options[:css_class] ) else @@ -247,59 +247,22 @@ module ActionView end end - alias_method :tag_without_error_wrapping, :tag - def tag(name, options) - if object.respond_to?(:errors) && object.errors.respond_to?(:on) - error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name)) - else - tag_without_error_wrapping(name, options) - end - end - - alias_method :content_tag_without_error_wrapping, :content_tag - def content_tag(name, value, options) - if object.respond_to?(:errors) && object.errors.respond_to?(:on) - error_wrapping(content_tag_without_error_wrapping(name, value, options), object.errors.on(@method_name)) - else - content_tag_without_error_wrapping(name, value, options) - end - end - - alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag - def to_date_select_tag(options = {}, html_options = {}) - if object.respond_to?(:errors) && object.errors.respond_to?(:on) - error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) - else - to_date_select_tag_without_error_wrapping(options, html_options) - end - end - - alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag - def to_datetime_select_tag(options = {}, html_options = {}) - if object.respond_to?(:errors) && object.errors.respond_to?(:on) - error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) - else - to_datetime_select_tag_without_error_wrapping(options, html_options) + %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| + without = "#{meth}_without_error_wrapping" + define_method "#{meth}_with_error_wrapping" do |*args| + error_wrapping(send(without, *args)) end + alias_method_chain meth, :error_wrapping end - alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag - def to_time_select_tag(options = {}, html_options = {}) - if object.respond_to?(:errors) && object.errors.respond_to?(:on) - error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name)) + def error_wrapping(html_tag) + if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any? + Base.field_error_proc.call(html_tag, self) else - to_time_select_tag_without_error_wrapping(options, html_options) + html_tag end end - def error_wrapping(html_tag, has_error) - has_error ? Base.field_error_proc.call(html_tag, self) : html_tag - end - - def error_message - object.errors.on(@method_name) - end - def column_type object.send(:column_for_attribute, @method_name).type end diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index b4b8cbe074..4691049a41 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -33,8 +33,8 @@ class ActiveRecordHelperTest < ActionView::TestCase ["Author name can't be empty"] end - def on(field) - "can't be empty" + def [](field) + ["can't be empty"] end end @@ -47,14 +47,14 @@ class ActiveRecordHelperTest < ActionView::TestCase @post = Post.new def @post.errors Class.new { - def on(field) + def [](field) case field.to_s when "author_name" - "can't be empty" + ["can't be empty"] when "body" - true + ['foo'] else - false + [] end end def empty?() false end @@ -85,7 +85,7 @@ class ActiveRecordHelperTest < ActionView::TestCase @user = User.new def @user.errors Class.new { - def on(field) field == "email" end + def [](field) field == "email" ? ['nonempty'] : [] end def empty?() false end def count() 1 end def full_messages() [ "User email can't be empty" ] end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 947668ccc6..f8215132e3 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -50,7 +50,7 @@ class FormHelperTest < ActionView::TestCase @comment = Comment.new def @post.errors() Class.new{ - def on(field); "can't be empty" if field == "author_name"; end + def [](field); field == "author_name" ? ["can't be empty"] : [] end def empty?() false end def count() 1 end def full_messages() [ "Author name can't be empty" ] end @@ -1193,4 +1193,4 @@ class FormHelperTest < ActionView::TestCase def protect_against_forgery? false end -end \ No newline at end of file +end -- cgit v1.2.3 From 497554fd10e8c7efc54a805661907bc39c8fe6e9 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Thu, 18 Jun 2009 11:14:44 -0700 Subject: Fix typo --- actionpack/lib/action_view/render/rendering.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 588a64a652..a9a40c099c 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -84,7 +84,7 @@ module ActionView # ==== Example # # # The template - # <% render :layout => "my_layout" do |name| %>Hello <%= customer.name %><% end %> + # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %> # # # The layout # <% yield Struct.new(:name).new("David") %> -- cgit v1.2.3 From 9f7eaea201b2f408d9effbf82f2731957e284adf Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Thu, 18 Jun 2009 12:08:50 -0700 Subject: Minor ActionView cleanup --- actionpack/lib/action_view/base.rb | 9 ++++----- actionpack/lib/action_view/render/rendering.rb | 4 +--- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index b994c7b141..45184f58fb 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -230,7 +230,7 @@ module ActionView #:nodoc: def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: @formats = formats || [:html] - @assigns = assigns_for_first_render + @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } @controller = controller @helpers = ProxyModule.new(self) @_content_for = Hash.new {|h,k| h[k] = "" } @@ -245,6 +245,7 @@ module ActionView #:nodoc: end def with_template(current_template) + _evaluate_assigns_and_ivars last_template, self.template = template, current_template last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols yield @@ -260,10 +261,7 @@ module ActionView #:nodoc: # Evaluates the local assigns and controller ivars, pushes them to the view. def _evaluate_assigns_and_ivars #:nodoc: - return if @assigns_added - @assigns.each { |key, value| instance_variable_set("@#{key}", value) } - _copy_ivars_from_controller - @assigns_added = true + @assigns_added ||= _copy_ivars_from_controller end private @@ -274,6 +272,7 @@ module ActionView #:nodoc: variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) } end + true end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index a9a40c099c..162e38c484 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -99,14 +99,12 @@ module ActionView def _render_template(template, local_assigns = {}) with_template(template) do - _evaluate_assigns_and_ivars - template.render(self, local_assigns) do |*names| capture(*names, &layout_proc(names.first)) end end rescue Exception => e - if TemplateError === e + if e.is_a?(TemplateError) e.sub_template_of(template) raise e else -- cgit v1.2.3 From 9cb8c812f2a23ab5653a7888740a014a02c97c18 Mon Sep 17 00:00:00 2001 From: Darragh Curran Date: Sun, 21 Jun 2009 17:05:43 +0100 Subject: Add content_for?(:name) helper to check if content_for(:name) is present [#1311 state:resolved] Signed-off-by: Pratik Naik --- .../lib/action_view/helpers/capture_helper.rb | 22 ++++++++++++++++++++++ actionpack/test/template/capture_helper_test.rb | 15 +++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 actionpack/test/template/capture_helper_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index a8b5a9dbb9..c90acc5ac2 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -117,6 +117,28 @@ module ActionView @_content_for[name] end + # content_for? simply checks whether any content has been captured yet using content_for + # Useful to render parts of your layout differently based on what is in your views. + # + # ==== Examples + # + # Perhaps you will use different css in you layout if no content_for :right_column + # + # <%# This is the layout %> + # + # + # My Website + # <%= yield :script %> + # + # + # <%= yield %> + # <%= yield :right_col %> + # + # + def content_for?(name) + @_content_for[name].present? + end + # Use an alternate output buffer for the duration of the block. # Defaults to a new empty string. def with_output_buffer(buf = nil) #:nodoc: diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb new file mode 100644 index 0000000000..2017a18806 --- /dev/null +++ b/actionpack/test/template/capture_helper_test.rb @@ -0,0 +1,15 @@ +require 'abstract_unit' + +class CaptureHelperTest < ActionView::TestCase + def setup + super + @_content_for = Hash.new {|h,k| h[k] = "" } + end + + def test_content_for + assert ! content_for?(:title) + content_for :title, 'title' + assert content_for?(:title) + assert ! content_for?(:something_else) + end +end -- cgit v1.2.3 From 21cd4c0e93fc6ac5497ada787d286c07f627e5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3Futf-8=3Fq=3FMislav=3D20Marohni=3DC4=3D87=3F=3D?= Date: Sun, 21 Jun 2009 17:53:07 +0100 Subject: Fix polymorphic_path doesn't modify options hash [#2099 state:resolved] Signed-off-by: Pratik Naik --- .../action_controller/routing/generation/polymorphic_routes.rb | 3 +-- actionpack/test/activerecord/polymorphic_routes_test.rb | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb index d9b614c237..c6f7de17bd 100644 --- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb @@ -112,8 +112,7 @@ module ActionController # Returns the path component of a URL for the given record. It uses # polymorphic_url with :routing_type => :path. def polymorphic_path(record_or_hash_or_array, options = {}) - options[:routing_type] = :path - polymorphic_url(record_or_hash_or_array, options) + polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path)) end %w(edit new).each do |action| diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb index b9f5be2361..2036d1eeb5 100644 --- a/actionpack/test/activerecord/polymorphic_routes_test.rb +++ b/actionpack/test/activerecord/polymorphic_routes_test.rb @@ -234,10 +234,13 @@ class PolymorphicRoutesTest < ActionController::TestCase with_admin_test_routes do @project.save @task.save + + options = {} object_array = [:admin, @project, @task] - assert_no_difference 'object_array.size' do - polymorphic_url(object_array) - end + original_args = [object_array.dup, options.dup] + + assert_no_difference('object_array.size') { polymorphic_path(object_array, options) } + assert_equal original_args, [object_array, options] end end -- cgit v1.2.3 From 4417a19b035d73eb46a5e06e296a4b1c8091bef1 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 23 Jun 2009 14:04:04 -0700 Subject: Small changes to get 1.9 passing (for the most part) --- actionpack/lib/action_controller/base/http.rb | 2 +- actionpack/lib/action_dispatch/http/mime_type.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/http.rb b/actionpack/lib/action_controller/base/http.rb index 2e73561f93..ec78bc15a8 100644 --- a/actionpack/lib/action_controller/base/http.rb +++ b/actionpack/lib/action_controller/base/http.rb @@ -36,7 +36,7 @@ module ActionController # ==== Returns # String def self.controller_path - @controller_path ||= self.name.sub(/Controller$/, '').underscore + @controller_path ||= name && name.sub(/Controller$/, '').underscore end # Delegates to the class' #controller_path diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index dda6604bdd..27f27e27fe 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -10,7 +10,7 @@ module Mime %w(<< concat shift unshift push pop []= clear compact! collect! delete delete_at delete_if flatten! map! insert reject! reverse! replace slice! sort! uniq!).each do |method| - define_method(method) { @symbols = nil; super } + define_method(method) {|*args| @symbols = nil; super(*args) } end end -- cgit v1.2.3 From 1b844e4bf4462609c6f4f58950ca05cec5cb3929 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Tue, 23 Jun 2009 14:45:27 -0700 Subject: Passes in 1.9 --- actionpack/test/lib/fixture_template.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb index 5cf414a1c6..ee526b5de5 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/test/lib/fixture_template.rb @@ -12,7 +12,7 @@ module ActionView #:nodoc: @hash.select { |k,v| k =~ regexp }.each do |path, source| templates << Template.new(source, path, *path_to_details(path)) end - templates + templates.sort_by {|t| -t.details.values.compact.size } end end end @@ -44,7 +44,7 @@ module ActionView #:nodoc: k == :formats ? formats_regexp : '' end end - + %r'^#{Regexp.escape(path)}#{extensions}#{handler_regexp}$' end @@ -52,7 +52,7 @@ module ActionView #:nodoc: # :api: plugin def path_to_details(path) # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + if m = path.match(%r'(_)?[\w-]+((?:\.[\w-]+)*)\.(\w+)$') partial = m[1] == '_' details = (m[2]||"").split('.').reject { |e| e.empty? } handler = Template.handler_class_for_extension(m[3]) -- cgit v1.2.3 From 92b229e1251d7d3578c3f73e8185972354e76436 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Jun 2009 17:02:04 +0100 Subject: Make performance tests work again --- actionpack/lib/action_controller/testing/performance.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 actionpack/lib/action_controller/testing/performance.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/testing/performance.rb b/actionpack/lib/action_controller/testing/performance.rb deleted file mode 100644 index d88180087d..0000000000 --- a/actionpack/lib/action_controller/testing/performance.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'active_support/testing/performance' -require 'active_support/testing/default' - -module ActionController - # An integration test that runs a code profiler on your test methods. - # Profiling output for combinations of each test method, measurement, and - # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. - class PerformanceTest < ActionController::IntegrationTest - include ActiveSupport::Testing::Performance - include ActiveSupport::Testing::Default - end -end -- cgit v1.2.3 From 97159fad6a69cc0bc6fe504e2063bb98fcf6e42d Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Jun 2009 17:03:04 +0100 Subject: Missed file from the previous commit 92b229e1251d7d3578c3f73e8185972354e76436 --- .../lib/action_controller/testing/performance_test.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 actionpack/lib/action_controller/testing/performance_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/testing/performance_test.rb b/actionpack/lib/action_controller/testing/performance_test.rb new file mode 100644 index 0000000000..d88180087d --- /dev/null +++ b/actionpack/lib/action_controller/testing/performance_test.rb @@ -0,0 +1,15 @@ +require 'active_support/testing/performance' +require 'active_support/testing/default' + +module ActionController + # An integration test that runs a code profiler on your test methods. + # Profiling output for combinations of each test method, measurement, and + # output format are written to your tmp/performance directory. + # + # By default, process_time is measured and both flat and graph_html output + # formats are written, so you'll have two output files per test method. + class PerformanceTest < ActionController::IntegrationTest + include ActiveSupport::Testing::Performance + include ActiveSupport::Testing::Default + end +end -- cgit v1.2.3 From 7aa730440c2143051b46c0857e637100f9367628 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 25 Jun 2009 12:51:21 -0500 Subject: Remove deprecated implicit ivar assignment --- actionpack/lib/action_view/render/partials.rb | 26 -------------------------- actionpack/test/controller/render_test.rb | 7 ------- 2 files changed, 33 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 87314fff67..a80ffe3c20 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -232,18 +232,6 @@ module ActionView ensure @_proc_for_layout = nil 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_layout(layout, options) if layout @@ -253,18 +241,6 @@ module ActionView 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] @@ -290,8 +266,6 @@ module ActionView end 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 diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9934639d21..acb0c895e0 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1268,13 +1268,6 @@ class RenderTest < ActionController::TestCase assert_equal "Hola: PratikHola: Amy", @response.body end - def test_partial_with_implicit_local_assignment - assert_deprecated do - get :partial_with_implicit_local_assignment - assert_equal "Hello: Marcel", @response.body - end - end - def test_render_missing_partial_template assert_raise(ActionView::MissingTemplate) do get :missing_partial -- cgit v1.2.3 From 7837da41a378a69f88763701291a79ef55f14dea Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 25 Jun 2009 14:47:15 -0500 Subject: send_data should set Content-Length as a string --- actionpack/lib/action_controller/base/streaming.rb | 2 +- actionpack/test/controller/send_file_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb index 70a97ccfec..9ff4f25f43 100644 --- a/actionpack/lib/action_controller/base/streaming.rb +++ b/actionpack/lib/action_controller/base/streaming.rb @@ -168,7 +168,7 @@ module ActionController #:nodoc: end headers.merge!( - 'Content-Length' => options[:length], + 'Content-Length' => options[:length].to_s, 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary' ) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index d88d5c5dac..ae32ee5649 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -115,7 +115,7 @@ class SendFileTest < ActionController::TestCase @controller.send(:send_file_headers!, options) h = @controller.headers - assert_equal 1, h['Content-Length'] + assert_equal '1', h['Content-Length'] assert_equal 'image/png', @controller.content_type assert_equal 'disposition; filename="filename"', h['Content-Disposition'] assert_equal 'binary', h['Content-Transfer-Encoding'] -- cgit v1.2.3 From b598baf813a85895e1df4e442599170e57d6445c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 25 Jun 2009 14:52:58 -0500 Subject: ignore absolute tmp directory --- actionpack/test/fixtures/public/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 actionpack/test/fixtures/public/.gitignore (limited to 'actionpack') diff --git a/actionpack/test/fixtures/public/.gitignore b/actionpack/test/fixtures/public/.gitignore new file mode 100644 index 0000000000..0c6759baec --- /dev/null +++ b/actionpack/test/fixtures/public/.gitignore @@ -0,0 +1 @@ +absolute \ No newline at end of file -- cgit v1.2.3 From 8ee60660cec54f008ddaa54a4e8e06d099d8c7f5 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Thu, 25 Jun 2009 14:23:03 -0700 Subject: Try speeding up rails booting --- actionpack/lib/action_view/template/handlers/builder.rb | 3 +-- actionpack/lib/action_view/template/handlers/erb.rb | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index abe140af0b..5f381f7bf0 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -1,5 +1,3 @@ -require 'builder' - module ActionView module TemplateHandlers class Builder < TemplateHandler @@ -8,6 +6,7 @@ module ActionView self.default_format = Mime::XML def compile(template) + require 'builder' "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 21272ef089..e3a7d96941 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -1,4 +1,3 @@ -require 'erb' require 'active_support/core_ext/class/attribute_accessors' module ActionView @@ -16,6 +15,8 @@ module ActionView self.default_format = Mime::HTML def compile(template) + require 'erb' + magic = $1 if template.source =~ /\A(<%#.*coding:\s*(\S+)\s*-?%>)/ erb = "#{magic}<% __in_erb_template=true %>#{template.source}" ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src -- cgit v1.2.3 From 18a97a66017452dbe6cf6881c69d7a7dedc7a7bd Mon Sep 17 00:00:00 2001 From: Christos Zisopoulos Date: Fri, 29 May 2009 15:42:44 +0200 Subject: Handle missing javascript/stylesheets assets by raising an exception An exception will be raised if a local javascript/stylesheet file included by the stylesheet_link_tag or javascript_include_tag can not be found. When caching is enabled, we use atomic_write to ensure that the cache file is not created with zero length. Signed-off-by: Michael Koziarski [#2738 state:committed] --- .../lib/action_view/helpers/asset_tag_helper.rb | 32 +++++- actionpack/test/fixtures/public/elsewhere/cools.js | 1 + actionpack/test/fixtures/public/elsewhere/file.css | 1 + .../fixtures/public/javascripts/common.javascript | 1 + .../test/fixtures/public/stylesheets/random.styles | 1 + actionpack/test/template/asset_tag_helper_test.rb | 118 +++++++++++++++------ 6 files changed, 120 insertions(+), 34 deletions(-) create mode 100644 actionpack/test/fixtures/public/elsewhere/cools.js create mode 100644 actionpack/test/fixtures/public/elsewhere/file.css create mode 100644 actionpack/test/fixtures/public/javascripts/common.javascript create mode 100644 actionpack/test/fixtures/public/stylesheets/random.styles (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index babb9db38a..14cdc7a025 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -1,6 +1,7 @@ require 'cgi' require 'action_view/helpers/url_helper' require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/file' module ActionView module Helpers #:nodoc: @@ -285,7 +286,7 @@ module ActionView end javascript_src_tag(joined_javascript_name, options) else - expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n") + ensure_javascript_sources!(expand_javascript_sources(sources, recursive)).collect { |source| javascript_src_tag(source, options) }.join("\n") end end @@ -434,7 +435,7 @@ module ActionView end stylesheet_tag(joined_stylesheet_name, options) else - expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n") + ensure_stylesheet_sources!(expand_stylesheet_sources(sources, recursive)).collect { |source| stylesheet_tag(source, options) }.join("\n") end end @@ -664,13 +665,28 @@ module ActionView end end + def ensure_stylesheet_sources!(sources) + sources.each do |source| + asset_file_path!(path_to_stylesheet(source)) + end + return sources + end + + def ensure_javascript_sources!(sources) + sources.each do |source| + asset_file_path!(path_to_javascript(source)) + end + return sources + end + def join_asset_file_contents(paths) - paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n") + paths.collect { |path| File.read(asset_file_path!(path)) }.join("\n\n") end def write_asset_file_contents(joined_asset_path, asset_paths) + FileUtils.mkdir_p(File.dirname(joined_asset_path)) - File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) } + File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) } # Set mtime to the latest of the combined files to allow for # consistent ETag without a shared filesystem. @@ -682,6 +698,14 @@ module ActionView File.join(ASSETS_DIR, path.split('?').first) end + def asset_file_path!(path) + unless path =~ %r{^[-a-z]+://} + absolute_path = asset_file_path(path) + raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path) + return absolute_path + end + end + def collect_asset_files(*path) dir = path.first diff --git a/actionpack/test/fixtures/public/elsewhere/cools.js b/actionpack/test/fixtures/public/elsewhere/cools.js new file mode 100644 index 0000000000..6e12fe29c4 --- /dev/null +++ b/actionpack/test/fixtures/public/elsewhere/cools.js @@ -0,0 +1 @@ +// cools.js \ No newline at end of file diff --git a/actionpack/test/fixtures/public/elsewhere/file.css b/actionpack/test/fixtures/public/elsewhere/file.css new file mode 100644 index 0000000000..6aea0733b1 --- /dev/null +++ b/actionpack/test/fixtures/public/elsewhere/file.css @@ -0,0 +1 @@ +/*file.css*/ \ No newline at end of file diff --git a/actionpack/test/fixtures/public/javascripts/common.javascript b/actionpack/test/fixtures/public/javascripts/common.javascript new file mode 100644 index 0000000000..2ae1929056 --- /dev/null +++ b/actionpack/test/fixtures/public/javascripts/common.javascript @@ -0,0 +1 @@ +// common.javascript \ No newline at end of file diff --git a/actionpack/test/fixtures/public/stylesheets/random.styles b/actionpack/test/fixtures/public/stylesheets/random.styles new file mode 100644 index 0000000000..d4eeead95c --- /dev/null +++ b/actionpack/test/fixtures/public/stylesheets/random.styles @@ -0,0 +1 @@ +/* random.styles */ \ No newline at end of file diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index f1dad9f50e..65289a59bc 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -75,22 +75,22 @@ class AssetTagHelperTest < ActionView::TestCase } JavascriptIncludeToTag = { - %(javascript_include_tag("xmlhr")) => %(), - %(javascript_include_tag("xmlhr.js")) => %(), - %(javascript_include_tag("xmlhr", :lang => "vbscript")) => %(), + %(javascript_include_tag("bank")) => %(), + %(javascript_include_tag("bank.js")) => %(), + %(javascript_include_tag("bank", :lang => "vbscript")) => %(), %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n), %(javascript_include_tag(:defaults)) => %(\n\n\n\n), %(javascript_include_tag(:all)) => %(\n\n\n\n\n\n\n), %(javascript_include_tag(:all, :recursive => true)) => %(\n\n\n\n\n\n\n\n), - %(javascript_include_tag(:defaults, "test")) => %(\n\n\n\n\n), - %(javascript_include_tag("test", :defaults)) => %(\n\n\n\n\n) + %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n), + %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n) } StylePathToTag = { - %(stylesheet_path("style")) => %(/stylesheets/style.css), - %(stylesheet_path("style.css")) => %(/stylesheets/style.css), - %(stylesheet_path('dir/file')) => %(/stylesheets/dir/file.css), - %(stylesheet_path('/dir/file.rcss')) => %(/dir/file.rcss) + %(stylesheet_path("bank")) => %(/stylesheets/bank.css), + %(stylesheet_path("bank.css")) => %(/stylesheets/bank.css), + %(stylesheet_path('subdir/subdir')) => %(/stylesheets/subdir/subdir.css), + %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css) } PathToStyleToTag = { @@ -101,15 +101,16 @@ class AssetTagHelperTest < ActionView::TestCase } StyleLinkToTag = { - %(stylesheet_link_tag("style")) => %(), - %(stylesheet_link_tag("style.css")) => %(), - %(stylesheet_link_tag("/dir/file")) => %(), - %(stylesheet_link_tag("dir/file")) => %(), - %(stylesheet_link_tag("style", :media => "all")) => %(), + %(stylesheet_link_tag("bank")) => %(), + %(stylesheet_link_tag("bank.css")) => %(), + %(stylesheet_link_tag("/elsewhere/file")) => %(), + %(stylesheet_link_tag("subdir/subdir")) => %(), + %(stylesheet_link_tag("bank", :media => "all")) => %(), %(stylesheet_link_tag(:all)) => %(\n\n), %(stylesheet_link_tag(:all, :recursive => true)) => %(\n\n\n), %(stylesheet_link_tag(:all, :media => "all")) => %(\n\n), - %(stylesheet_link_tag("random.styles", "/css/stylish")) => %(\n), + %(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(\n), + %(stylesheet_link_tag("http://www.example.com/styles/style")) => %() } @@ -160,6 +161,20 @@ class AssetTagHelperTest < ActionView::TestCase JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_javascript_include_tag_with_missing_source + assert_raise(Errno::ENOENT) { + javascript_include_tag('missing_security_guard') + } + + assert_raise(Errno::ENOENT) { + javascript_include_tag(:defaults, 'missing_security_guard') + } + + assert_nothing_raised { + javascript_include_tag('http://example.com/css/missing_security_guard') + } + end + def test_javascript_include_tag_with_given_asset_id ENV["RAILS_ASSET_ID"] = "1" assert_dom_equal(%(\n\n\n\n), javascript_include_tag(:defaults)) @@ -167,26 +182,27 @@ class AssetTagHelperTest < ActionView::TestCase def test_register_javascript_include_default ENV["RAILS_ASSET_ID"] = "" - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider' - assert_dom_equal %(\n\n\n\n\n), javascript_include_tag(:defaults) + ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' + assert_dom_equal %(\n\n\n\n\n), javascript_include_tag(:defaults) end def test_register_javascript_include_default_mixed_defaults ENV["RAILS_ASSET_ID"] = "" - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider' - ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2' - assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) + ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' + ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js' + assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_custom_javascript_expansions - ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"] - assert_dom_equal %(\n\n\n\n), javascript_include_tag('first', :monkey, 'last') + ENV["RAILS_ASSET_ID"] = "" + ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] + assert_dom_equal %(\n\n\n), javascript_include_tag('controls', :robbery, 'effects') end def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end ENV["RAILS_ASSET_ID"] = "" - ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"] - assert_dom_equal %(\n\n\n\n\n\n\n\n\n), javascript_include_tag('first', :defaults, :monkey, 'last') + ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] + assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') end def test_custom_javascript_expansions_with_undefined_symbol @@ -195,6 +211,7 @@ class AssetTagHelperTest < ActionView::TestCase end def test_stylesheet_path + ENV["RAILS_ASSET_ID"] = "" StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end @@ -207,9 +224,20 @@ class AssetTagHelperTest < ActionView::TestCase StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_stylesheet_link_tag_with_missing_source + assert_raise(Errno::ENOENT) { + stylesheet_link_tag('missing_security_guard') + } + + assert_nothing_raised { + stylesheet_link_tag('http://example.com/css/missing_security_guard') + } + end + def test_custom_stylesheet_expansions - ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => ["head", "body", "tail"] - assert_dom_equal %(\n\n\n\n), stylesheet_link_tag('first', :monkey, 'last') + ENV["RAILS_ASSET_ID"] = '' + ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :robbery => ["bank", "robber"] + assert_dom_equal %(\n\n\n), stylesheet_link_tag('version.1.0', :robbery, 'subdir/subdir') end def test_custom_stylesheet_expansions_with_undefined_symbol @@ -546,8 +574,14 @@ class AssetTagHelperTest < ActionView::TestCase stylesheet_link_tag(:all, :cache => true) ) - expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max - assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"] + + expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max + assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + bytes_added_by_join = "\n\n".size * files_to_be_joined.size - "\n\n".size + expected_size = files_to_be_joined.sum { |p| File.size(p) } + bytes_added_by_join + assert_equal expected_size, File.size(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) assert_dom_equal( %(), @@ -598,6 +632,28 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end + def test_caching_stylesheet_link_tag_when_caching_on_and_missing_css_file + ENV["RAILS_ASSET_ID"] = "" + ActionController::Base.asset_host = 'http://a0.example.com' + ActionController::Base.perform_caching = true + + assert_raise(Errno::ENOENT) { + stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true) + } + + assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_raise(Errno::ENOENT) { + stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money") + } + + assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + end + def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" } @@ -625,8 +681,10 @@ class AssetTagHelperTest < ActionView::TestCase stylesheet_link_tag(:all, :cache => true) ) - expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max - assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"] + + expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max + assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) assert_dom_equal( %(), -- cgit v1.2.3 From 68b02cb00aae4f4ee1b2b9b1eadb6951b747c181 Mon Sep 17 00:00:00 2001 From: Vicente Mundim Date: Sat, 27 Jun 2009 12:40:55 +1200 Subject: Make filter_parameters work correctly with array parameters. --- actionpack/lib/action_controller/base/filter_parameter_logging.rb | 4 ++++ actionpack/test/controller/filter_params_test.rb | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb index 26cd03f277..065e62a37f 100644 --- a/actionpack/lib/action_controller/base/filter_parameter_logging.rb +++ b/actionpack/lib/action_controller/base/filter_parameter_logging.rb @@ -43,6 +43,10 @@ module ActionController filtered_parameters[key] = '[FILTERED]' elsif value.is_a?(Hash) filtered_parameters[key] = filter_parameters(value) + elsif value.is_a?(Array) + filtered_parameters[key] = value.collect do |item| + filter_parameters(item) + end elsif block_given? key = key.dup value = value.dup if value diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index 8c9e4f81de..f7864745eb 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -40,7 +40,8 @@ class FilterParamTest < ActionController::TestCase [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'], [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'], [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'], - [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']] + [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'], + [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)]] test_hashes.each do |before_filter, after_filter, filter_words| FilterParamController.filter_parameter_logging(*filter_words) -- cgit v1.2.3 From 085db5e128ad4ad8fd042776722c78e194c6d0a4 Mon Sep 17 00:00:00 2001 From: Chris Mear Date: Thu, 19 Feb 2009 14:16:10 +0000 Subject: Make text_area_tag escape contents by default. Signed-off-by: Michael Koziarski [#2015 state:committed] --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 5 +++++ actionpack/test/template/form_tag_helper_test.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 8ab78e7bc6..ca6ba501e2 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -231,6 +231,8 @@ module ActionView # * :rows - Specify the number of rows in the textarea # * :cols - Specify the number of columns in the textarea # * :disabled - If set to true, the user will not be able to use this input. + # * :escape - By default, the contents of the text input are HTML escaped. + # If you need unescaped contents, set this to false. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples @@ -258,6 +260,9 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end + escape = options.key?("escape") ? options.delete("escape") : true + content = html_escape(content) if escape + content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 09d199b75d..f387123117 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -159,6 +159,18 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, input_elem['id'] end + def test_text_area_tag_escape_content + actual = text_area_tag "body", "hello world", :size => "20x40" + expected = %() + assert_dom_equal expected, actual + end + + def test_text_area_tag_unescaped_content + actual = text_area_tag "body", "hello world", :size => "20x40", :escape => false + expected = %() + assert_dom_equal expected, actual + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() -- cgit v1.2.3 From db3de78a83379ab2a58e0d29fb10622b813a4d44 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 30 Jun 2009 14:37:12 -0700 Subject: Bump up the version to 3.0.pre --- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 142c72ce6b..1fc5018561 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -115,7 +115,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index e0aa2a5f2f..ed0cdf38ee 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,8 +1,8 @@ module ActionPack #:nodoc: module VERSION #:nodoc: - MAJOR = 2 - MINOR = 3 - TINY = 2 + MAJOR = 3 + MINOR = 0 + TINY = "pre" STRING = [MAJOR, MINOR, TINY].join('.') end -- cgit v1.2.3 From 0515256fd32e6efb67fe90aedf0dadc25dc9e1ba Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 11:16:18 -0700 Subject: Accept Symbol for contoller name [#2855 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- actionpack/lib/action_controller/routing/route_set.rb | 2 +- actionpack/test/controller/routing_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 45ad8a3a3b..87b4b0571c 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -436,7 +436,7 @@ module ActionController def recognize(request) params = recognize_path(request.path, extract_request_environment(request)) request.path_parameters = params.with_indifferent_access - "#{params[:controller].camelize}Controller".constantize + "#{params[:controller].to_s.camelize}Controller".constantize end def recognize_path(path, environment={}) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index c2acc03a1b..16d7df4843 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1667,6 +1667,17 @@ class RouteSetTest < Test::Unit::TestCase assert_equal 1, set.routes.size end + def test_draw_symbol_controller_name + assert_equal 0, set.routes.size + set.draw do |map| + map.connect '/users/index', :controller => :users, :action => :index + end + @request = ActionController::TestRequest.new + @request.request_uri = '/users/index' + assert_nothing_raised { set.recognize(@request) } + assert_equal 1, set.routes.size + end + def test_named_draw assert_equal 0, set.routes.size set.draw do |map| -- cgit v1.2.3 From 7583a24ee0ea85d55a5e235c3082f1b67d3d7694 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 11:53:17 -0700 Subject: Move mocha down below initial T::U require and bump version to 0.9.7 [#2858 state:resolved] --- actionpack/test/old_base/abstract_unit.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/old_base/abstract_unit.rb b/actionpack/test/old_base/abstract_unit.rb index c71da7fa6c..3301041a41 100644 --- a/actionpack/test/old_base/abstract_unit.rb +++ b/actionpack/test/old_base/abstract_unit.rb @@ -12,9 +12,6 @@ require 'yaml' require 'stringio' require 'test/unit' -gem 'mocha', '>= 0.9.5' -require 'mocha' - begin require 'ruby-debug' Debugger.settings[:autoeval] = true -- cgit v1.2.3 From 92bff2ebf122252cf3ff64160bdfa9891fcff3f4 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 1 Jul 2009 16:25:33 -0700 Subject: Fix pattern to match various magic comment formats --- actionpack/lib/action_view/template/handlers/erb.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index e3a7d96941..aab7baf442 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -17,7 +17,7 @@ module ActionView def compile(template) require 'erb' - magic = $1 if template.source =~ /\A(<%#.*coding:\s*(\S+)\s*-?%>)/ + magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/ erb = "#{magic}<% __in_erb_template=true %>#{template.source}" ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end -- cgit v1.2.3 From 8bb510f6c1e235f5fb1cf9e79af759a429a497b0 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Sun, 28 Jun 2009 02:14:44 -0500 Subject: Patch FormTagHelper so that when a form tag is created, the div which holds the form authenticity token is set to display:inline [#2846 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index ca6ba501e2..e126b35e90 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -451,10 +451,10 @@ module ActionView '' when /^post$/i, "", nil html_options["method"] = "post" - protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : '' + protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : '' else html_options["method"] = "post" - content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0') + content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline') end end -- cgit v1.2.3 From 51d7b3070c68492f5376c19d24d8e5a2d746d7ea Mon Sep 17 00:00:00 2001 From: Tieg Zaharia Date: Fri, 26 Jun 2009 14:38:18 -0400 Subject: Adds a video_tag helper for the HTML5 video tag (similar to how the image_tag works) (tests included); removes a duplicate test line for image_tag; adds boolean attributes for video tag to tag()'s boolean attributes Signed-off-by: Yehuda Katz + Carl Lerche --- .../lib/action_view/helpers/asset_tag_helper.rb | 67 ++++++++++++++++++++++ actionpack/lib/action_view/helpers/tag_helper.rb | 3 +- actionpack/test/template/asset_tag_helper_test.rb | 43 +++++++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 14cdc7a025..a3b99327ce 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -454,6 +454,21 @@ module ActionView end alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route + # Computes the path to a video asset in the public videos directory. + # Full paths from the document root will be passed through. + # Used internally by +video_tag+ to build the video path. + # + # ==== Examples + # video_path("hd") # => /videos/hd + # video_path("hd.avi") # => /videos/hd.avi + # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi + # video_path("/trailers/hd.avi") # => /videos/hd.avi + # video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi + def video_path(source) + compute_public_path(source, 'videos') + end + alias_method :path_to_video, :video_path # aliased to avoid conflicts with an video_path named route + # Returns an html image tag for the +source+. The +source+ can be a full # path or a file that exists in your public images directory. # @@ -505,6 +520,58 @@ module ActionView tag("img", options) end + # Returns an html video tag for the +sources+. If +sources+ is a string, + # a single video tag will be returned. If +sources+ is an array, a video + # tag with nested source tags for each source will be returned. The + # +sources+ can be full paths or files that exists in your public videos + # directory. + # + # ==== Options + # You can add HTML attributes using the +options+. The +options+ supports + # two additional keys for convenience and conformance: + # + # * :poster - Set an image (like a screenshot) to be shown + # before the video loads. The path is calculated like the +src+ of +image_tag+. + # * :size - Supplied as "{Width}x{Height}", so "30x45" becomes + # width="30" and height="45". :size will be ignored if the + # value is not in the correct format. + # + # ==== Examples + # video_tag("trailer") # => + #