aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorYehuda Katz <wycats@gmail.com>2009-03-12 13:19:13 -0600
committerYehuda Katz <wycats@gmail.com>2009-03-12 13:19:13 -0600
commita2637e9f1fba92bc0b8dbf461ce9f4f8ffb4cfaa (patch)
tree21207daf4fd7dd7dc77930b934b900b1d1d6e2f0 /actionpack
parent67f9b39bd05678881e200ddeed02b2bce9744ac8 (diff)
downloadrails-a2637e9f1fba92bc0b8dbf461ce9f4f8ffb4cfaa.tar.gz
rails-a2637e9f1fba92bc0b8dbf461ce9f4f8ffb4cfaa.tar.bz2
rails-a2637e9f1fba92bc0b8dbf461ce9f4f8ffb4cfaa.zip
Try to build a new AC::Base on top of AbstractController
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_controller/new_base.rb5
-rw-r--r--actionpack/lib/action_controller/new_base/base.rb26
-rw-r--r--actionpack/lib/action_controller/new_base/hide_actions.rb28
-rw-r--r--actionpack/lib/action_controller/new_base/url_for.rb40
-rw-r--r--actionpack/test/new_base/base_test.rb317
5 files changed, 416 insertions, 0 deletions
diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb
new file mode 100644
index 0000000000..2cef221de3
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base.rb
@@ -0,0 +1,5 @@
+module ActionController
+ autoload :AbstractBase, "action_controller/new_base/base"
+ autoload :HideActions, "action_controller/new_base/hide_actions"
+ autoload :UrlFor, "action_controller/new_base/url_for"
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb
new file mode 100644
index 0000000000..ebe7c8dda6
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/base.rb
@@ -0,0 +1,26 @@
+module ActionController
+ class AbstractBase < AbstractController::Base
+ attr_internal :request, :response, :params
+
+ def self.controller_name
+ @controller_name ||= controller_path.split("/").last
+ end
+
+ def controller_name() self.class.controller_name end
+
+ def self.controller_path
+ @controller_path ||= self.name.sub(/Controller$/, '').underscore
+ end
+
+ def controller_path() self.class.controller_path end
+
+ def self.action_methods
+ @action_names ||= Set.new(self.public_instance_methods - self::CORE_METHODS)
+ end
+
+ def self.action_names() action_methods end
+
+ def action_methods() self.class.action_names end
+ def action_names() action_methods end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb
new file mode 100644
index 0000000000..9847d7b086
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/hide_actions.rb
@@ -0,0 +1,28 @@
+module ActionController
+ module HideActions
+ def self.included(klass)
+ klass.class_eval do
+ extend ClassMethods
+ extlib_inheritable_accessor :hidden_actions
+ self.hidden_actions ||= Set.new
+ end
+ end
+
+ def action_methods() self.class.action_names end
+ def action_names() action_methods end
+
+ module ClassMethods
+ def hide_action(*args)
+ args.each do |arg|
+ self.hidden_actions << arg.to_s
+ end
+ end
+
+ def action_methods
+ @action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)})
+ end
+
+ def self.action_names() action_methods end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/url_for.rb b/actionpack/lib/action_controller/new_base/url_for.rb
new file mode 100644
index 0000000000..af5b21012b
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/url_for.rb
@@ -0,0 +1,40 @@
+module ActionController
+ module UrlFor
+ 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 \ No newline at end of file
diff --git a/actionpack/test/new_base/base_test.rb b/actionpack/test/new_base/base_test.rb
new file mode 100644
index 0000000000..7ac5eac3c5
--- /dev/null
+++ b/actionpack/test/new_base/base_test.rb
@@ -0,0 +1,317 @@
+$:.unshift(File.dirname(__FILE__) + '/../../lib')
+$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
+
+require 'test/unit'
+require 'active_support'
+require 'active_support/test_case'
+require 'action_controller'
+require 'action_view/base'
+
+begin
+ require 'ruby-debug'
+ Debugger.settings[:autoeval] = true
+ Debugger.start
+rescue LoadError
+ # Debugging disabled. `gem install ruby-debug` to enable.
+end
+
+require 'action_controller/abstract'
+require 'action_controller/new_base'
+require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
+
+require 'rubygems'
+require 'rack/test'
+
+module ActionController
+ module TestProcess
+ 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!
+
+ @html_document = nil
+ @request.env['REQUEST_METHOD'] = http_method
+
+ @request.action = action.to_s
+
+ 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
+ @controller.request = @request
+ @controller.response = @response
+ @controller.process(action)
+ end
+ end
+
+ class Base2 < AbstractBase
+ include AbstractController::Callbacks
+ include AbstractController::Renderer
+ include AbstractController::Helpers
+ include AbstractController::Layouts
+ include AbstractController::Logger
+
+ include ActionController::HideActions
+ include ActionController::UrlFor
+
+ CORE_METHODS = self.public_instance_methods
+ end
+end
+
+# Provide some controller to run the tests on.
+module Submodule
+ class ContainedEmptyController < ActionController::Base2
+ end
+ class ContainedNonEmptyController < ActionController::Base2
+ def public_action
+ render :nothing => true
+ end
+
+ hide_action :hidden_action
+ def hidden_action
+ raise "Noooo!"
+ end
+
+ def another_hidden_action
+ end
+ hide_action :another_hidden_action
+ end
+ class SubclassedController < ContainedNonEmptyController
+ hide_action :public_action # Hiding it here should not affect the superclass.
+ end
+end
+class EmptyController < ActionController::Base2
+end
+class NonEmptyController < ActionController::Base2
+ def public_action
+ end
+
+ hide_action :hidden_action
+ def hidden_action
+ end
+end
+
+class MethodMissingController < ActionController::Base
+
+ hide_action :shouldnt_be_called
+ def shouldnt_be_called
+ raise "NO WAY!"
+ end
+
+protected
+
+ def method_missing(selector)
+ render :text => selector.to_s
+ end
+
+end
+
+class DefaultUrlOptionsController < ActionController::Base2
+ def default_url_options_action
+ end
+
+ def default_url_options(options = nil)
+ { :host => 'www.override.com', :action => 'new', :bacon => 'chunky' }
+ end
+end
+
+class ControllerClassTests < Test::Unit::TestCase
+ def test_controller_path
+ assert_equal 'empty', EmptyController.controller_path
+ assert_equal EmptyController.controller_path, EmptyController.new.controller_path
+ assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
+ assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
+ end
+ def test_controller_name
+ assert_equal 'empty', EmptyController.controller_name
+ assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
+ end
+end
+
+class ControllerInstanceTests < Test::Unit::TestCase
+ def setup
+ @empty = EmptyController.new
+ @contained = Submodule::ContainedEmptyController.new
+ @empty_controllers = [@empty, @contained, Submodule::SubclassedController.new]
+
+ @non_empty_controllers = [NonEmptyController.new,
+ Submodule::ContainedNonEmptyController.new]
+ end
+
+ 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!"
+ 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!"
+ end
+ end
+
+ protected
+ # Mocha adds some public instance methods to Object that would be
+ # considered actions, so explicitly hide_action them.
+ def hide_mocha_methods_from_controller(controller)
+ mocha_methods = [
+ :expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
+ :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
+ ]
+ controller.class.__send__(:hide_action, *mocha_methods)
+ end
+end
+
+
+class PerformActionTest < ActiveSupport::TestCase
+ class MockLogger
+ attr_reader :logged
+
+ def initialize
+ @logged = []
+ end
+
+ def method_missing(method, *args)
+ @logged << args.first
+ end
+ end
+
+ def use_controller(controller_class)
+ @controller = controller_class.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nxtangle.com"
+
+ rescue_action_in_public!
+ end
+
+ attr_accessor :app
+ include Rack::Test::Methods
+
+ def with_routing
+ real_routes = ActionController::Routing::Routes
+ ActionController::Routing.module_eval { remove_const :Routes }
+
+ temporary_routes = ActionController::Routing::RouteSet.new
+ ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
+
+ yield temporary_routes
+ ensure
+ if ActionController::Routing.const_defined? :Routes
+ ActionController::Routing.module_eval { remove_const :Routes }
+ end
+ ActionController::Routing.const_set(:Routes, real_routes) if real_routes
+ end
+
+ def test_get_on_priv_should_show_selector
+ ActionController::Base.session_options[:key] = "abc"
+ ActionController::Base.session_options[:secret] = ("*" * 30)
+
+ with_routing do |set|
+ set.draw do |map|
+ map.connect ':controller/:action'
+ end
+
+ @app = ActionController::Dispatcher.new
+
+ resp = get "/method_missing/shouldnt_be_called"
+ assert_equal 'shouldnt_be_called', resp.body
+ end
+
+ # use_controller MethodMissingController
+ # get :shouldnt_be_called
+ # assert_response :success
+ # assert_equal 'shouldnt_be_called', @response.body
+ end
+
+ def test_method_missing_is_not_an_action_name
+ use_controller MethodMissingController
+ assert ! @controller.__send__(:action_methods).include?('method_missing')
+
+ get :method_missing
+ assert_response :success
+ assert_equal 'method_missing', @response.body
+ end
+
+ def test_get_on_hidden_should_fail
+ use_controller NonEmptyController
+ get :hidden_action
+ assert_response 404
+
+ get :another_hidden_action
+ assert_response 404
+ end
+
+ def test_namespaced_action_should_log_module_name
+ use_controller Submodule::ContainedNonEmptyController
+ @controller.logger = MockLogger.new
+ get :public_action
+ assert_match /Processing\sSubmodule::ContainedNonEmptyController#public_action/, @controller.logger.logged[1]
+ end
+end
+
+class DefaultUrlOptionsTest < ActionController::TestCase
+ tests DefaultUrlOptionsController
+
+ def setup
+ @request.host = 'www.example.com'
+ rescue_action_in_public!
+ end
+
+ def test_default_url_options_are_used_if_set
+ ActionController::Routing::Routes.draw do |map|
+ map.default_url_options 'default_url_options', :controller => 'default_url_options'
+ map.connect ':controller/:action/:id'
+ end
+
+ get :default_url_options_action # Make a dummy request so that the controller is initialized properly.
+
+ assert_equal 'http://www.override.com/default_url_options/new?bacon=chunky', @controller.url_for(:controller => 'default_url_options')
+ assert_equal 'http://www.override.com/default_url_options?bacon=chunky', @controller.send(:default_url_options_url)
+ ensure
+ ActionController::Routing::Routes.load!
+ end
+end
+
+class EmptyUrlOptionsTest < ActionController::TestCase
+ tests NonEmptyController
+
+ def setup
+ @request.host = 'www.example.com'
+ rescue_action_in_public!
+ end
+
+ def test_ensure_url_for_works_as_expected_when_called_with_no_options_if_default_url_options_is_not_set
+ get :public_action
+ assert_equal "http://www.example.com/non_empty/public_action", @controller.url_for
+ end
+end
+
+class EnsureNamedRoutesWorksTicket22BugTest < Test::Unit::TestCase
+ def test_named_routes_still_work
+ ActionController::Routing::Routes.draw do |map|
+ map.resources :things
+ end
+ EmptyController.send :include, ActionController::UrlWriter
+
+ assert_equal '/things', EmptyController.new.send(:things_path)
+ ensure
+ ActionController::Routing::Routes.load!
+ end
+end