diff options
author | José Valim <jose.valim@gmail.com> | 2009-12-11 19:24:32 -0200 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2009-12-11 19:24:32 -0200 |
commit | cf7b94c013d4e824433a018001474e71ddd81a99 (patch) | |
tree | 67b90f1e13a88d60170469e281f081711a976194 /actionpack/lib | |
parent | 6e55b32e98fcaad82184d2e21ee611a3465e4b20 (diff) | |
parent | 2297eaed5b195ea42b99d062ad45f87dde9d3c60 (diff) | |
download | rails-cf7b94c013d4e824433a018001474e71ddd81a99.tar.gz rails-cf7b94c013d4e824433a018001474e71ddd81a99.tar.bz2 rails-cf7b94c013d4e824433a018001474e71ddd81a99.zip |
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'actionpack/lib')
36 files changed, 808 insertions, 774 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 1a6c4278c9..688a2fe31c 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -2,15 +2,20 @@ require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/delegation" module AbstractController - autoload :Base, "abstract_controller/base" - autoload :Callbacks, "abstract_controller/callbacks" - autoload :Helpers, "abstract_controller/helpers" - autoload :Layouts, "abstract_controller/layouts" - autoload :LocalizedCache, "abstract_controller/localized_cache" - autoload :Logger, "abstract_controller/logger" - autoload :RenderingController, "abstract_controller/rendering_controller" + extend ActiveSupport::Autoload + + autoload :Base + autoload :Callbacks + autoload :Helpers + autoload :Layouts + autoload :LocalizedCache + autoload :Logger + autoload :RenderingController + # === Exceptions - autoload :ActionNotFound, "abstract_controller/exceptions" - autoload :DoubleRenderError, "abstract_controller/exceptions" - autoload :Error, "abstract_controller/exceptions" + autoload_at "abstract_controller/exceptions" do + autoload :ActionNotFound + autoload :DoubleRenderError + autoload :Error + end end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index 7054b9cf26..777e515d60 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -115,7 +115,7 @@ module AbstractController # _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial def _determine_template(options) if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(options[:text], format_for_text) + options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) 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, {}) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 03a40e4fce..f830223058 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,66 +1,72 @@ +require "active_support" + module ActionController - autoload :Base, "action_controller/base" - autoload :Benchmarking, "action_controller/metal/benchmarking" - autoload :ConditionalGet, "action_controller/metal/conditional_get" - autoload :Configuration, "action_controller/metal/configuration" - autoload :Head, "action_controller/metal/head" - autoload :Helpers, "action_controller/metal/helpers" - autoload :HideActions, "action_controller/metal/hide_actions" - autoload :Layouts, "action_controller/metal/layouts" - autoload :Metal, "action_controller/metal" - autoload :Middleware, "action_controller/middleware" - autoload :RackConvenience, "action_controller/metal/rack_convenience" - autoload :Rails2Compatibility, "action_controller/metal/compatibility" - autoload :Redirector, "action_controller/metal/redirector" - autoload :RenderingController, "action_controller/metal/rendering_controller" - autoload :RenderOptions, "action_controller/metal/render_options" - autoload :Rescue, "action_controller/metal/rescuable" - autoload :Responder, "action_controller/metal/responder" - autoload :Session, "action_controller/metal/session" - autoload :Testing, "action_controller/metal/testing" - autoload :UrlFor, "action_controller/metal/url_for" + extend ActiveSupport::Autoload - autoload :Caching, 'action_controller/caching' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' - autoload :MimeResponds, 'action_controller/metal/mime_responds' - autoload :PerformanceTest, 'action_controller/deprecated/performance_test' - autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Routing, 'action_controller/deprecated' - autoload :SessionManagement, 'action_controller/metal/session_management' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :TestProcess, 'action_controller/testing/process' - autoload :UrlRewriter, 'action_controller/url_rewriter' - autoload :UrlWriter, 'action_controller/url_rewriter' + autoload :Base + autoload :Caching + autoload :PolymorphicRoutes + autoload :RecordIdentifier + autoload :UrlRewriter + autoload :Translation + autoload :Metal + autoload :Middleware - autoload :Verification, 'action_controller/metal/verification' - autoload :Flash, 'action_controller/metal/flash' - autoload :RequestForgeryProtection, 'action_controller/metal/request_forgery_protection' - autoload :Streaming, 'action_controller/metal/streaming' - autoload :HttpAuthentication, 'action_controller/metal/http_authentication' - autoload :FilterParameterLogging, 'action_controller/metal/filter_parameter_logging' - autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/metal/cookies' + autoload_under "metal" do + autoload :Benchmarking + autoload :ConditionalGet + autoload :Configuration + autoload :Head + autoload :Helpers + autoload :HideActions + autoload :Layouts + autoload :MimeResponds + autoload :RackConvenience + autoload :Compatibility + autoload :Redirector + autoload :RenderingController + autoload :RenderOptions + autoload :Rescue + autoload :Responder + autoload :Session + autoload :SessionManagement + autoload :UrlFor + autoload :Verification + autoload :Flash + autoload :RequestForgeryProtection + autoload :Streaming + autoload :HttpAuthentication + autoload :FilterParameterLogging + autoload :Cookies + end - autoload :ActionControllerError, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :RoutingError, 'action_controller/metal/exceptions' - autoload :MethodNotAllowed, 'action_controller/metal/exceptions' - autoload :NotImplemented, 'action_controller/metal/exceptions' - autoload :UnknownController, 'action_controller/metal/exceptions' - autoload :MissingFile, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :SessionOverflowError, 'action_controller/metal/exceptions' - autoload :UnknownHttpMethod, 'action_controller/metal/exceptions' -end + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :Routing, 'action_controller/deprecated' + autoload :Integration, 'action_controller/deprecated/integration_test' + autoload :IntegrationTest, 'action_controller/deprecated/integration_test' -autoload :HTML, 'action_controller/vendor/html-scanner' -autoload :AbstractController, 'abstract_controller' + autoload :UrlWriter, 'action_controller/url_rewriter' + + autoload_at "action_controller/metal/exceptions" do + autoload :ActionControllerError + autoload :RenderError + autoload :RoutingError + autoload :MethodNotAllowed + autoload :NotImplemented + autoload :UnknownController + autoload :MissingFile + autoload :RenderError + autoload :SessionOverflowError + autoload :UnknownHttpMethod + end +end +# All of these simply register additional autoloads +require 'abstract_controller' require 'action_dispatch' require 'action_view' +require 'action_controller/vendor/html-scanner' # Common ActiveSupport usage in ActionController require "active_support/concern" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4c026fe5f7..48bfbab215 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -19,12 +19,11 @@ module ActionController # Legacy modules include SessionManagement - include ActionDispatch::StatusCodes include ActionController::Caching include ActionController::MimeResponds # Rails 2.x compatibility - include ActionController::Rails2Compatibility + include ActionController::Compatibility include ActionController::Cookies include ActionController::Session @@ -91,7 +90,7 @@ module ActionController end if options[:status] - options[:status] = interpret_status(options[:status]).to_i + options[:status] = _interpret_status(options[:status]) end options[:update] = blk if block_given? @@ -140,9 +139,9 @@ module ActionController raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? status = if options.is_a?(Hash) && options.key?(:status) - interpret_status(options.delete(:status)) + _interpret_status(options.delete(:status)) elsif response_status.key?(:status) - interpret_status(response_status[:status]) + _interpret_status(response_status[:status]) else 302 end @@ -164,5 +163,14 @@ module ActionController super(url, status) end + + private + def _interpret_status(status) + if status.is_a?(Symbol) + (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) + else + status.to_i + end + end end end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 3caf759032..ad357cceda 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -30,10 +30,11 @@ module ActionController #:nodoc: # config.action_controller.cache_store = MyOwnStore.new("parameter") module Caching extend ActiveSupport::Concern + extend ActiveSupport::Autoload - autoload :Actions, 'action_controller/caching/actions' - autoload :Fragments, 'action_controller/caching/fragments' - autoload :Pages, 'action_controller/caching/pages' + autoload :Actions + autoload :Fragments + autoload :Pages autoload :Sweeper, 'action_controller/caching/sweeping' autoload :Sweeping, 'action_controller/caching/sweeping' diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb index 86336b6bc4..05c8c0f156 100644 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ b/actionpack/lib/action_controller/deprecated/integration_test.rb @@ -1,2 +1,4 @@ +require "action_dispatch/testing/integration" + ActionController::Integration = ActionDispatch::Integration ActionController::IntegrationTest = ActionDispatch::IntegrationTest diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 0c264fcd09..a90f798cd5 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -1,5 +1,5 @@ module ActionController - module Rails2Compatibility + module Compatibility extend ActiveSupport::Concern class ::ActionController::ActionControllerError < StandardError #:nodoc: diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index b9d23da3e0..07024d0a9a 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -18,18 +18,9 @@ module ActionController 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 diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index f43900faa0..b2d44c6c63 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -133,8 +133,20 @@ module ActionController #:nodoc: Array(key || keys).each { |k| used ? @used << k : @used.delete(k) } return key ? self[key] : self end + end + + # Access the contents of the flash. Use <tt>flash["notice"]</tt> to + # read a notice you put there or <tt>flash["notice"] = "hello"</tt> + # to put a new one. + def flash #:doc: + unless @_flash + @_flash = session["flash"] || FlashHash.new + @_flash.sweep end + @_flash + end + protected def process_action(method_name) super @@ -146,17 +158,5 @@ module ActionController #:nodoc: super @_flash = nil end - - # Access the contents of the flash. Use <tt>flash["notice"]</tt> to - # read a notice you put there or <tt>flash["notice"] = "hello"</tt> - # to put a new one. - def flash #:doc: - unless @_flash - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end - - @_flash - end end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index b4325e24ad..d0402e5bad 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -52,7 +52,7 @@ module ActionController included do # Set the default directory for helpers extlib_inheritable_accessor(:helpers_dir) do - defined?(Rails) ? "#{Rails.root}/app/helpers" : "app/helpers" + defined?(Rails.root) ? "#{Rails.root}/app/helpers" : "app/helpers" end end diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb index 0d69ca10df..b6a7ca0eda 100644 --- a/actionpack/lib/action_controller/metal/render_options.rb +++ b/actionpack/lib/action_controller/metal/render_options.rb @@ -1,19 +1,24 @@ module ActionController + + def self.add_renderer(key, &block) + RenderOptions.add(key, &block) + end + module RenderOptions extend ActiveSupport::Concern included do extlib_inheritable_accessor :_renderers - self._renderers = [] + self._renderers = {} end module ClassMethods def _write_render_options - renderers = _renderers.map do |r| + renderers = _renderers.map do |name, value| <<-RUBY_EVAL - if options.key?(:#{r}) + if options.key?(:#{name}) _process_options(options) - return render_#{r}(options[:#{r}], options) + return _render_option_#{name}(options[:#{name}], options) end RUBY_EVAL end @@ -25,79 +30,63 @@ module ActionController RUBY_EVAL end - def _add_render_option(name) - _renderers << name + def use_renderers(*args) + args.each do |key| + _renderers[key] = RENDERERS[key] + end _write_render_options end + alias use_renderer use_renderers 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 + RENDERERS = {} + def self.add(key, &block) + define_method("_render_option_#{key}", &block) + RENDERERS[key] = block + All._write_render_options end - end - module RenderOptions - module Json - extend RenderOption - register_renderer :json + module All + extend ActiveSupport::Concern + include RenderOptions - 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 + INCLUDED = [] + included do + self._renderers = RENDERERS + _write_render_options + INCLUDED << self end - end - - module Js - extend RenderOption - register_renderer :js - def render_js(js, options) - self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js : js + def self._write_render_options + INCLUDED.each(&:_write_render_options) 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 + add :json do |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 - module RJS - extend RenderOption - register_renderer :update - - def render_update(proc, options) - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) - self.content_type = Mime::JS - self.response_body = generator.to_s - end + add :js do |js, options| + self.content_type ||= Mime::JS + self.response_body = js.respond_to?(:to_js) ? js.to_js : js end - module All - extend ActiveSupport::Concern + add :xml do |xml, options| + self.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end - include ActionController::RenderOptions::Json - include ActionController::RenderOptions::Js - include ActionController::RenderOptions::Xml - include ActionController::RenderOptions::RJS + add :update do |proc, options| + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) + self.content_type = Mime::JS + self.response_body = generator.to_s end end end diff --git a/actionpack/lib/action_controller/metal/rescuable.rb b/actionpack/lib/action_controller/metal/rescue.rb index bbca1b2179..bbca1b2179 100644 --- a/actionpack/lib/action_controller/metal/rescuable.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 6c76c57839..cb0e600871 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -80,6 +80,11 @@ module ActionController #:nodoc: class Responder attr_reader :controller, :request, :format, :resource, :resources, :options + ACTIONS_FOR_VERBS = { + :post => :new, + :put => :edit + } + def initialize(controller, resources, options={}) @controller = controller @request = controller.request @@ -138,7 +143,7 @@ module ActionController #:nodoc: def navigation_behavior(error) if get? raise error - elsif has_errors? + elsif has_errors? && default_action render :action => default_action else redirect_to resource_location @@ -209,7 +214,7 @@ module ActionController #:nodoc: # the verb is post. # def default_action - @action || (request.post? ? :new : :edit) + @action ||= ACTIONS_FOR_VERBS[request.method] end end end diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 01a55fe930..7533a22299 100644 --- a/actionpack/lib/action_controller/testing/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,5 +1,8 @@ require 'active_support/test_case' require 'rack/session/abstract/id' +require 'action_controller/metal/testing' +require 'action_controller/testing/process' +require 'action_dispatch/test_case' module ActionController class TestRequest < ActionDispatch::TestRequest #:nodoc: diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb index f622d195ee..2cb20ddd05 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner.rb @@ -1,6 +1,8 @@ $LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner" module HTML + extend ActiveSupport::Autoload + autoload :CDATA, 'html/node' autoload :Document, 'html/document' autoload :FullSanitizer, 'html/sanitizer' diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 259814a322..e21dbc59cc 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -28,37 +28,38 @@ module Rack end module ActionDispatch - autoload :Request, 'action_dispatch/http/request' - autoload :Response, 'action_dispatch/http/response' - autoload :StatusCodes, 'action_dispatch/http/status_codes' - autoload :Utils, 'action_dispatch/http/utils' + extend ActiveSupport::Autoload - autoload :Callbacks, 'action_dispatch/middleware/callbacks' - autoload :MiddlewareStack, 'action_dispatch/middleware/stack' - autoload :ParamsParser, 'action_dispatch/middleware/params_parser' - autoload :Rescue, 'action_dispatch/middleware/rescue' - autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions' - autoload :Static, 'action_dispatch/middleware/static' - autoload :StringCoercion, 'action_dispatch/middleware/string_coercion' + autoload_under "http" do + autoload :Request + autoload :Response + autoload :StatusCodes + autoload :Utils + end - autoload :Routing, 'action_dispatch/routing' + autoload_under "middleware" do + autoload :Callbacks + autoload :ParamsParser + autoload :Rescue + autoload :ShowExceptions + autoload :Static + autoload :StringCoercion + end - autoload :Assertions, 'action_dispatch/testing/assertions' - autoload :Integration, 'action_dispatch/testing/integration' - autoload :IntegrationTest, 'action_dispatch/testing/integration' - autoload :PerformanceTest, 'action_dispatch/testing/performance_test' - autoload :TestRequest, 'action_dispatch/testing/test_request' - autoload :TestResponse, 'action_dispatch/testing/test_response' + autoload :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Routing autoload :HTML, 'action_controller/vendor/html-scanner' module Http - autoload :Headers, 'action_dispatch/http/headers' + extend ActiveSupport::Autoload + + autoload :Headers end module Session autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' end end diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb index 5bac842ec1..ea1475e827 100644 --- a/actionpack/lib/action_dispatch/http/status_codes.rb +++ b/actionpack/lib/action_dispatch/http/status_codes.rb @@ -21,22 +21,5 @@ module ActionDispatch hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code hash }.freeze - - private - # Given a status parameter, determine whether it needs to be converted - # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup - # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE - # hash to convert it. - def interpret_status(status) - case status - when Fixnum then - "#{status} #{STATUS_CODES[status]}".strip - when Symbol then - interpret_status(SYMBOL_TO_STATUS_CODE[status] || - "500 Unknown Status #{status.inspect}") - else - status.to_s - end - end end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 036deec6d2..471d18491c 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -2,8 +2,6 @@ require "active_support/core_ext/exception" module ActionDispatch class ShowExceptions - include StatusCodes - LOCALHOST = '127.0.0.1'.freeze RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') @@ -29,7 +27,7 @@ module ActionDispatch 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', ActionController::UnknownAction.name => 'unknown_action', - 'ActionView::TemplateError' => 'template_error' + 'ActionView::Template::Error' => 'template_error' }) FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, @@ -104,7 +102,7 @@ module ActionDispatch end def status_code(exception) - interpret_status(@@rescue_responses[exception.class.name]).to_i + ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[@@rescue_responses[exception.class.name]] end def render(status, body) @@ -119,7 +117,7 @@ module ActionDispatch return unless logger ActiveSupport::Deprecation.silence do - if ActionView::TemplateError === exception + if ActionView::Template::Error === exception logger.fatal(exception.to_s) else logger.fatal( diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 400039353c..d480af876d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1,221 +1,193 @@ module ActionDispatch module Routing class Mapper - module Resources - class Resource #:nodoc: - attr_reader :plural, :singular - attr_reader :path_prefix, :name_prefix - - def initialize(entities, options = {}) - entities = entities.to_s - - @plural = entities.pluralize - @singular = entities.singularize - - @path_prefix = options[:path_prefix] - @name_prefix = options[:name_prefix] - end - - def name - plural - end - - def controller - plural + class Constraints + def self.new(app, constraints = []) + if constraints.any? + super(app, constraints) + else + app end + end - def member_name - if name_prefix - "#{name_prefix}_#{singular}" - else - singular - end - end + def initialize(app, constraints = []) + @app, @constraints = app, constraints + end - def collection_name - if name_prefix - "#{name_prefix}_#{plural}" - else - plural - end - end + def call(env) + req = Rack::Request.new(env) - def new_name - if name_prefix - "new_#{name_prefix}_#{singular}" - else - "new_#{singular}" + @constraints.each { |constraint| + if constraint.respond_to?(:matches?) && !constraint.matches?(req) + return [417, {}, []] + elsif constraint.respond_to?(:call) && !constraint.call(req) + return [417, {}, []] end - end + } - def edit_name - if name_prefix - "edit_#{name_prefix}_#{singular}" - else - "edit_#{singular}" - end - end + @app.call(env) end + end - class SingletonResource < Resource #:nodoc: - def initialize(entity, options = {}) - super(entity) - end + module Base + def initialize(set) + @set = set + end - def name - singular - end + def root(options = {}) + match '/', options.merge(:as => :root) end - def resource(*resources, &block) - options = resources.extract_options! + def match(*args) + options = args.extract_options! - if resources.length > 1 - raise ArgumentError if block_given? - resources.each { |r| resource(r, options) } - return self - end + path = args.first - name_prefix = @scope[:options][:name_prefix] if @scope[:options] - resource = SingletonResource.new(resources.pop, :name_prefix => name_prefix) + conditions, defaults = {}, {} - if @scope[:scope_level] == :resources - parent_resource = @scope[:scope_level_options][:name] - parent_named_prefix = @scope[:scope_level_options][:name_prefix] - with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do - resource(resource.name, options, &block) - end - end - return self - end + path = nil if path == "" + path = "#{@scope[:path]}#{path}" if @scope[:path] + path = Rack::Mount::Utils.normalize_path(path) if path - controller(resource.controller) do - namespace(resource.name) do - with_scope_level(:resource, :name => resource.singular, :name_prefix => resource.member_name) do - yield if block_given? + raise ArgumentError, "path is required" unless path - get "", :to => :show, :as => resource.member_name - post "", :to => :create - put "", :to => :update - delete "", :to => :destroy - get "new", :to => :new, :as => resource.new_name - get "edit", :to => :edit, :as => resource.edit_name - end - end + constraints = options[:constraints] || {} + unless constraints.is_a?(Hash) + block, constraints = constraints, {} end + blocks = ((@scope[:blocks] || []) + [block]).compact + constraints = (@scope[:constraints] || {}).merge(constraints) + options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - self - end + conditions[:path_info] = path + requirements = constraints.dup - def resources(*resources, &block) - options = resources.extract_options! - - if resources.length > 1 - raise ArgumentError if block_given? - resources.each { |r| resources(r, options) } - return self - end + path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) + segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names + constraints.reject! { |k, v| segment_keys.include?(k.to_s) } + conditions.merge!(constraints) - name_prefix = @scope[:options][:name_prefix] if @scope[:options] - resource = Resource.new(resources.pop, :name_prefix => name_prefix) + requirements[:controller] ||= @set.controller_constraints - if @scope[:scope_level] == :resources - parent_resource = @scope[:scope_level_options][:name] - parent_named_prefix = @scope[:scope_level_options][:name_prefix] - with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do - resources(resource.name, options, &block) - end - end - return self + if via = options[:via] + via = Array(via).map { |m| m.to_s.upcase } + conditions[:request_method] = Regexp.union(*via) end - controller(resource.controller) do - namespace(resource.name) do - with_scope_level(:resources, :name => resource.singular, :name_prefix => resource.member_name) do - yield if block_given? + defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] - collection do - get "", :to => :index, :as => resource.collection_name - post "", :to => :create - get "new", :to => :new, :as => resource.new_name - end + app = initialize_app_endpoint(options, defaults) + validate_defaults!(app, defaults, segment_keys) + app = Constraints.new(app, blocks) - member do - get "", :to => :show, :as => resource.member_name - put "", :to => :update - delete "", :to => :destroy - get "edit", :to => :edit, :as => resource.edit_name - end - end - end - end + @set.add_route(app, conditions, requirements, defaults, options[:as]) self end - def collection - unless @scope[:scope_level] == :resources - raise ArgumentError, "can't use collection outside resources scope" - end + private + def initialize_app_endpoint(options, defaults) + app = nil + + if options[:to].respond_to?(:call) + app = options[:to] + defaults.delete(:controller) + defaults.delete(:action) + elsif options[:to].is_a?(String) + defaults[:controller], defaults[:action] = options[:to].split('#') + elsif options[:to].is_a?(Symbol) + defaults[:action] = options[:to].to_s + end - with_scope_level(:collection) do - yield + app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) end - end - def member - unless @scope[:scope_level] == :resources - raise ArgumentError, "can't use member outside resources scope" - end + def validate_defaults!(app, defaults, segment_keys) + return unless app.is_a?(Routing::RouteSet::Dispatcher) - with_scope_level(:member) do - scope(":id") do - yield + unless defaults.include?(:controller) || segment_keys.include?("controller") + raise ArgumentError, "missing :controller" + end + + unless defaults.include?(:action) || segment_keys.include?("action") + raise ArgumentError, "missing :action" end end + end + + module HttpHelpers + def get(*args, &block) + map_method(:get, *args, &block) end - def match(*args) - options = args.extract_options! - args.push(options) + def post(*args, &block) + map_method(:post, *args, &block) + end - case options.delete(:on) - when :collection - return collection { match(*args) } - when :member - return member { match(*args) } - end + def put(*args, &block) + map_method(:put, *args, &block) + end - if @scope[:scope_level] == :resources - raise ArgumentError, "can't define route directly in resources scope" - end + def delete(*args, &block) + map_method(:delete, *args, &block) + end - super + def redirect(path, options = {}) + status = options[:status] || 301 + lambda { |env| + req = Rack::Request.new(env) + url = req.scheme + '://' + req.host + path + [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] + } end private - def with_scope_level(kind, options = {}) - old, @scope[:scope_level] = @scope[:scope_level], kind - old_options, @scope[:scope_level_options] = @scope[:scope_level_options], options - yield - ensure - @scope[:scope_level] = old - @scope[:scope_level_options] = old_options + def map_method(method, *args, &block) + options = args.extract_options! + options[:via] = method + args.push(options) + match(*args, &block) + self end end module Scoping - def self.extended(object) - object.instance_eval do - @scope = {} - end + def initialize(*args) + @scope = {} + super end def scope(*args) options = args.extract_options! + case args.first + when String + options[:path] = args.first + when Symbol + options[:controller] = args.first + end + + if path = options.delete(:path) + path_set = true + path, @scope[:path] = @scope[:path], Rack::Mount::Utils.normalize_path(@scope[:path].to_s + path.to_s) + else + path_set = false + end + + if name_prefix = options.delete(:name_prefix) + name_prefix_set = true + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix) + else + name_prefix_set = false + end + + if controller = options.delete(:controller) + controller_set = true + controller, @scope[:controller] = @scope[:controller], controller + else + controller_set = false + end + constraints = options.delete(:constraints) || {} unless constraints.is_a?(Hash) block, constraints = constraints, {} @@ -225,24 +197,12 @@ module ActionDispatch options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options) - path_set = controller_set = false - - case args.first - when String - path_set = true - path = args.first - path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" - when Symbol - controller_set = true - controller = args.first - controller, @scope[:controller] = @scope[:controller], controller - end - yield self ensure @scope[:path] = path if path_set + @scope[:name_prefix] = name_prefix if name_prefix_set @scope[:controller] = controller if controller_set @scope[:options] = options @scope[:blocks] = blocks @@ -254,7 +214,7 @@ module ActionDispatch end def namespace(path) - scope(path.to_s) { yield } + scope("/#{path}") { yield } end def constraints(constraints = {}) @@ -263,172 +223,244 @@ module ActionDispatch def match(*args) options = args.extract_options! + options = (@scope[:options] || {}).merge(options) + + if @scope[:name_prefix] && !options[:as].blank? + options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" + elsif @scope[:name_prefix] && options[:as] == "" + options[:as] = @scope[:name_prefix].to_s + end + args.push(options) super(*args) end end - module HttpHelpers - def get(*args, &block) - map_method(:get, *args, &block) - end + module Resources + class Resource #:nodoc: + attr_reader :plural, :singular - def post(*args, &block) - map_method(:post, *args, &block) - end + def initialize(entities, options = {}) + entities = entities.to_s - def put(*args, &block) - map_method(:put, *args, &block) - end + @plural = entities.pluralize + @singular = entities.singularize + end - def delete(*args, &block) - map_method(:delete, *args, &block) - end + def name + plural + end - def redirect(path, options = {}) - status = options[:status] || 301 - lambda { |env| - req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } - end + def controller + plural + end - private - def map_method(method, *args, &block) - options = args.extract_options! - options[:via] = method - args.push(options) - match(*args, &block) - self + def member_name + singular end - end - class Constraints - def new(app, constraints = []) - if constraints.any? - super(app, constraints) - else - app + def collection_name + plural + end + + def id_segment + ":#{singular}_id" end end - def initialize(app, constraints = []) - @app, @constraints = app, constraints + class SingletonResource < Resource #:nodoc: + def initialize(entity, options = {}) + super + end + + def name + singular + end end - def call(env) - req = Rack::Request.new(env) + def resource(*resources, &block) + options = resources.extract_options! - @constraints.each { |constraint| - if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [417, {}, []] - elsif constraint.respond_to?(:call) && !constraint.call(req) - return [417, {}, []] + if resources.length > 1 + raise ArgumentError if block_given? + resources.each { |r| resource(r, options) } + return self + end + + resource = SingletonResource.new(resources.pop) + + if @scope[:scope_level] == :resources + nested do + resource(resource.name, options, &block) end - } + return self + end - @app.call(env) - end - end + scope(:path => "/#{resource.name}", :controller => resource.controller) do + with_scope_level(:resource, resource) do + yield if block_given? - def initialize(set) - @set = set + get "(.:format)", :to => :show, :as => resource.member_name + post "(.:format)", :to => :create + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" + end + end - extend HttpHelpers - extend Scoping - extend Resources - end + self + end - def root(options = {}) - match '/', options.merge(:as => :root) - end + def resources(*resources, &block) + options = resources.extract_options! - def match(*args) - options = args.extract_options! + if resources.length > 1 + raise ArgumentError if block_given? + resources.each { |r| resources(r, options) } + return self + end - if args.length > 1 - args.each { |path| match(path, options) } - return self - end + resource = Resource.new(resources.pop) - if args.first.is_a?(Symbol) - return match(args.first.to_s, options.merge(:to => args.first.to_sym)) - end + if @scope[:scope_level] == :resources + nested do + resources(resource.name, options, &block) + end + return self + end - path = args.first + scope(:path => "/#{resource.name}", :controller => resource.controller) do + with_scope_level(:resources, resource) do + yield if block_given? - conditions, defaults = {}, {} + with_scope_level(:collection) do + get "(.:format)", :to => :index, :as => resource.collection_name + post "(.:format)", :to => :create + with_exclusive_name_prefix :new do + get "/new(.:format)", :to => :new, :as => resource.singular + end + end + + with_scope_level(:member) do + scope("/:id") do + get "(.:format)", :to => :show, :as => resource.member_name + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + with_exclusive_name_prefix :edit do + get "/edit(.:format)", :to => :edit, :as => resource.singular + end + end + end + end + end - path = nil if path == "" - path = Rack::Mount::Utils.normalize_path(path) if path - path = "#{@scope[:path]}#{path}" if @scope[:path] + self + end - raise ArgumentError, "path is required" unless path + def collection + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use collection outside resources scope" + end - constraints = options[:constraints] || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} + with_scope_level(:collection) do + scope(:name_prefix => parent_resource.collection_name, :as => "") do + yield + end + end end - blocks = ((@scope[:blocks] || []) + [block]).compact - constraints = (@scope[:constraints] || {}).merge(constraints) - options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - conditions[:path_info] = path - requirements = constraints.dup + def member + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use member outside resources scope" + end - path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) - segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names - constraints.reject! { |k, v| segment_keys.include?(k.to_s) } - conditions.merge!(constraints) + with_scope_level(:member) do + scope("/:id", :name_prefix => parent_resource.member_name, :as => "") do + yield + end + end + end - requirements[:controller] ||= @set.controller_constraints + def nested + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use nested outside resources scope" + end - if via = options[:via] - via = Array(via).map { |m| m.to_s.upcase } - conditions[:request_method] = Regexp.union(*via) + with_scope_level(:nested) do + scope("/#{parent_resource.id_segment}", :name_prefix => parent_resource.member_name) do + yield + end + end end - defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] + def match(*args) + options = args.extract_options! - app = initialize_app_endpoint(options, defaults) - validate_defaults!(app, defaults, segment_keys) - app = Constraints.new(app, blocks) + if args.length > 1 + args.each { |path| match(path, options) } + return self + end - @set.add_route(app, conditions, requirements, defaults, options[:as]) + if args.first.is_a?(Symbol) + with_exclusive_name_prefix(args.first) do + return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym)) + end + end - self - end + args.push(options) - private - def initialize_app_endpoint(options, defaults) - app = nil + case options.delete(:on) + when :collection + return collection { match(*args) } + when :member + return member { match(*args) } + end - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s + if @scope[:scope_level] == :resources + raise ArgumentError, "can't define route directly in resources scope" end - app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) + super end - def validate_defaults!(app, defaults, segment_keys) - return unless app.is_a?(Routing::RouteSet::Dispatcher) + protected + def parent_resource + @scope[:scope_level_resource] + end - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" + private + def with_exclusive_name_prefix(prefix) + begin + old_name_prefix = @scope[:name_prefix] + + if !old_name_prefix.blank? + @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}" + else + @scope[:name_prefix] = prefix.to_s + end + + yield + ensure + @scope[:name_prefix] = old_name_prefix + end end - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" + def with_scope_level(kind, resource = parent_resource) + old, @scope[:scope_level] = @scope[:scope_level], kind + old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource + yield + ensure + @scope[:scope_level] = old + @scope[:scope_level_resource] = old_resource end - end + end + + include Base + include HttpHelpers + include Scoping + include Resources end end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index a8073c2105..8afd42a293 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -18,36 +18,37 @@ module ActionDispatch def call(env) params = env[PARAMETERS_KEY] + prepare_params!(params) + + unless controller = controller(params) + return [417, {}, []] + end + + controller.action(params[:action]).call(env) + end + + def prepare_params!(params) merge_default_action!(params) split_glob_param!(params) if @glob_param + params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding) params[key] = URI.unescape(value) end end + end - unless controller = controller(params) - return [417, {}, []] - end - - if env['action_controller.recognize'] - [200, {}, params] - else - controller.action(params[:action]).call(env) + def controller(params) + if params && params.has_key?(:controller) + controller = "#{params[:controller].camelize}Controller" + ActiveSupport::Inflector.constantize(controller) end + rescue NameError + nil end private - def controller(params) - if params && params.has_key?(:controller) - controller = "#{params[:controller].camelize}Controller" - ActiveSupport::Inflector.constantize(controller) - end - rescue NameError - nil - end - def merge_default_action!(params) params[:action] ||= 'index' end @@ -216,7 +217,14 @@ module ActionDispatch def draw(&block) clear! - Mapper.new(self).instance_exec(DeprecatedMapper.new(self), &block) + + mapper = Mapper.new(self) + if block.arity == 1 + mapper.instance_exec(DeprecatedMapper.new(self), &block) + else + mapper.instance_exec(&block) + end + @set.add_route(NotFound) install_helpers @set.freeze @@ -426,7 +434,7 @@ module ActionDispatch raise ActionController::RoutingError, "No route matches #{options.inspect}" end - uri, params = result + path, params = result params.each do |k, v| if v params[k] = v @@ -435,16 +443,10 @@ module ActionDispatch end end - uri << "?#{params.to_query}" if uri && params.any? - path = uri - if path && method == :generate_extras - uri = URI(path) - extras = uri.query ? - Rack::Utils.parse_nested_query(uri.query).keys.map { |k| k.to_sym } : - [] - [uri.path, extras] + [path, params.keys] elsif path + path << "?#{params.to_query}" if params.any? path else raise ActionController::RoutingError, "No route matches #{options.inspect}" @@ -455,37 +457,11 @@ module ActionDispatch def call(env) @set.call(env) - rescue ActionController::RoutingError => e - raise e if env['action_controller.rescue_error'] == false - - method, path = env['REQUEST_METHOD'].downcase.to_sym, env['PATH_INFO'] - - # Route was not recognized. Try to find out why (maybe wrong verb). - allows = HTTP_METHODS.select { |verb| - begin - recognize_path(path, {:method => verb}, false) - rescue ActionController::RoutingError - nil - end - } - - if !HTTP_METHODS.include?(method) - raise ActionController::NotImplemented.new(*allows) - elsif !allows.empty? - raise ActionController::MethodNotAllowed.new(*allows) - else - raise e - end - end - - def recognize(request) - params = recognize_path(request.path, extract_request_environment(request)) - request.path_parameters = params.with_indifferent_access - "#{params[:controller].to_s.camelize}Controller".constantize end - def recognize_path(path, environment = {}, rescue_error = true) + def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase + path = Rack::Mount::Utils.normalize_path(path) begin env = Rack::MockRequest.env_for(path, {:method => method}) @@ -493,16 +469,16 @@ module ActionDispatch raise ActionController::RoutingError, e.message end - env['action_controller.recognize'] = true - env['action_controller.rescue_error'] = rescue_error - status, headers, body = call(env) - body - end + req = Rack::Request.new(env) + @set.recognize(req) do |route, params| + dispatcher = route.app + if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params) + dispatcher.prepare_params!(params) + return params + end + end - # Subclasses and plugins may override this method to extract further attributes - # from the request, for use by route conditions and such. - def extract_request_environment(request) - { :method => request.method } + raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" end end end diff --git a/actionpack/lib/action_dispatch/test_case.rb b/actionpack/lib/action_dispatch/test_case.rb new file mode 100644 index 0000000000..afd708f06f --- /dev/null +++ b/actionpack/lib/action_dispatch/test_case.rb @@ -0,0 +1,6 @@ +require "action_dispatch/testing/assertions" +require "action_dispatch/testing/integration" +require "action_dispatch/testing/performance_test" +require "action_dispatch/testing/test_request" +require "action_dispatch/testing/test_response" +require "action_dispatch/testing/integration"
\ No newline at end of file diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index e6d6b5a3ef..4bc5275e04 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -134,9 +134,11 @@ module ActionDispatch # Assume given controller request = ActionController::TestRequest.new request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method - request.path = path + request.path = path + + params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method }) + request.path_parameters = params.with_indifferent_access - ActionController::Routing::Routes.recognize(request) request end end diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index b1ed9d31f4..1b9a6c18b7 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,15 +1,17 @@ require 'active_support/testing/performance' require 'active_support/testing/default' -module ActionDispatch - # 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 < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Performance - include ActiveSupport::Testing::Default +if defined?(ActiveSupport::Testing::Performance) + module ActionDispatch + # 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 < ActionDispatch::IntegrationTest + include ActiveSupport::Testing::Performance + include ActiveSupport::Testing::Default + end end -end +end
\ No newline at end of file diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index f6f7ec0c8c..06238ca747 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -24,27 +24,29 @@ require File.join(File.dirname(__FILE__), "action_pack") module ActionView - def self.load_all! - [Context, Base, TemplateError] + extend ActiveSupport::Autoload + + autoload :Base + autoload :Context + autoload :Template + autoload :Helpers + autoload :SafeBuffer + + + autoload_under "render" do + autoload :Partials + autoload :Rendering end - autoload :Base, 'action_view/base' - autoload :Context, 'action_view/context' - autoload :Helpers, 'action_view/helpers' autoload :MissingTemplate, 'action_view/base' - autoload :Partials, 'action_view/render/partials' autoload :Resolver, 'action_view/template/resolver' autoload :PathResolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' - autoload :Rendering, 'action_view/render/rendering' - 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 :FileSystemResolverWithFallback, 'action_view/template/resolver' - autoload :SafeBuffer, 'action_view/safe_buffer' + + autoload :TemplateError, 'action_view/template/error' + autoload :TemplateHandler, 'action_view/template' + autoload :TemplateHandlers, 'action_view/template' end require 'action_view/erb/util' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c33695770f..d69e5109fa 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -196,7 +196,7 @@ module ActionView #:nodoc: end class << self - delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' + delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 23bde61f9c..0059b79e5f 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -4,7 +4,7 @@ module ActionView #:nodoc: # TODO: Clean this up if obj.is_a?(String) if cache.nil? - cache = !defined?(Rails) || Rails.application.config.cache_classes + cache = !defined?(Rails.application) || Rails.application.config.cache_classes end FileSystemResolverWithFallback.new(obj, :cache => cache) else diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template.rb index d1970ca3c7..210ad508f5 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -6,7 +6,14 @@ require "action_view/template/resolver" module ActionView class Template - extend TemplateHandlers + extend ActiveSupport::Autoload + + autoload :Error + autoload :Handler + autoload :Handlers + autoload :Text + + extend Template::Handlers attr_reader :source, :identifier, :handler, :mime_type, :formats, :details def initialize(source, identifier, handler, details) @@ -32,11 +39,11 @@ module ActionView view.send(method_name, locals, &block) end rescue Exception => e - if e.is_a?(TemplateError) + if e.is_a?(Template::Error) e.sub_template_of(self) raise e else - raise TemplateError.new(self, view.assigns, e) + raise Template::Error.new(self, view.assigns, e) end end @@ -103,7 +110,7 @@ module ActionView logger.debug "Backtrace: #{e.backtrace.join("\n")}" end - raise ActionView::TemplateError.new(self, {}, e) + raise ActionView::Template::Error.new(self, {}, e) end end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index aa21606f76..648f708d3d 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,101 +1,105 @@ 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. - class TemplateError < ActionViewError #:nodoc: - SOURCE_CODE_RADIUS = 3 + class Template + # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a + # bunch of intimate details and uses it to report a very precise exception message. + class Error < ActionViewError #:nodoc: + SOURCE_CODE_RADIUS = 3 - attr_reader :original_exception + attr_reader :original_exception - def initialize(template, assigns, original_exception) - @template, @assigns, @original_exception = template, assigns.dup, original_exception - @backtrace = compute_backtrace - end + def initialize(template, assigns, original_exception) + @template, @assigns, @original_exception = template, assigns.dup, original_exception + @backtrace = compute_backtrace + end - def file_name - @template.identifier - end + def file_name + @template.identifier + end - def message - ActiveSupport::Deprecation.silence { original_exception.message } - end + def message + ActiveSupport::Deprecation.silence { original_exception.message } + end - def clean_backtrace - if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) - Rails.backtrace_cleaner.clean(original_exception.backtrace) - else - original_exception.backtrace + def clean_backtrace + if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) + Rails.backtrace_cleaner.clean(original_exception.backtrace) + else + original_exception.backtrace + end end - end - def sub_template_message - if @sub_templates - "Trace of template inclusion: " + - @sub_templates.collect { |template| template.inspect }.join(", ") - else - "" + def sub_template_message + if @sub_templates + "Trace of template inclusion: " + + @sub_templates.collect { |template| template.inspect }.join(", ") + else + "" + end end - end - def source_extract(indentation = 0) - return unless num = line_number - num = num.to_i + def source_extract(indentation = 0) + return unless num = line_number + num = num.to_i - source_code = @template.source.split("\n") + source_code = @template.source.split("\n") - start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max - end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min + start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max + end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min - indent = ' ' * indentation - line_counter = start_on_line - return unless source_code = source_code[start_on_line..end_on_line] + indent = ' ' * indentation + line_counter = start_on_line + return unless source_code = source_code[start_on_line..end_on_line] - source_code.sum do |line| - line_counter += 1 - "#{indent}#{line_counter}: #{line}\n" + source_code.sum do |line| + line_counter += 1 + "#{indent}#{line_counter}: #{line}\n" + end end - end - - def sub_template_of(template_path) - @sub_templates ||= [] - @sub_templates << template_path - end - - def line_number - @line_number ||= - if file_name - regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } - end - end + def sub_template_of(template_path) + @sub_templates ||= [] + @sub_templates << template_path + end - def to_s - "\n#{self.class} (#{message}) #{source_location}:\n" + - "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" - end + def line_number + @line_number ||= + if file_name + regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - # don't do anything nontrivial here. Any raised exception from here becomes fatal - # (and can't be rescued). - def backtrace - @backtrace - end + $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } + end + end - private - def compute_backtrace - [ - "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + - clean_backtrace.join("\n ") - ] + def to_s + "\n#{self.class} (#{message}) #{source_location}:\n" + + "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" end - def source_location - if line_number - "on line ##{line_number} of " - else - 'in ' - end + file_name + # don't do anything nontrivial here. Any raised exception from here becomes fatal + # (and can't be rescued). + def backtrace + @backtrace end + + private + def compute_backtrace + [ + "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + + clean_backtrace.join("\n ") + ] + end + + def source_location + if line_number + "on line ##{line_number} of " + else + 'in ' + end + file_name + end + end end -end
\ No newline at end of file + + TemplateError = Template::Error +end diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 4bf58b9fa8..5a46a27893 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -3,34 +3,39 @@ require "action_dispatch/http/mime_type" # Legacy TemplateHandler stub module ActionView - module TemplateHandlers #:nodoc: - module Compilable - def self.included(base) - base.extend(ClassMethods) - end + class Template + module Handlers #:nodoc: + module Compilable + def self.included(base) + base.extend(ClassMethods) + end - module ClassMethods - def call(template) - new.compile(template) + module ClassMethods + def call(template) + new.compile(template) + end end - end - def compile(template) - raise "Need to implement #{self.class.name}#compile(template)" - end + def compile(template) + raise "Need to implement #{self.class.name}#compile(template)" + end + end end - end - class TemplateHandler - extlib_inheritable_accessor :default_format - self.default_format = Mime::HTML + class Template::Handler + extlib_inheritable_accessor :default_format + self.default_format = Mime::HTML - def self.call(template) - raise "Need to implement #{self.class.name}#call(template)" - end + def self.call(template) + raise "Need to implement #{self.class.name}#call(template)" + end - def render(template, local_assigns) - raise "Need to implement #{self.class.name}#render(template, local_assigns)" + def render(template, local_assigns) + raise "Need to implement #{self.class.name}#render(template, local_assigns)" + end end end + + TemplateHandlers = Template::Handlers + TemplateHandler = Template::Handler end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index faf54b9fe5..35488c0391 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -1,52 +1,54 @@ module ActionView #:nodoc: - module TemplateHandlers #:nodoc: - autoload :ERB, 'action_view/template/handlers/erb' - autoload :RJS, 'action_view/template/handlers/rjs' - autoload :Builder, 'action_view/template/handlers/builder' - - def self.extended(base) - base.register_default_template_handler :erb, TemplateHandlers::ERB - base.register_template_handler :rjs, TemplateHandlers::RJS - base.register_template_handler :builder, TemplateHandlers::Builder - - # TODO: Depreciate old template extensions - base.register_template_handler :rhtml, TemplateHandlers::ERB - base.register_template_handler :rxml, TemplateHandlers::Builder - end - - @@template_handlers = {} - @@default_template_handlers = nil + class Template + module Handlers #:nodoc: + autoload :ERB, 'action_view/template/handlers/erb' + autoload :RJS, 'action_view/template/handlers/rjs' + autoload :Builder, 'action_view/template/handlers/builder' + + def self.extended(base) + base.register_default_template_handler :erb, ERB + base.register_template_handler :rjs, RJS + base.register_template_handler :builder, Builder + + # TODO: Depreciate old template extensions + base.register_template_handler :rhtml, ERB + base.register_template_handler :rxml, Builder + end + + @@template_handlers = {} + @@default_template_handlers = nil - def self.extensions - @@template_handlers.keys - end - - # Register a class that knows how to handle template files with the given - # extension. This can be used to implement new template types. - # The constructor for the class must take the ActiveView::Base instance - # as a parameter, and the class must implement a +render+ method that - # takes the contents of the template to render as well as the Hash of - # local assigns available to the template. The +render+ method ought to - # return the rendered template as a string. - def register_template_handler(extension, klass) - @@template_handlers[extension.to_sym] = klass - end - - def template_handler_extensions - @@template_handlers.keys.map {|key| key.to_s }.sort - end - - def registered_template_handler(extension) - extension && @@template_handlers[extension.to_sym] - end - - def register_default_template_handler(extension, klass) - register_template_handler(extension, klass) - @@default_template_handlers = klass - end - - def handler_class_for_extension(extension) - (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + def self.extensions + @@template_handlers.keys + end + + # Register a class that knows how to handle template files with the given + # extension. This can be used to implement new template types. + # The constructor for the class must take the ActiveView::Base instance + # as a parameter, and the class must implement a +render+ method that + # takes the contents of the template to render as well as the Hash of + # local assigns available to the template. The +render+ method ought to + # return the rendered template as a string. + def register_template_handler(extension, klass) + @@template_handlers[extension.to_sym] = klass + end + + def template_handler_extensions + @@template_handlers.keys.map {|key| key.to_s }.sort + end + + def registered_template_handler(extension) + extension && @@template_handlers[extension.to_sym] + end + + def register_default_template_handler(extension, klass) + register_template_handler(extension, klass) + @@default_template_handlers = klass + end + + def handler_class_for_extension(extension) + (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + end end end end diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 5f381f7bf0..a93cfca8aa 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class Builder < TemplateHandler + module Template::Handlers + class Builder < Template::Handler include Compilable self.default_format = Mime::XML diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 88aeb4b053..f8e6376589 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/string/output_safety' require 'erubis' module ActionView - module TemplateHandlers + module Template::Handlers class Erubis < ::Erubis::Eruby def add_preamble(src) src << "@output_buffer = ActionView::SafeBuffer.new;" @@ -26,7 +26,7 @@ module ActionView end end - class ERB < TemplateHandler + class ERB < Template::Handler include Compilable ## diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index b1d15dc209..63e7dc0902 100644 --- a/actionpack/lib/action_view/template/handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class RJS < TemplateHandler + module Template::Handlers + class RJS < Template::Handler include Compilable self.default_format = Mime::JS diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 7336114e1b..a2f4ab2ef5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,6 +1,6 @@ require "pathname" require "active_support/core_ext/class" -require "action_view/template/template" +require "action_view/template" module ActionView # Abstract superclass @@ -20,7 +20,7 @@ module ActionView register_detail(:locale) { [I18n.locale] } register_detail(:formats) { Mime::SET.symbols } register_detail(:handlers, :allow_nil => false) do - TemplateHandlers.extensions + Template::Handlers.extensions end def initialize(options = {}) @@ -65,7 +65,7 @@ module ActionView # as well as incorrectly putting part of the path in the template # name instead of the prefix. def normalize_name(name, prefix) - handlers = TemplateHandlers.extensions.join('|') + handlers = Template::Handlers.extensions.join('|') name = name.to_s.gsub(/\.(?:#{handlers})$/, '') parts = name.split('/') diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index f6e011a5ab..67e086d8bd 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,38 +1,40 @@ module ActionView #:nodoc: - class TextTemplate < String #:nodoc: - HTML = Mime[:html] - - def initialize(string, content_type = HTML) - super(string.to_s) - @content_type = Mime[content_type] || content_type - end - - def details - {:formats => [@content_type.to_sym]} - end - - def identifier - self - end - - def inspect - 'text template' - end - - def render(*args) - to_s - end - - def mime_type - @content_type - end - - def formats - [mime_type] - end - - def partial? - false + class Template + class Text < String #:nodoc: + HTML = Mime[:html] + + def initialize(string, content_type = HTML) + super(string.to_s) + @content_type = Mime[content_type] || content_type + end + + def details + {:formats => [@content_type.to_sym]} + end + + def identifier + self + end + + def inspect + 'text template' + end + + def render(*args) + to_s + end + + def mime_type + @content_type + end + + def formats + [mime_type] + end + + def partial? + false + end end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 86bbad822d..ab5bc49cf9 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,5 +1,5 @@ require 'active_support/test_case' -require 'action_controller/testing/test_case' +require 'action_controller/test_case' module ActionView class Base |