Reorganize autoloads:

* A new module (ActiveSupport::Autoload) is provide that extends
    autoloading with new behavior.
  * All autoloads in modules that have extended ActiveSupport::Autoload
    will be eagerly required in threadsafe environments
  * Autoloads can optionally leave off the path if the path is the same
    as full_constant_name.underscore
  * It is possible to specify that a group of autoloads live under an
    additional path. For instance, all of ActionDispatch's middlewares
    are ActionDispatch::MiddlewareName, but they live under 
    "action_dispatch/middlewares/middleware_name"
  * It is possible to specify that a group of autoloads are all found
    at the same path. For instance, a number of exceptions might all
    be declared there.
  * One consequence of this is that testing-related constants are not
    autoloaded. To get the testing helpers for a given component,
    require "component_name/test_case". For instance, "action_controller/test_case".
  * test_help.rb, which is automatically required by a Rails application's
    test helper, requires the test_case.rb for all active components, so
    this change will not be disruptive in existing or new applications.
This commit is contained in:
Carlhuda 2009-12-02 20:01:01 -08:00
parent 399909b11c
commit c1304098cc
52 changed files with 1018 additions and 822 deletions

View File

@ -25,32 +25,34 @@ actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib"
$:.unshift(actionpack_path) if File.directory?(actionpack_path)
require 'action_controller'
require 'action_view'
require 'active_support/autoload'
module ActionMailer
def self.load_all!
[Base, Part, ::Text::Format, ::Net::SMTP]
end
extend ::ActiveSupport::Autoload
autoload :AdvAttrAccessor, 'action_mailer/adv_attr_accessor'
autoload :DeprecatedBody, 'action_mailer/deprecated_body'
autoload :Base, 'action_mailer/base'
autoload :DeliveryMethod, 'action_mailer/delivery_method'
autoload :Part, 'action_mailer/part'
autoload :PartContainer, 'action_mailer/part_container'
autoload :Quoting, 'action_mailer/quoting'
autoload :TestCase, 'action_mailer/test_case'
autoload :TestHelper, 'action_mailer/test_helper'
autoload :Utils, 'action_mailer/utils'
autoload :AdvAttrAccessor
autoload :DeprecatedBody
autoload :Base
autoload :DeliveryMethod
autoload :MailHelper
autoload :Part
autoload :PartContainer
autoload :Quoting
autoload :TestHelper
autoload :Utils
end
module Text
extend ActiveSupport::Autoload
autoload :Format, 'action_mailer/vendor/text_format'
end
module Net
autoload :SMTP, 'net/smtp'
extend ActiveSupport::Autoload
autoload :SMTP
end
autoload :MailHelper, 'action_mailer/mail_helper'
require 'action_mailer/vendor/tmail'

View File

@ -258,7 +258,7 @@ module ActionMailer #:nodoc:
include AbstractController::Layouts
include AbstractController::Helpers
helper MailHelper
helper ActionMailer::MailHelper
if Object.const_defined?(:ActionController)
include ActionController::UrlWriter

View File

@ -6,7 +6,7 @@ module ActionMailer
# A delivery method implementation which writes all mails to a file.
class File < Method
self.settings = {
:location => defined?(Rails) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
}
def perform_delivery(mail)

View File

@ -1,17 +1,19 @@
module MailHelper
# Uses Text::Format to take the text and format it, indented two spaces for
# each line, and wrapped at 72 columns.
def block_format(text)
formatted = text.split(/\n\r\n/).collect { |paragraph|
Text::Format.new(
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
).format
}.join("\n")
module ActionMailer
module MailHelper
# Uses Text::Format to take the text and format it, indented two spaces for
# each line, and wrapped at 72 columns.
def block_format(text)
formatted = text.split(/\n\r\n/).collect { |paragraph|
Text::Format.new(
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
).format
}.join("\n")
# Make list points stand on their own line
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
# Make list points stand on their own line
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
formatted
formatted
end
end
end
end

View File

@ -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

View File

@ -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, {})

View File

@ -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'
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 :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
autoload :HTML, 'action_controller/vendor/html-scanner'
autoload :AbstractController, 'abstract_controller'
# 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"

View File

@ -24,7 +24,7 @@ module ActionController
include ActionController::MimeResponds
# Rails 2.x compatibility
include ActionController::Rails2Compatibility
include ActionController::Compatibility
include ActionController::Cookies
include ActionController::Session

View File

@ -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'

View File

@ -1,2 +1,4 @@
require "action_dispatch/testing/integration"
ActionController::Integration = ActionDispatch::Integration
ActionController::IntegrationTest = ActionDispatch::IntegrationTest

View File

@ -1,5 +1,5 @@
module ActionController
module Rails2Compatibility
module Compatibility
extend ActiveSupport::Concern
class ::ActionController::ActionControllerError < StandardError #:nodoc:

View File

@ -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

View File

@ -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:

View File

@ -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'

View File

@ -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_under "http" do
autoload :Request
autoload :Response
autoload :StatusCodes
autoload :Utils
end
autoload_under "middleware" do
autoload :Callbacks
autoload :ParamsParser
autoload :Rescue
autoload :ShowExceptions
autoload :Static
autoload :StringCoercion
end
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 :Routing, 'action_dispatch/routing'
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 :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

View File

@ -29,7 +29,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'},
@ -119,7 +119,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(

View File

@ -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"

View File

@ -24,27 +24,25 @@
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'
end
require 'action_view/erb/util'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,101 +1,103 @@
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 file_name
@template.identifier
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 initialize(template, assigns, original_exception)
@template, @assigns, @original_exception = template, assigns.dup, original_exception
@backtrace = compute_backtrace
end
end
def sub_template_message
if @sub_templates
"Trace of template inclusion: " +
@sub_templates.collect { |template| template.inspect }.join(", ")
else
""
def file_name
@template.identifier
end
end
def source_extract(indentation = 0)
return unless num = line_number
num = num.to_i
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
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"
def message
ActiveSupport::Deprecation.silence { original_exception.message }
end
end
def sub_template_of(template_path)
@sub_templates ||= []
@sub_templates << template_path
end
def clean_backtrace
if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
Rails.backtrace_cleaner.clean(original_exception.backtrace)
else
original_exception.backtrace
end
end
def line_number
@line_number ||=
if file_name
regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
def sub_template_message
if @sub_templates
"Trace of template inclusion: " +
@sub_templates.collect { |template| template.inspect }.join(", ")
else
""
end
end
$1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
def source_extract(indentation = 0)
return unless num = line_number
num = num.to_i
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
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"
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 to_s
"\n#{self.class} (#{message}) #{source_location}:\n" +
"#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
end
# 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
def to_s
"\n#{self.class} (#{message}) #{source_location}:\n" +
"#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
end
# 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

View File

@ -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
module ClassMethods
def call(template)
new.compile(template)
class Template
module Handlers #:nodoc:
module Compilable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def call(template)
new.compile(template)
end
end
def compile(template)
raise "Need to implement #{self.class.name}#compile(template)"
end
end
end
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 compile(template)
raise "Need to implement #{self.class.name}#compile(template)"
end
end
end
class TemplateHandler
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 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

View File

@ -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'
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, TemplateHandlers::ERB
base.register_template_handler :rjs, TemplateHandlers::RJS
base.register_template_handler :builder, TemplateHandlers::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, TemplateHandlers::ERB
base.register_template_handler :rxml, TemplateHandlers::Builder
end
# TODO: Depreciate old template extensions
base.register_template_handler :rhtml, ERB
base.register_template_handler :rxml, Builder
end
@@template_handlers = {}
@@default_template_handlers = nil
@@template_handlers = {}
@@default_template_handlers = nil
def self.extensions
@@template_handlers.keys
end
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
# 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 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 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 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 handler_class_for_extension(extension)
(extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
end
end
end
end

View File

@ -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

View File

@ -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
##

View File

@ -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

View File

@ -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('/')

View File

@ -1,38 +1,40 @@
module ActionView #:nodoc:
class TextTemplate < String #:nodoc:
HTML = Mime[:html]
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 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 details
{:formats => [@content_type.to_sym]}
end
def identifier
self
end
def identifier
self
end
def inspect
'text template'
end
def inspect
'text template'
end
def render(*args)
to_s
end
def render(*args)
to_s
end
def mime_type
@content_type
end
def mime_type
@content_type
end
def formats
[mime_type]
end
def formats
[mime_type]
end
def partial?
false
def partial?
false
end
end
end
end

View File

@ -1,5 +1,5 @@
require 'active_support/test_case'
require 'action_controller/testing/test_case'
require 'action_controller/test_case'
module ActionView
class Base

View File

@ -23,7 +23,7 @@ module AbstractControllerTests
self.view_paths = []
def index
render :_template => ActionView::TextTemplate.new("Hello blank!")
render :_template => ActionView::Template::Text.new("Hello blank!")
end
end
@ -31,19 +31,19 @@ module AbstractControllerTests
layout "hello"
def index
render :_template => ActionView::TextTemplate.new("Hello string!")
render :_template => ActionView::Template::Text.new("Hello string!")
end
def overwrite_default
render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => :default
render :_template => ActionView::Template::Text.new("Hello string!"), :layout => :default
end
def overwrite_false
render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => false
render :_template => ActionView::Template::Text.new("Hello string!"), :layout => false
end
def overwrite_string
render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => "omg"
render :_template => ActionView::Template::Text.new("Hello string!"), :layout => "omg"
end
def overwrite_skip
@ -72,7 +72,7 @@ module AbstractControllerTests
layout :hello
def index
render :_template => ActionView::TextTemplate.new("Hello symbol!")
render :_template => ActionView::Template::Text.new("Hello symbol!")
end
private
def hello
@ -84,7 +84,7 @@ module AbstractControllerTests
layout :no_hello
def index
render :_template => ActionView::TextTemplate.new("Hello missing symbol!")
render :_template => ActionView::Template::Text.new("Hello missing symbol!")
end
private
def no_hello
@ -96,7 +96,7 @@ module AbstractControllerTests
layout :nilz
def index
render :_template => ActionView::TextTemplate.new("Hello nilz!")
render :_template => ActionView::Template::Text.new("Hello nilz!")
end
def nilz() end
@ -106,7 +106,7 @@ module AbstractControllerTests
layout :objekt
def index
render :_template => ActionView::TextTemplate.new("Hello nilz!")
render :_template => ActionView::Template::Text.new("Hello nilz!")
end
def objekt
@ -118,7 +118,7 @@ module AbstractControllerTests
layout :omg_no_method
def index
render :_template => ActionView::TextTemplate.new("Hello boom!")
render :_template => ActionView::Template::Text.new("Hello boom!")
end
end
@ -126,7 +126,7 @@ module AbstractControllerTests
layout "missing"
def index
render :_template => ActionView::TextTemplate.new("Hello missing!")
render :_template => ActionView::Template::Text.new("Hello missing!")
end
end
@ -134,7 +134,7 @@ module AbstractControllerTests
layout false
def index
render :_template => ActionView::TextTemplate.new("Hello false!")
render :_template => ActionView::Template::Text.new("Hello false!")
end
end
@ -142,7 +142,7 @@ module AbstractControllerTests
layout nil
def index
render :_template => ActionView::TextTemplate.new("Hello nil!")
render :_template => ActionView::Template::Text.new("Hello nil!")
end
end

View File

@ -38,7 +38,7 @@ module AbstractController
end
def object
render :_template => ActionView::TextTemplate.new("With Object")
render :_template => ActionView::Template::Text.new("With Object")
end
end

View File

@ -191,7 +191,7 @@ class IsolatedHelpersTest < Test::Unit::TestCase
end
def test_helper_in_a
assert_raise(ActionView::TemplateError) { call_controller(A, "index") }
assert_raise(ActionView::Template::Error) { call_controller(A, "index") }
end
def test_helper_in_b

View File

@ -115,7 +115,7 @@ class RendersNoLayoutController < LayoutTest
end
class LayoutSetInResponseTest < ActionController::TestCase
include ActionView::TemplateHandlers
include ActionView::Template::Handlers
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new

View File

@ -106,8 +106,8 @@ module RenderTestCases
def test_render_partial_with_errors
@view.render(:partial => "test/raise")
flunk "Render did not raise TemplateError"
rescue ActionView::TemplateError => e
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
assert_match "undefined local variable or method `doesnt_exist'", e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
@ -116,8 +116,8 @@ module RenderTestCases
def test_render_sub_template_with_errors
@view.render(:file => "test/sub_template_raise")
flunk "Render did not raise TemplateError"
rescue ActionView::TemplateError => e
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
assert_match "undefined local variable or method `doesnt_exist'", e.message
assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message
assert_equal "1", e.line_number

View File

@ -26,28 +26,31 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path)
require 'active_support'
module ActiveModel
autoload :AttributeMethods, 'active_model/attribute_methods'
autoload :Conversion, 'active_model/conversion'
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
autoload :Dirty, 'active_model/dirty'
autoload :Errors, 'active_model/errors'
autoload :Lint, 'active_model/lint'
autoload :Name, 'active_model/naming'
autoload :Naming, 'active_model/naming'
autoload :Observer, 'active_model/observing'
autoload :Observing, 'active_model/observing'
autoload :Serialization, 'active_model/serialization'
autoload :StateMachine, 'active_model/state_machine'
autoload :TestCase, 'active_model/test_case'
autoload :Translation, 'active_model/translation'
autoload :Validations, 'active_model/validations'
autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper'
autoload :Validator, 'active_model/validator'
autoload :VERSION, 'active_model/version'
extend ActiveSupport::Autoload
autoload :AttributeMethods
autoload :Conversion
autoload :DeprecatedErrorMethods
autoload :Dirty
autoload :Errors
autoload :Lint
autoload :Name, 'active_model/naming'
autoload :Naming
autoload :Observer, 'active_model/observing'
autoload :Observing
autoload :Serialization
autoload :StateMachine
autoload :Translation
autoload :Validations
autoload :ValidationsRepairHelper
autoload :Validator
autoload :VERSION
module Serializers
autoload :JSON, 'active_model/serializers/json'
autoload :Xml, 'active_model/serializers/xml'
extend ActiveSupport::Autoload
autoload :JSON
autoload :Xml
end
end

View File

@ -10,6 +10,7 @@ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
require 'config'
require 'active_model'
require 'active_model/test_case'
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true

View File

@ -2,6 +2,7 @@ require 'logger'
$:.unshift(File.dirname(__FILE__) + '/../../../activerecord/lib')
require 'active_record'
require 'active_record/test_case'
require 'active_record/fixtures'
module ActiveModel

View File

@ -32,64 +32,66 @@ require 'active_model'
require 'arel'
module ActiveRecord
# TODO: Review explicit loads to see if they will automatically be handled by the initializer.
def self.load_all!
[Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter]
end
extend ActiveSupport::Autoload
autoload :VERSION, 'active_record/version'
autoload :VERSION
autoload :ActiveRecordError, 'active_record/base'
autoload :ConnectionNotEstablished, 'active_record/base'
autoload :Aggregations, 'active_record/aggregations'
autoload :AssociationPreload, 'active_record/association_preload'
autoload :Associations, 'active_record/associations'
autoload :AttributeMethods, 'active_record/attribute_methods'
autoload :Attributes, 'active_record/attributes'
autoload :AutosaveAssociation, 'active_record/autosave_association'
autoload :Relation, 'active_record/relation'
autoload :Base, 'active_record/base'
autoload :Batches, 'active_record/batches'
autoload :Calculations, 'active_record/calculations'
autoload :Callbacks, 'active_record/callbacks'
autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match'
autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match'
autoload :Migration, 'active_record/migration'
autoload :Aggregations
autoload :AssociationPreload
autoload :Associations
autoload :AttributeMethods
autoload :Attributes
autoload :AutosaveAssociation
autoload :Relation
autoload :Base
autoload :Batches
autoload :Calculations
autoload :Callbacks
autoload :DynamicFinderMatch
autoload :DynamicScopeMatch
autoload :Migration
autoload :Migrator, 'active_record/migration'
autoload :NamedScope, 'active_record/named_scope'
autoload :NestedAttributes, 'active_record/nested_attributes'
autoload :Observer, 'active_record/observer'
autoload :QueryCache, 'active_record/query_cache'
autoload :Reflection, 'active_record/reflection'
autoload :Schema, 'active_record/schema'
autoload :SchemaDumper, 'active_record/schema_dumper'
autoload :Serialization, 'active_record/serialization'
autoload :SessionStore, 'active_record/session_store'
autoload :StateMachine, 'active_record/state_machine'
autoload :TestCase, 'active_record/test_case'
autoload :Timestamp, 'active_record/timestamp'
autoload :Transactions, 'active_record/transactions'
autoload :Types, 'active_record/types'
autoload :Validations, 'active_record/validations'
autoload :NamedScope
autoload :NestedAttributes
autoload :Observer
autoload :QueryCache
autoload :Reflection
autoload :Schema
autoload :SchemaDumper
autoload :Serialization
autoload :SessionStore
autoload :StateMachine
autoload :Timestamp
autoload :Transactions
autoload :Types
autoload :Validations
module AttributeMethods
autoload :BeforeTypeCast, 'active_record/attribute_methods/before_type_cast'
autoload :Dirty, 'active_record/attribute_methods/dirty'
autoload :PrimaryKey, 'active_record/attribute_methods/primary_key'
autoload :Query, 'active_record/attribute_methods/query'
autoload :Read, 'active_record/attribute_methods/read'
autoload :TimeZoneConversion, 'active_record/attribute_methods/time_zone_conversion'
autoload :Write, 'active_record/attribute_methods/write'
extend ActiveSupport::Autoload
autoload :BeforeTypeCast
autoload :Dirty
autoload :PrimaryKey
autoload :Query
autoload :Read
autoload :TimeZoneConversion
autoload :Write
end
module Attributes
autoload :Aliasing, 'active_record/attributes/aliasing'
autoload :Store, 'active_record/attributes/store'
autoload :Typecasting, 'active_record/attributes/typecasting'
extend ActiveSupport::Autoload
autoload :Aliasing
autoload :Store
autoload :Typecasting
end
module Type
extend ActiveSupport::Autoload
autoload :Number, 'active_record/types/number'
autoload :Object, 'active_record/types/object'
autoload :Serialize, 'active_record/types/serialize'
@ -98,12 +100,16 @@ module ActiveRecord
end
module Locking
autoload :Optimistic, 'active_record/locking/optimistic'
autoload :Pessimistic, 'active_record/locking/pessimistic'
extend ActiveSupport::Autoload
autoload :Optimistic
autoload :Pessimistic
end
module ConnectionAdapters
autoload :AbstractAdapter, 'active_record/connection_adapters/abstract_adapter'
extend ActiveSupport::Autoload
autoload :AbstractAdapter
end
end

View File

@ -30,11 +30,13 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path)
require 'active_model'
module ActiveResource
autoload :Base, 'active_resource/base'
autoload :Connection, 'active_resource/connection'
autoload :CustomMethods, 'active_resource/custom_methods'
autoload :Formats, 'active_resource/formats'
autoload :Observing, 'active_resource/observing'
autoload :Validations, 'active_resource/validations'
autoload :HttpMock, 'active_resource/http_mock'
extend ActiveSupport::Autoload
autoload :Base
autoload :Connection
autoload :CustomMethods
autoload :Formats
autoload :Observing
autoload :Validations
autoload :HttpMock
end

View File

@ -13,6 +13,7 @@ require 'test/unit'
require 'active_support'
require 'active_support/test_case'
require 'active_resource'
require 'active_model/test_case'
$:.unshift "#{File.dirname(__FILE__)}/../test"
require 'setter_trap'

View File

@ -543,9 +543,9 @@ class BaseTest < Test::Unit::TestCase
assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male')
assert Person.collection_path(:gender => 'male', :student => true).include?('student=true')
assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
assert_equal '/people.xml?name[]=bob&name[]=your+uncle%2Bme&name[]=&name[]=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'})
assert_equal '/people.xml?struct[a][]=2&struct[a][]=1&struct[b]=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'})
end
def test_custom_element_path
@ -581,7 +581,7 @@ class BaseTest < Test::Unit::TestCase
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work')
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work')
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1)
assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
assert_equal '/people/1/addresses/1.xml?type[]=work&type[]=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
end
def test_custom_element_path_with_prefix_and_parameters

View File

@ -1,28 +1,31 @@
require "active_support/dependencies/autoload"
module ActiveSupport
autoload :BacktraceCleaner, 'active_support/backtrace_cleaner'
autoload :Base64, 'active_support/base64'
autoload :BasicObject, 'active_support/basic_object'
autoload :Benchmarkable, 'active_support/benchmarkable'
autoload :BufferedLogger, 'active_support/buffered_logger'
autoload :Cache, 'active_support/cache'
autoload :Callbacks, 'active_support/callbacks'
autoload :Concern, 'active_support/concern'
autoload :Configurable, 'active_support/configurable'
autoload :DependencyModule, 'active_support/dependency_module'
autoload :DeprecatedCallbacks, 'active_support/deprecated_callbacks'
autoload :Deprecation, 'active_support/deprecation'
autoload :Gzip, 'active_support/gzip'
autoload :Inflector, 'active_support/inflector'
autoload :Memoizable, 'active_support/memoizable'
autoload :MessageEncryptor, 'active_support/message_encryptor'
autoload :MessageVerifier, 'active_support/message_verifier'
autoload :Multibyte, 'active_support/multibyte'
autoload :OptionMerger, 'active_support/option_merger'
autoload :OrderedHash, 'active_support/ordered_hash'
autoload :OrderedOptions, 'active_support/ordered_options'
autoload :Notifications, 'active_support/notifications'
autoload :Rescuable, 'active_support/rescuable'
autoload :SecureRandom, 'active_support/secure_random'
autoload :StringInquirer, 'active_support/string_inquirer'
autoload :XmlMini, 'active_support/xml_mini'
extend ActiveSupport::Autoload
autoload :BacktraceCleaner
autoload :Base64
autoload :BasicObject
autoload :Benchmarkable
autoload :BufferedLogger
autoload :Cache
autoload :Callbacks
autoload :Concern
autoload :Configurable
autoload :DeprecatedCallbacks
autoload :Deprecation
autoload :Gzip
autoload :Inflector
autoload :Memoizable
autoload :MessageEncryptor
autoload :MessageVerifier
autoload :Multibyte
autoload :OptionMerger
autoload :OrderedHash
autoload :OrderedOptions
autoload :Notifications
autoload :Rescuable
autoload :SecureRandom
autoload :StringInquirer
autoload :XmlMini
end

View File

@ -0,0 +1,37 @@
require "active_support/inflector/methods"
module ActiveSupport
module Autoload
@@autoloads = {}
@@under_path = nil
@@at_path = nil
def autoload(const_name, path = @@at_path)
full = [self.name, @@under_path, const_name.to_s, path].compact.join("::")
location = path || Inflector.underscore(full)
@@autoloads[const_name] = location
super const_name, location
end
def autoload_under(path)
@@under_path, old_path = path, @@under_path
yield
ensure
@@under_path = old_path
end
def autoload_at(path)
@@at_path, old_path = path, @@at_path
yield
ensure
@@at_path = old_path
end
def self.eager_autoload!
@@autoloads.values.each {|file| require file }
end
end
end

View File

@ -1,450 +1,454 @@
require 'ruby-prof'
begin
require 'ruby-prof'
require 'fileutils'
require 'rails/version'
require 'fileutils'
require 'rails/version'
require 'active_support/core_ext/class/delegating_attributes'
module ActiveSupport
module Testing
module Performance
DEFAULTS =
if benchmark = ARGV.include?('--benchmark') # HAX for rake test
{ :benchmark => true,
:runs => 4,
:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time],
:output => 'tmp/performance' }
else
{ :benchmark => false,
:runs => 1,
:min_percent => 0.01,
:metrics => [:process_time, :memory, :objects],
:formats => [:flat, :graph_html, :call_tree],
:output => 'tmp/performance' }
end.freeze
module ActiveSupport
module Testing
module Performance
DEFAULTS =
if benchmark = ARGV.include?('--benchmark') # HAX for rake test
{ :benchmark => true,
:runs => 4,
:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time],
:output => 'tmp/performance' }
else
{ :benchmark => false,
:runs => 1,
:min_percent => 0.01,
:metrics => [:process_time, :memory, :objects],
:formats => [:flat, :graph_html, :call_tree],
:output => 'tmp/performance' }
end.freeze
def self.included(base)
base.superclass_delegating_accessor :profile_options
base.profile_options = DEFAULTS
end
def full_test_name
"#{self.class.name}##{method_name}"
end
def run(result)
return if method_name =~ /^default_test$/
yield(self.class::STARTED, name)
@_result = result
run_warmup
if profile_options && metrics = profile_options[:metrics]
metrics.each do |metric_name|
if klass = Metrics[metric_name.to_sym]
run_profile(klass.new)
result.add_run
end
end
def self.included(base)
base.superclass_delegating_accessor :profile_options
base.profile_options = DEFAULTS
end
yield(self.class::FINISHED, name)
end
def full_test_name
"#{self.class.name}##{method_name}"
end
def run_test(metric, mode)
run_callbacks :setup
setup
metric.send(mode) { __send__ @method_name }
rescue ::Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue StandardError, ScriptError
add_error($!)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
def run(result)
return if method_name =~ /^default_test$/
yield(self.class::STARTED, name)
@_result = result
run_warmup
if profile_options && metrics = profile_options[:metrics]
metrics.each do |metric_name|
if klass = Metrics[metric_name.to_sym]
run_profile(klass.new)
result.add_run
end
end
end
yield(self.class::FINISHED, name)
end
def run_test(metric, mode)
run_callbacks :setup
setup
metric.send(mode) { __send__ @method_name }
rescue ::Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue StandardError, ScriptError
add_error($!)
end
end
protected
def run_warmup
GC.start
time = Metrics::Time.new
run_test(time, :benchmark)
puts "%s (%s warmup)" % [full_test_name, time.format(time.total)]
GC.start
end
def run_profile(metric)
klass = profile_options[:benchmark] ? Benchmarker : Profiler
performer = klass.new(self, metric)
performer.run
puts performer.report
performer.record
end
class Performer
delegate :run_test, :profile_options, :full_test_name, :to => :@harness
def initialize(harness, metric)
@harness, @metric = harness, metric
end
def report
rate = @total / profile_options[:runs]
'%20s: %s' % [@metric.name, @metric.format(rate)]
end
protected
def output_filename
"#{profile_options[:output]}/#{full_test_name}_#{@metric.name}"
end
end
class Benchmarker < Performer
def run
profile_options[:runs].to_i.times { run_test(@metric, :benchmark) }
@total = @metric.total
end
def record
avg = @metric.total / profile_options[:runs].to_i
now = Time.now.utc.xmlschema
with_output_file do |file|
file.puts "#{avg},#{now},#{environment}"
end
end
def environment
unless defined? @env
app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/
rails = Rails::VERSION::STRING
if File.directory?('vendor/rails/.git')
Dir.chdir('vendor/rails') do
rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/
end
end
ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
@env = [app, rails, ruby, RUBY_PLATFORM] * ','
end
@env
end
protected
HEADER = 'measurement,created_at,app,rails,ruby,platform'
def with_output_file
fname = output_filename
if new = !File.exist?(fname)
FileUtils.mkdir_p(File.dirname(fname))
end
File.open(fname, 'ab') do |file|
file.puts(HEADER) if new
yield file
end
end
def output_filename
"#{super}.csv"
end
end
class Profiler < Performer
def initialize(*args)
super
@supported = @metric.measure_mode rescue false
end
def run
return unless @supported
RubyProf.measure_mode = @metric.measure_mode
RubyProf.start
RubyProf.pause
profile_options[:runs].to_i.times { run_test(@metric, :profile) }
@data = RubyProf.stop
@total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time }
end
def report
if @supported
super
else
'%20s: unsupported' % @metric.name
end
end
def record
return unless @supported
klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
klasses.each do |klass|
fname = output_filename(klass)
FileUtils.mkdir_p(File.dirname(fname))
File.open(fname, 'wb') do |file|
klass.new(@data).print(file, profile_options.slice(:min_percent))
end
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue ::Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue StandardError, ScriptError
add_error($!)
end
end
protected
def output_filename(printer_class)
suffix =
case printer_class.name.demodulize
when 'FlatPrinter'; 'flat.txt'
when 'GraphPrinter'; 'graph.txt'
when 'GraphHtmlPrinter'; 'graph.html'
when 'CallTreePrinter'; 'tree.txt'
else printer_class.name.sub(/Printer$/, '').underscore
end
def run_warmup
GC.start
"#{super()}_#{suffix}"
end
end
time = Metrics::Time.new
run_test(time, :benchmark)
puts "%s (%s warmup)" % [full_test_name, time.format(time.total)]
module Metrics
def self.[](name)
const_get(name.to_s.camelize)
rescue NameError
nil
end
class Base
attr_reader :total
def initialize
@total = 0
GC.start
end
def name
@name ||= self.class.name.demodulize.underscore
def run_profile(metric)
klass = profile_options[:benchmark] ? Benchmarker : Profiler
performer = klass.new(self, metric)
performer.run
puts performer.report
performer.record
end
def measure_mode
self.class::Mode
class Performer
delegate :run_test, :profile_options, :full_test_name, :to => :@harness
def initialize(harness, metric)
@harness, @metric = harness, metric
end
def measure
0
end
def benchmark
with_gc_stats do
before = measure
yield
@total += (measure - before)
end
end
def profile
RubyProf.resume
yield
ensure
RubyProf.pause
def report
rate = @total / profile_options[:runs]
'%20s: %s' % [@metric.name, @metric.format(rate)]
end
protected
if GC.respond_to?(:enable_stats)
def with_gc_stats
GC.enable_stats
yield
ensure
GC.disable_stats
end
elsif defined?(GC::Profiler)
def with_gc_stats
GC.start
GC.disable
GC::Profiler.enable
yield
ensure
GC::Profiler.disable
GC.enable
end
else
def with_gc_stats
yield
end
def output_filename
"#{profile_options[:output]}/#{full_test_name}_#{@metric.name}"
end
end
class Time < Base
def measure
::Time.now.to_f
class Benchmarker < Performer
def run
profile_options[:runs].to_i.times { run_test(@metric, :benchmark) }
@total = @metric.total
end
def format(measurement)
if measurement < 2
'%d ms' % (measurement * 1000)
else
'%.2f sec' % measurement
def record
avg = @metric.total / profile_options[:runs].to_i
now = Time.now.utc.xmlschema
with_output_file do |file|
file.puts "#{avg},#{now},#{environment}"
end
end
end
class ProcessTime < Time
Mode = RubyProf::PROCESS_TIME
def environment
unless defined? @env
app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/
def measure
RubyProf.measure_process_time
rails = Rails::VERSION::STRING
if File.directory?('vendor/rails/.git')
Dir.chdir('vendor/rails') do
rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/
end
end
ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
@env = [app, rails, ruby, RUBY_PLATFORM] * ','
end
@env
end
protected
HEADER = 'measurement,created_at,app,rails,ruby,platform'
def with_output_file
fname = output_filename
if new = !File.exist?(fname)
FileUtils.mkdir_p(File.dirname(fname))
end
File.open(fname, 'ab') do |file|
file.puts(HEADER) if new
yield file
end
end
def output_filename
"#{super}.csv"
end
end
class WallTime < Time
Mode = RubyProf::WALL_TIME
def measure
RubyProf.measure_wall_time
end
end
class CpuTime < Time
Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
class Profiler < Performer
def initialize(*args)
# FIXME: yeah my CPU is 2.33 GHz
RubyProf.cpu_frequency = 2.33e9
super
@supported = @metric.measure_mode rescue false
end
def measure
RubyProf.measure_cpu_time
def run
return unless @supported
RubyProf.measure_mode = @metric.measure_mode
RubyProf.start
RubyProf.pause
profile_options[:runs].to_i.times { run_test(@metric, :profile) }
@data = RubyProf.stop
@total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time }
end
def report
if @supported
super
else
'%20s: unsupported' % @metric.name
end
end
def record
return unless @supported
klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
klasses.each do |klass|
fname = output_filename(klass)
FileUtils.mkdir_p(File.dirname(fname))
File.open(fname, 'wb') do |file|
klass.new(@data).print(file, profile_options.slice(:min_percent))
end
end
end
protected
def output_filename(printer_class)
suffix =
case printer_class.name.demodulize
when 'FlatPrinter'; 'flat.txt'
when 'GraphPrinter'; 'graph.txt'
when 'GraphHtmlPrinter'; 'graph.html'
when 'CallTreePrinter'; 'tree.txt'
else printer_class.name.sub(/Printer$/, '').underscore
end
"#{super()}_#{suffix}"
end
end
class Memory < Base
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
module Metrics
def self.[](name)
const_get(name.to_s.camelize)
rescue NameError
nil
end
# ruby-prof wrapper
if RubyProf.respond_to?(:measure_memory)
def measure
RubyProf.measure_memory / 1024.0
class Base
attr_reader :total
def initialize
@total = 0
end
# Ruby 1.8 + railsbench patch
elsif GC.respond_to?(:allocated_size)
def measure
GC.allocated_size / 1024.0
def name
@name ||= self.class.name.demodulize.underscore
end
# Ruby 1.8 + lloyd patch
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['heap_current_memory'] / 1024.0
def measure_mode
self.class::Mode
end
# Ruby 1.9 with total_malloc_allocated_size patch
elsif GC.respond_to?(:malloc_total_allocated_size)
def measure
GC.total_malloc_allocated_size / 1024.0
0
end
# Ruby 1.9 unpatched
elsif GC.respond_to?(:malloc_allocated_size)
def measure
GC.malloc_allocated_size / 1024.0
def benchmark
with_gc_stats do
before = measure
yield
@total += (measure - before)
end
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
def profile
RubyProf.resume
yield
ensure
RubyProf.pause
end
protected
if GC.respond_to?(:enable_stats)
def with_gc_stats
GC.enable_stats
yield
ensure
GC.disable_stats
end
elsif defined?(GC::Profiler)
def with_gc_stats
GC.start
GC.disable
GC::Profiler.enable
yield
ensure
GC::Profiler.disable
GC.enable
end
else
def with_gc_stats
yield
end
end
end
class Time < Base
def measure
GC.enable
GC.start
kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0
GC.disable
kb
::Time.now.to_f
end
def format(measurement)
if measurement < 2
'%d ms' % (measurement * 1000)
else
'%.2f sec' % measurement
end
end
end
def format(measurement)
'%.2f KB' % measurement
end
end
class ProcessTime < Time
Mode = RubyProf::PROCESS_TIME
class Objects < Base
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
if RubyProf.respond_to?(:measure_allocations)
def measure
RubyProf.measure_allocations
end
# Ruby 1.8 + railsbench patch
elsif ObjectSpace.respond_to?(:allocated_objects)
def measure
ObjectSpace.allocated_objects
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
def measure
GC.enable
GC.start
last = GC::Profiler.data.last
count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS]
GC.disable
count
RubyProf.measure_process_time
end
end
def format(measurement)
measurement.to_i.to_s
end
end
class WallTime < Time
Mode = RubyProf::WALL_TIME
class GcRuns < Base
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
if RubyProf.respond_to?(:measure_gc_runs)
def measure
RubyProf.measure_gc_runs
end
elsif GC.respond_to?(:collections)
def measure
GC.collections
end
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['num_gc_passes']
RubyProf.measure_wall_time
end
end
def format(measurement)
measurement.to_i.to_s
end
end
class CpuTime < Time
Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
class GcTime < Base
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
if RubyProf.respond_to?(:measure_gc_time)
def measure
RubyProf.measure_gc_time
def initialize(*args)
# FIXME: yeah my CPU is 2.33 GHz
RubyProf.cpu_frequency = 2.33e9
super
end
elsif GC.respond_to?(:time)
def measure
GC.time
RubyProf.measure_cpu_time
end
end
def format(measurement)
'%d ms' % (measurement / 1000)
class Memory < Base
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
# ruby-prof wrapper
if RubyProf.respond_to?(:measure_memory)
def measure
RubyProf.measure_memory / 1024.0
end
# Ruby 1.8 + railsbench patch
elsif GC.respond_to?(:allocated_size)
def measure
GC.allocated_size / 1024.0
end
# Ruby 1.8 + lloyd patch
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['heap_current_memory'] / 1024.0
end
# Ruby 1.9 with total_malloc_allocated_size patch
elsif GC.respond_to?(:malloc_total_allocated_size)
def measure
GC.total_malloc_allocated_size / 1024.0
end
# Ruby 1.9 unpatched
elsif GC.respond_to?(:malloc_allocated_size)
def measure
GC.malloc_allocated_size / 1024.0
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
def measure
GC.enable
GC.start
kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0
GC.disable
kb
end
end
def format(measurement)
'%.2f KB' % measurement
end
end
class Objects < Base
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
if RubyProf.respond_to?(:measure_allocations)
def measure
RubyProf.measure_allocations
end
# Ruby 1.8 + railsbench patch
elsif ObjectSpace.respond_to?(:allocated_objects)
def measure
ObjectSpace.allocated_objects
end
# Ruby 1.9 + GC profiler patch
elsif defined?(GC::Profiler)
def measure
GC.enable
GC.start
last = GC::Profiler.data.last
count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS]
GC.disable
count
end
end
def format(measurement)
measurement.to_i.to_s
end
end
class GcRuns < Base
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
if RubyProf.respond_to?(:measure_gc_runs)
def measure
RubyProf.measure_gc_runs
end
elsif GC.respond_to?(:collections)
def measure
GC.collections
end
elsif GC.respond_to?(:heap_info)
def measure
GC.heap_info['num_gc_passes']
end
end
def format(measurement)
measurement.to_i.to_s
end
end
class GcTime < Base
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
if RubyProf.respond_to?(:measure_gc_time)
def measure
RubyProf.measure_gc_time
end
elsif GC.respond_to?(:time)
def measure
GC.time
end
end
def format(measurement)
'%d ms' % (measurement / 1000)
end
end
end
end
end
end
end
rescue LoadError
end

View File

@ -0,0 +1,80 @@
require 'abstract_unit'
class TestAutoloadModule < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
module ::Fixtures
extend ActiveSupport::Autoload
module Autoload
extend ActiveSupport::Autoload
end
end
test "the autoload module works like normal autoload" do
module ::Fixtures::Autoload
autoload :SomeClass, "fixtures/autoload/some_class"
end
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
test "when specifying an :eager constant it still works like normal autoload by default" do
module ::Fixtures::Autoload
autoload :SomeClass, "fixtures/autoload/some_class"
end
assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
test ":eager constants can be triggered via ActiveSupport::Autoload.eager_autoload!" do
module ::Fixtures::Autoload
autoload :SomeClass, "fixtures/autoload/some_class"
end
ActiveSupport::Autoload.eager_autoload!
assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
test "the location of autoloaded constants defaults to :name.underscore" do
module ::Fixtures::Autoload
autoload :SomeClass
end
assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
test "the location of :eager autoloaded constants defaults to :name.underscore" do
module ::Fixtures::Autoload
autoload :SomeClass
end
ActiveSupport::Autoload.eager_autoload!
assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
test "a directory for a block of autoloads can be specified" do
module ::Fixtures
autoload_under "autoload" do
autoload :AnotherClass
end
end
assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb")
assert_nothing_raised { ::Fixtures::AnotherClass }
end
test "a path for a block of autoloads can be specified" do
module ::Fixtures
autoload_at "fixtures/autoload/another_class" do
autoload :AnotherClass
end
end
assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb")
assert_nothing_raised { ::Fixtures::AnotherClass }
end
end

View File

@ -0,0 +1,2 @@
class Fixtures::AnotherClass
end

View File

@ -0,0 +1,2 @@
class Fixtures::Autoload::SomeClass
end

View File

@ -1,6 +1,7 @@
require 'active_support/all'
require 'active_support/test_case'
require 'action_controller'
require 'action_dispatch/test_case'
# work around the at_exit hook in test/unit, which kills IRB
Test::Unit.run = true if Test::Unit.respond_to?(:run=)

View File

@ -8,8 +8,14 @@ gem "rack-test", "~> 0.5.0"
require 'test/unit'
require 'active_support/core_ext/kernel/requires'
# AP is always present
require 'action_controller/test_case'
require 'action_view/test_case'
require 'action_dispatch/test_case'
require 'action_mailer/test_case' if defined?(ActionMailer)
require 'active_model/test_case' if defined?(ActiveModel)
if defined?(ActiveRecord)
require 'active_record/test_case'

View File

@ -1,5 +1,6 @@
require 'abstract_unit'
require 'action_controller'
require 'action_controller/test_case'
require 'rails/info'
require 'rails/info_controller'