From fa1d9a884c0d5b70c97442e3360ac98ca5fa4340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 12 Dec 2011 22:51:33 +0100 Subject: Speed up development by only reloading classes if dependencies files changed. This can be turned off by setting `config.reload_classes_only_on_change` to false. Extensions like Active Record should add their respective files like db/schema.rb and db/structure.sql to `config.watchable_files` if they want their changes to affect classes reloading. Thanks to https://github.com/paneq/active_reload and Pastorino for the inspiration. <3 --- railties/lib/rails/application.rb | 40 +++++++++++++++++- railties/lib/rails/application/configuration.rb | 49 ++++++++++++----------- railties/lib/rails/application/finisher.rb | 14 +++---- railties/lib/rails/application/routes_reloader.rb | 12 ++---- railties/lib/rails/engine.rb | 2 +- railties/lib/rails/railtie/configuration.rb | 12 ++++++ 6 files changed, 87 insertions(+), 42 deletions(-) (limited to 'railties/lib') diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index acbfd7078b..0b8eac8a8b 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -71,12 +71,14 @@ module Rails attr_accessor :assets, :sandbox alias_method :sandbox?, :sandbox + attr_reader :reloaders delegate :default_url_options, :default_url_options=, :to => :routes def initialize super @initialized = false + @reloaders = [] end # This method is called just after an application inherits from Rails::Application, @@ -119,6 +121,7 @@ module Rails reloader = routes_reloader hook = lambda { reloader.execute_if_updated } hook.call + self.reloaders << reloader ActionDispatch::Reloader.to_prepare(&hook) end @@ -126,10 +129,35 @@ module Rails # A plugin may override this if they desire to provide a more exquisite app reloading. # :api: plugin def set_dependencies_hook - ActionDispatch::Reloader.to_cleanup do + callback = lambda do ActiveSupport::DescendantsTracker.clear ActiveSupport::Dependencies.clear end + + if config.reload_classes_only_on_change + reloader = ActiveSupport::FileUpdateChecker.new(watchable_args, true, &callback) + self.reloaders << reloader + # We need to set a to_prepare callback regardless of the reloader result, i.e. + # models should be reloaded if any of the reloaders (i18n, routes) were updated. + ActionDispatch::Reloader.to_prepare(:prepend => true, &callback) + else + ActionDispatch::Reloader.to_cleanup(&callback) + end + end + + # Returns an array of file paths appended with a hash of directories-extensions + # suitable for ActiveSupport::FileUpdateChecker API. + def watchable_args + files = [] + files.concat config.watchable_files + + dirs = {} + dirs.merge! config.watchable_dirs + ActiveSupport::Dependencies.autoload_paths.each do |path| + dirs[path.to_s] = [:rb] + end + + files << dirs end # Initialize the application passing the given group. By default, the @@ -223,6 +251,10 @@ module Rails alias :build_middleware_stack :app + def reload_dependencies? + config.reload_classes_only_on_change != true || reloaders.map(&:updated?).any? + end + def default_middleware_stack ActionDispatch::MiddlewareStack.new.tap do |middleware| if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache @@ -252,7 +284,11 @@ module Rails middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header end - middleware.use ::ActionDispatch::Reloader unless config.cache_classes + unless config.cache_classes + app = self + middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? } + end + middleware.use ::ActionDispatch::Callbacks middleware.use ::ActionDispatch::Cookies diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 4b2afe3a28..39d66ecc31 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -7,11 +7,11 @@ module Rails class Configuration < ::Rails::Engine::Configuration attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets, :cache_classes, :cache_store, :consider_all_requests_local, - :dependency_loading, :filter_parameters, - :force_ssl, :helpers_paths, :logger, :log_tags, :preload_frameworks, - :relative_url_root, :reload_plugins, :secret_token, :serve_static_assets, - :ssl_options, :static_cache_control, :session_options, - :time_zone, :whiny_nils, :railties_order, :all_initializers + :dependency_loading, :filter_parameters, :force_ssl, :helpers_paths, + :initializers_paths, :logger, :log_tags, :preload_frameworks, + :railties_order, :relative_url_root, :reload_plugins, :secret_token, + :serve_static_assets, :ssl_options, :static_cache_control, :session_options, + :time_zone, :reload_classes_only_on_change, :whiny_nils attr_writer :log_level attr_reader :encoding @@ -19,25 +19,26 @@ module Rails def initialize(*) super self.encoding = "utf-8" - @allow_concurrency = false - @consider_all_requests_local = false - @filter_parameters = [] - @helpers_paths = [] - @dependency_loading = true - @serve_static_assets = true - @static_cache_control = nil - @force_ssl = false - @ssl_options = {} - @session_store = :cookie_store - @session_options = {} - @time_zone = "UTC" - @log_level = nil - @middleware = app_middleware - @generators = app_generators - @cache_store = [ :file_store, "#{root}/tmp/cache/" ] - @railties_order = [:all] - @all_initializers = [] - @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"] + @allow_concurrency = false + @consider_all_requests_local = false + @filter_parameters = [] + @helpers_paths = [] + @dependency_loading = true + @serve_static_assets = true + @static_cache_control = nil + @force_ssl = false + @ssl_options = {} + @session_store = :cookie_store + @session_options = {} + @time_zone = "UTC" + @log_level = nil + @middleware = app_middleware + @generators = app_generators + @cache_store = [ :file_store, "#{root}/tmp/cache/" ] + @railties_order = [:all] + @initializers_paths = [] + @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"] + @reload_classes_only_on_change = true @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index 17e7aa0f28..e000f6ef3a 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -5,7 +5,7 @@ module Rails $rails_rake_task = nil initializer :load_config_initializers do - config.all_initializers.each { |init| load(init) } + config.initializers_paths.each { |init| load(init) } end initializer :add_generator_templates do @@ -64,18 +64,18 @@ module Rails ActiveSupport.run_load_hooks(:after_initialize, self) end - # Set app reload just after the finisher hook to ensure - # paths added in the hook are still loaded. - initializer :set_dependencies_hook, :group => :all do |app| - app.set_dependencies_hook - end - # Set app reload just after the finisher hook to ensure # routes added in the hook are still loaded. initializer :set_routes_reloader_hook do |app| app.set_routes_reloader_hook end + # Set app reload just after the finisher hook to ensure + # paths added in the hook are still loaded. + initializer :set_dependencies_hook, :group => :all do |app| + app.set_dependencies_hook + end + # Disable dependency loading during request cycle initializer :disable_dependency_loading do if config.cache_classes && !config.dependency_loading diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb index c1f435a3ee..460b84dfd9 100644 --- a/railties/lib/rails/application/routes_reloader.rb +++ b/railties/lib/rails/application/routes_reloader.rb @@ -1,21 +1,17 @@ +require "active_support/core_ext/module/delegation" + module Rails class Application class RoutesReloader attr_reader :route_sets + delegate :paths, :execute_if_updated, :updated?, :to => :@updater + def initialize(updater=ActiveSupport::FileUpdateChecker) @updater = updater.new([]) { reload! } @route_sets = [] end - def paths - @updater.paths - end - - def execute_if_updated - @updater.execute_if_updated - end - def reload! clear! load_paths diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 8ebe1f48a5..86efc7332c 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -584,7 +584,7 @@ module Rails end initializer :append_config_initializers do |app| - app.config.all_initializers.concat config.paths["config/initializers"].existent.sort + app.config.initializers_paths.concat config.paths["config/initializers"].existent.sort end initializer :engines_blank_point do diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index f888684117..cf9e4ad500 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -7,6 +7,18 @@ module Rails @@options ||= {} end + # Add files that should be watched for change. + def watchable_files + @@watchable_files ||= [] + end + + # Add directories that should be watched for change. + # The key of the hashes should be directories and the values should + # be an array of extensions to match in each directory. + def watchable_dirs + @@watchable_dirs ||= {} + end + # This allows you to modify the application's middlewares from Engines. # # All operations you run on the app_middleware will be replayed on the -- cgit v1.2.3