diff options
35 files changed, 904 insertions, 768 deletions
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index b05d21ae5d..ac6f514dfa 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -18,11 +18,5 @@ module ActionMailer initializer "action_mailer.logger" do ActionMailer::Base.logger ||= Rails.logger end - - initializer "action_mailer.view_paths" do |app| - # TODO: this should be combined with the logic for default config.action_mailer.view_paths - view_path = ActionView::PathSet.type_cast(app.config.view_path, app.config.cache_classes) - ActionMailer::Base.template_root = view_path if ActionMailer::Base.view_paths.blank? - end end end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index cdd14560e1..24f8cb8a57 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -51,6 +51,8 @@ module ActionController included do # Set the default directory for helpers + # TODO This should support multiple directories in order + # to work with engines extlib_inheritable_accessor(:helpers_dir) do defined?(Rails.root) ? "#{Rails.root}/app/helpers" : "app/helpers" end diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 7ea64c1923..6b270d1136 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -19,25 +19,8 @@ module ActionController ActionController::Base.logger ||= Rails.logger end - # Routing must be initialized after plugins to allow the former to extend the routes - initializer "action_controller.initialize_routing" do |app| - app.route_configuration_files << app.config.routes_configuration_file - app.route_configuration_files << app.config.builtin_routes_configuration_file - end - initializer "action_controller.initialize_framework_caches" do ActionController::Base.cache_store ||= RAILS_CACHE end - - # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ - # (but only for those frameworks that are to be loaded). If the framework's - # paths have already been set, it is not changed, otherwise it is - # set to use Configuration#view_path. - initializer "action_controller.initialize_framework_views" do |app| - # TODO: this should be combined with the logic for default config.action_controller.view_paths - view_path = ActionView::PathSet.type_cast(app.config.view_path, app.config.cache_classes) - ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank? - end - end end diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index d07841218a..7cf75ffe63 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -37,7 +37,7 @@ module ActionDispatch def initialize(app, prepare_each_request = false) @app, @prepare_each_request = app, prepare_each_request - run_callbacks(:prepare) + run_callbacks(:prepare) unless @prepare_each_request end def call(env) diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index e4bd143e78..bd15ca9b3b 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -5,22 +5,14 @@ module ActionDispatch class Railtie < Rails::Railtie plugin_name :action_dispatch + # Initialize route files to an array + config.action_dispatch.route_files = [] + # Prepare dispatcher callbacks and run 'prepare' callbacks initializer "action_dispatch.prepare_dispatcher" do |app| # TODO: This used to say unless defined?(Dispatcher). Find out why and fix. require 'rails/dispatcher' - - unless app.config.cache_classes - # Setup dev mode route reloading - routes_last_modified = app.routes_changed_at - reload_routes = lambda do - unless app.routes_changed_at == routes_last_modified - routes_last_modified = app.routes_changed_at - app.reload_routes! - end - end - ActionDispatch::Callbacks.before { |callbacks| reload_routes.call } - end + ActionDispatch::Callbacks.to_prepare { app.routes_reloader.reload_if_changed } end end end
\ No newline at end of file diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index bc06333f1c..d5ec776b34 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -18,6 +18,11 @@ module ActiveRecord require "active_record/railties/subscriber" subscriber ActiveRecord::Railties::Subscriber.new + initializer "active_record.initialize_timezone" do + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + end + initializer "active_record.set_configs" do |app| app.config.active_record.each do |k,v| ActiveRecord::Base.send "#{k}=", v @@ -31,11 +36,6 @@ module ActiveRecord ActiveRecord::Base.establish_connection end - initializer "active_record.initialize_timezone" do - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - end - # Expose database runtime to controller for logging. initializer "active_record.log_runtime" do |app| require "active_record/railties/controller_runtime" diff --git a/activeresource/lib/active_resource/railtie.rb b/activeresource/lib/active_resource/railtie.rb index 1b9307d472..5c318f21e6 100644 --- a/activeresource/lib/active_resource/railtie.rb +++ b/activeresource/lib/active_resource/railtie.rb @@ -7,5 +7,11 @@ module ActiveResource require "active_resource/railties/subscriber" subscriber ActiveResource::Railties::Subscriber.new + + initializer "active_resource.set_configs" do |app| + app.config.active_resource.each do |k,v| + ActiveResource::Base.send "#{k}=", v + end + end end end
\ No newline at end of file diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb new file mode 100644 index 0000000000..d443e0e997 --- /dev/null +++ b/activesupport/lib/active_support/railtie.rb @@ -0,0 +1,58 @@ +require "active_support" +require "rails" + +module I18n + class Railtie < Rails::Railtie + plugin_name :i18n + + # Initialize I18n load paths to an array + config.i18n.load_path = [] + + initializer :initialize_i18n do + require 'active_support/i18n' + + ActionDispatch::Callbacks.to_prepare do + I18n.reload! + end + end + + # Set the i18n configuration from config.i18n but special-case for + # the load_path which should be appended to what's already set instead of overwritten. + config.after_initialize do |app| + app.config.i18n.each do |setting, value| + if setting == :load_path + I18n.load_path += value + else + I18n.send("#{setting}=", value) + end + end + end + end +end + +module ActiveSupport + class Railtie < Rails::Railtie + plugin_name :active_support + + # Loads support for "whiny nil" (noisy warnings when methods are invoked + # on +nil+ values) if Configuration#whiny_nils is true. + initializer :initialize_whiny_nils do |app| + require 'active_support/whiny_nil' if app.config.whiny_nils + end + + # Sets the default value for Time.zone + # If assigned value cannot be matched to a TimeZone, an exception will be raised. + initializer :initialize_time_zone do |app| + require 'active_support/core_ext/time/zones' + zone_default = Time.__send__(:get_zone, app.config.time_zone) + + unless zone_default + raise \ + 'Value assigned to config.time_zone not recognized.' + + 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' + end + + Time.zone_default = zone_default + end + end +end
\ No newline at end of file diff --git a/railties/lib/generators/rails/app/templates/config/boot.rb b/railties/lib/generators/rails/app/templates/config/boot.rb index 466e1e50ec..cbfa5ca3e9 100644 --- a/railties/lib/generators/rails/app/templates/config/boot.rb +++ b/railties/lib/generators/rails/app/templates/config/boot.rb @@ -19,7 +19,7 @@ require 'rails/all' # To pick the frameworks you want, remove 'require "rails/all"' # and list the framework railties that you want: # -# require "active_model/railtie" +# require "active_support/railtie" # require "active_record/railtie" # require "action_controller/railtie" # require "action_view/railtie" @@ -28,7 +28,7 @@ require 'rails/all' <% else -%> # Pick the frameworks you want: # require "active_record/railtie" -require "active_model/railtie" +require "active_support/railtie" require "action_controller/railtie" require "action_view/railtie" require "action_mailer/railtie" diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 0bc7160815..623555e7c1 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -4,15 +4,8 @@ require 'active_support' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/logger' -require 'rails/initializable' require 'rails/application' -require 'rails/railtie' -require 'rails/plugin' -require 'rails/railties_path' require 'rails/version' -require 'rails/rack' -require 'rails/paths' -require 'rails/configuration' require 'rails/deprecation' require 'rails/subscriber' require 'rails/ruby_version_check' @@ -32,8 +25,6 @@ else end module Rails - autoload :Bootstrap, 'rails/bootstrap' - class << self def application @@application ||= nil diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb index 7dfe2b8b63..b8292a9b7e 100644 --- a/railties/lib/rails/all.rb +++ b/railties/lib/rails/all.rb @@ -1,6 +1,7 @@ require "rails" %w( + active_support active_model active_record action_controller diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 8366127476..504a241da8 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,118 +1,88 @@ -require "fileutils" -require 'active_support/core_ext/module/delegation' +require 'fileutils' +require 'rails/railties_path' +require 'rails/railtie' +require 'rails/engine' +require 'rails/plugin' module Rails - class Application - include Initializable - + class Application < Engine + autoload :Bootstrap, 'rails/application/bootstrap' + autoload :Configuration, 'rails/application/configuration' + autoload :Finisher, 'rails/application/finisher' + autoload :Railties, 'rails/application/railties' + autoload :RoutesReloader, 'rails/application/routes_reloader' + + # TODO Check helpers works as expected + # TODO Check routes namespaces + # TODO raise "You cannot have more than one Rails::Application" if Rails.application + # TODO Ensure production settings are read properly class << self - attr_writer :config - alias configure class_eval - delegate :call, - :initialize!, - :load_generators, - :load_tasks, - :middleware, - :root, - :to => :instance - private :new + alias :configure :class_eval + def instance @instance ||= new end - def config - @config ||= Configuration.new(Plugin::Configuration.default) + def inherited(base) + super + Rails.application = base.instance + base.require_environment! end - def routes - ActionController::Routing::Routes + protected + + def method_missing(*args, &block) + instance.send(*args, &block) end end - delegate :config, :routes, :to => :'self.class' - delegate :root, :middleware, :to => :config - attr_reader :route_configuration_files - - def initialize - require_environment - Rails.application ||= self - @route_configuration_files = [] + def require_environment! + environment = config.paths.config.environment.to_a.first + require environment if environment end - def initialize! - run_initializers(self) - self + def config + @config ||= Application::Configuration.new(self.class.find_root_with_flag("config.ru", Dir.pwd)) end - def require_environment - require config.environment_path - rescue LoadError + def routes + ::ActionController::Routing::Routes end - def routes_changed_at - routes_changed_at = nil - - route_configuration_files.each do |config| - config_changed_at = File.stat(config).mtime - - if routes_changed_at.nil? || config_changed_at > routes_changed_at - routes_changed_at = config_changed_at - end - end + def railties + @railties ||= Railties.new(config) + end - routes_changed_at + def routes_reloader + @routes_reloader ||= RoutesReloader.new(config) end def reload_routes! - routes.disable_clear_and_finalize = true - - routes.clear! - route_configuration_files.each { |config| load(config) } - routes.finalize! + routes_reloader.reload! + end - nil - ensure - routes.disable_clear_and_finalize = false + def initialize! + run_initializers(self) + self end def load_tasks - require "rails/tasks" - plugins.each { |p| p.load_tasks } - # Load all application tasks - # TODO: extract out the path to the rake tasks - Dir["#{root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } - task :environment do - $rails_rake_task = true - initialize! - end + initialize_tasks + super + railties.all { |r| r.load_tasks } + self end def load_generators - plugins.each { |p| p.load_generators } - end - - def initializers - initializers = Bootstrap.new(self).initializers - plugins.each { |p| initializers += p.initializers } - initializers += super - initializers - end - - # TODO: Fix this method. It loads all railties independent if :all is given - # or not, otherwise frameworks are never loaded. - def plugins - @plugins ||= begin - plugin_names = (config.plugins || [:all]).map { |p| p.to_sym } - Railtie.plugins.map(&:new) + Plugin.all(plugin_names, config.paths.vendor.plugins) - end + initialize_generators + super + railties.all { |r| r.load_generators } + self end def app - @app ||= begin - reload_routes! - middleware.build(routes) - end + @app ||= middleware.build(routes) end def call(env) @@ -120,42 +90,31 @@ module Rails app.call(env) end - initializer :load_application_initializers do - Dir["#{root}/config/initializers/**/*.rb"].sort.each do |initializer| - load(initializer) - end + def initializers + initializers = Bootstrap.initializers + initializers += super + railties.all { |r| initializers += r.initializers } + initializers += Finisher.initializers + initializers end - initializer :build_middleware_stack do - app - end + protected - # Fires the user-supplied after_initialize block (Configuration#after_initialize) - initializer :after_initialize do - config.after_initialize_blocks.each do |block| - block.call + def initialize_tasks + require "rails/tasks" + task :environment do + $rails_rake_task = true + initialize! end end - # Eager load application classes - initializer :load_application_classes do - next if $rails_rake_task - - if config.cache_classes - config.eager_load_paths.each do |load_path| - matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ - Dir.glob("#{load_path}/**/*.rb").sort.each do |file| - require_dependency file.sub(matcher, '\1') - end - end - end + def initialize_generators + require "rails/generators" end - # Disable dependency loading during request cycle - initializer :disable_dependency_loading do - if config.cache_classes && !config.dependency_loading - ActiveSupport::Dependencies.unhook! - end + # Application is always reloadable when config.cache_classes is false. + def reloadable?(app) + true end end end diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb new file mode 100644 index 0000000000..819d00be4e --- /dev/null +++ b/railties/lib/rails/application/bootstrap.rb @@ -0,0 +1,77 @@ +module Rails + class Application + module Bootstrap + include Initializable + + initializer :load_all_active_support do |app| + require "active_support/all" unless app.config.active_support.bare + end + + # Preload all frameworks specified by the Configuration#frameworks. + # Used by Passenger to ensure everything's loaded before forking and + # to avoid autoload race conditions in JRuby. + initializer :preload_frameworks do |app| + require 'active_support/dependencies' + ActiveSupport::Autoload.eager_autoload! if app.config.preload_frameworks + end + + # Initialize the logger early in the stack in case we need to log some deprecation. + initializer :initialize_logger do |app| + Rails.logger ||= app.config.logger || begin + path = app.config.paths.log.to_a.first + logger = ActiveSupport::BufferedLogger.new(path) + logger.level = ActiveSupport::BufferedLogger.const_get(app.config.log_level.to_s.upcase) + logger.auto_flushing = false if Rails.env.production? + logger + rescue StandardError => e + logger = ActiveSupport::BufferedLogger.new(STDERR) + logger.level = ActiveSupport::BufferedLogger::WARN + logger.warn( + "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " + + "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." + ) + logger + end + end + + # Initialize cache early in the stack so railties can make use of it. + initializer :initialize_cache do |app| + unless defined?(RAILS_CACHE) + silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(app.config.cache_store) } + + if RAILS_CACHE.respond_to?(:middleware) + app.config.middleware.insert_after(:"Rack::Lock", RAILS_CACHE.middleware) + end + end + end + + # Initialize rails subscriber on top of notifications. + initializer :initialize_subscriber do |app| + require 'active_support/notifications' + + if app.config.colorize_logging == false + Rails::Subscriber.colorize_logging = false + app.config.generators.colorize_logging = false + end + + ActiveSupport::Notifications.subscribe do |*args| + Rails::Subscriber.dispatch(args) + end + end + + initializer :set_clear_dependencies_hook do |app| + unless app.config.cache_classes + ActionDispatch::Callbacks.after do + ActiveSupport::Dependencies.clear + end + end + end + + # Sets the dependency loading mechanism. + # TODO: Remove files from the $" and always use require. + initializer :initialize_dependency_mechanism do |app| + ActiveSupport::Dependencies.mechanism = app.config.cache_classes ? :require : :load + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb new file mode 100644 index 0000000000..aaf18b5f51 --- /dev/null +++ b/railties/lib/rails/application/configuration.rb @@ -0,0 +1,85 @@ +require 'rails/engine/configuration' + +module Rails + class Application + class Configuration < ::Rails::Engine::Configuration + include ::Rails::Configuration::Deprecated + + attr_accessor :cache_classes, :cache_store, :colorize_logging, + :consider_all_requests_local, :dependency_loading, + :filter_parameters, :log_level, :logger, :metals, + :plugins, :preload_frameworks, :reload_plugins, + :serve_static_assets, :time_zone, :whiny_nils + + def initialize(*) + super + @filter_parameters = [] + @dependency_loading = true + @serve_static_assets = true + end + + def paths + @paths ||= begin + paths = super + paths.app.controllers << builtin_controller if builtin_controller + paths.config.database "config/database.yml" + paths.log "log/#{Rails.env}.log" + paths.tmp "tmp" + paths.tmp.cache "tmp/cache" + paths.vendor "vendor", :load_path => true + paths.vendor.plugins "vendor/plugins" + + if File.exists?("#{root}/test/mocks/#{Rails.env}") + ActiveSupport::Deprecation.warn "\"RAILS_ROOT/test/mocks/#{Rails.env}\" won't be added " << + "automatically to load paths anymore in future releases" + paths.mocks_path "test/mocks", :load_path => true, :glob => Rails.env + end + + paths + end + end + + # Enable threaded mode. Allows concurrent requests to controller actions and + # multiple database connections. Also disables automatic dependency loading + # after boot, and disables reloading code on every request, as these are + # fundamentally incompatible with thread safety. + def threadsafe! + self.preload_frameworks = true + self.cache_classes = true + self.dependency_loading = false + self.action_controller.allow_concurrency = true if respond_to?(:action_controller) + self + end + + # Loads and returns the contents of the #database_configuration_file. The + # contents of the file are processed via ERB before being sent through + # YAML::load. + def database_configuration + require 'erb' + YAML::load(ERB.new(IO.read(paths.config.database.to_a.first)).result) + end + + def cache_store + @cache_store ||= begin + if File.exist?("#{root}/tmp/cache/") + [ :file_store, "#{root}/tmp/cache/" ] + else + :memory_store + end + end + end + + def builtin_controller + File.join(RAILTIES_PATH, "builtin", "rails_info") if Rails.env.development? + end + + def log_level + @log_level ||= Rails.env.production? ? :info : :debug + end + + def time_zone + @time_zone ||= "UTC" + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb new file mode 100644 index 0000000000..2ac881ac11 --- /dev/null +++ b/railties/lib/rails/application/finisher.rb @@ -0,0 +1,43 @@ +module Rails + class Application + module Finisher + include Initializable + + initializer :ensure_load_once_paths_as_subset do + extra = ActiveSupport::Dependencies.load_once_paths - + ActiveSupport::Dependencies.load_paths + + unless extra.empty? + abort <<-end_error + load_once_paths must be a subset of the load_paths. + Extra items in load_once_paths: #{extra * ','} + end_error + end + end + + initializer :add_builtin_route do |app| + if Rails.env.development? + app.config.action_dispatch.route_files << File.join(RAILTIES_PATH, 'builtin', 'routes.rb') + end + end + + initializer :build_middleware_stack do |app| + app.app + end + + # Fires the user-supplied after_initialize block (config#after_initialize) + initializer :after_initialize do |app| + app.config.after_initialize_blocks.each do |block| + block.call(app) + end + end + + # Disable dependency loading during request cycle + initializer :disable_dependency_loading do |app| + if app.config.cache_classes && !app.config.dependency_loading + ActiveSupport::Dependencies.unhook! + end + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb new file mode 100644 index 0000000000..d167d9bf4f --- /dev/null +++ b/railties/lib/rails/application/railties.rb @@ -0,0 +1,31 @@ +module Rails + class Application + class Railties + # TODO Write tests + def initialize(config) + @config = config + end + + def all(&block) + @all ||= railties + engines + plugins + @all.each(&block) if block + @all + end + + def railties + @railties ||= ::Rails::Railtie.subclasses.map(&:new) + end + + def engines + @engines ||= ::Rails::Engine.subclasses.map(&:new) + end + + def plugins + @plugins ||= begin + plugin_names = (@config.plugins || [:all]).map { |p| p.to_sym } + Plugin.all(plugin_names, @config.paths.vendor.plugins) + end + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb new file mode 100644 index 0000000000..6d61de2320 --- /dev/null +++ b/railties/lib/rails/application/routes_reloader.rb @@ -0,0 +1,50 @@ +module Rails + class Application + class RoutesReloader + # TODO Change config.action_dispatch.route_files to config.action_dispatch.routes_path + # TODO Write tests + def initialize(config) + @config, @last_change_at = config, nil + end + + def changed_at + routes_changed_at = nil + + files.each do |file| + config_changed_at = File.stat(file).mtime + + if routes_changed_at.nil? || config_changed_at > routes_changed_at + routes_changed_at = config_changed_at + end + end + + routes_changed_at + end + + def reload! + routes = Rails::Application.routes + routes.disable_clear_and_finalize = true + + routes.clear! + files.each { |file| load(file) } + routes.finalize! + + nil + ensure + routes.disable_clear_and_finalize = false + end + + def reload_if_changed + current_change_at = changed_at + if @last_change_at != current_change_at + @last_change_at = current_change_at + reload! + end + end + + def files + @config.action_dispatch.route_files + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/bootstrap.rb b/railties/lib/rails/bootstrap.rb deleted file mode 100644 index 5db663f9ef..0000000000 --- a/railties/lib/rails/bootstrap.rb +++ /dev/null @@ -1,161 +0,0 @@ -module Rails - class Bootstrap #< Railtie - include Initializable - - def initialize(application) - @application = application - end - - delegate :config, :root, :to => :'@application' - - initializer :load_all_active_support do - require "active_support/all" unless config.active_support.bare - end - - # Set the <tt>$LOAD_PATH</tt> based on the value of - # Configuration#load_paths. Duplicates are removed. - initializer :set_load_path do - config.paths.add_to_load_path - $LOAD_PATH.uniq! - end - - # Set the paths from which Rails will automatically load source files, and - # the load_once paths. - initializer :set_autoload_paths do - require 'active_support/dependencies' - ActiveSupport::Dependencies.load_paths = expand_load_path(config.load_paths) - ActiveSupport::Dependencies.load_once_paths = expand_load_path(config.load_once_paths) - - extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths - unless extra.empty? - abort <<-end_error - load_once_paths must be a subset of the load_paths. - Extra items in load_once_paths: #{extra * ','} - end_error - end - - # Freeze the arrays so future modifications will fail rather than do nothing mysteriously - config.load_once_paths.freeze - end - - # Create tmp directories - initializer :ensure_tmp_directories_exist do - %w(cache pids sessions sockets).each do |dir_to_make| - FileUtils.mkdir_p(File.join(root, 'tmp', dir_to_make)) - end - end - - # Preload all frameworks specified by the Configuration#frameworks. - # Used by Passenger to ensure everything's loaded before forking and - # to avoid autoload race conditions in JRuby. - initializer :preload_frameworks do - ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks - end - - initializer :initialize_cache do - unless defined?(RAILS_CACHE) - silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(config.cache_store) } - - if RAILS_CACHE.respond_to?(:middleware) - # Insert middleware to setup and teardown local cache for each request - config.middleware.insert_after(:"Rack::Lock", RAILS_CACHE.middleware) - end - end - end - - initializer :initialize_logger do - Rails.logger ||= config.logger || begin - logger = ActiveSupport::BufferedLogger.new(config.log_path) - logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase) - logger.auto_flushing = false if Rails.env.production? - logger - rescue StandardError => e - logger = ActiveSupport::BufferedLogger.new(STDERR) - logger.level = ActiveSupport::BufferedLogger::WARN - logger.warn( - "Rails Error: Unable to access log file. Please ensure that #{config.log_path} exists and is chmod 0666. " + - "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." - ) - logger - end - end - - # Sets the logger for dependencies and cache store. - initializer :initialize_framework_logging do - ActiveSupport::Dependencies.logger ||= Rails.logger - Rails.cache.logger ||= Rails.logger - end - - # Sets the dependency loading mechanism based on the value of - # Configuration#cache_classes. - initializer :initialize_dependency_mechanism do - # TODO: Remove files from the $" and always use require - ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load - end - - # Loads support for "whiny nil" (noisy warnings when methods are invoked - # on +nil+ values) if Configuration#whiny_nils is true. - initializer :initialize_whiny_nils do - require 'active_support/whiny_nil' if config.whiny_nils - end - - # Sets the default value for Time.zone - # If assigned value cannot be matched to a TimeZone, an exception will be raised. - initializer :initialize_time_zone do - require 'active_support/core_ext/time/zones' - zone_default = Time.__send__(:get_zone, config.time_zone) - - unless zone_default - raise \ - 'Value assigned to config.time_zone not recognized.' + - 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' - end - - Time.zone_default = zone_default - end - - # Set the i18n configuration from config.i18n but special-case for the load_path which should be - # appended to what's already set instead of overwritten. - initializer :initialize_i18n do - require 'active_support/i18n' - - config.i18n.each do |setting, value| - if setting == :load_path - I18n.load_path += value - else - I18n.send("#{setting}=", value) - end - end - - ActionDispatch::Callbacks.to_prepare do - I18n.reload! - end - end - - initializer :set_clear_dependencies_hook do - unless config.cache_classes - ActionDispatch::Callbacks.after do - ActiveSupport::Dependencies.clear - end - end - end - - initializer :initialize_notifications do - require 'active_support/notifications' - - if config.colorize_logging == false - Rails::Subscriber.colorize_logging = false - config.generators.colorize_logging = false - end - - ActiveSupport::Notifications.subscribe do |*args| - Rails::Subscriber.dispatch(args) - end - end - - private - def expand_load_path(load_paths) - load_paths.map { |path| Dir.glob(path.to_s) }.flatten.uniq - end - end -end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 7f1783a6b9..e0bd12497f 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -1,296 +1,84 @@ require 'active_support/ordered_options' +require 'rails/paths' +require 'rails/rack' module Rails - # Temporarily separate the plugin configuration class from the main - # configuration class while this bit is being cleaned up. - class Railtie::Configuration - def self.default - @default ||= new - end - - def self.default_middleware_stack - ActionDispatch::MiddlewareStack.new.tap do |middleware| - middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { Rails.application.config.serve_static_assets }) - middleware.use('::Rack::Lock', :if => lambda { !ActionController::Base.allow_concurrency }) - middleware.use('::Rack::Runtime') - middleware.use('::Rails::Rack::Logger') - middleware.use('::ActionDispatch::ShowExceptions', lambda { ActionController::Base.consider_all_requests_local }) - middleware.use('::ActionDispatch::Callbacks', lambda { !Rails.application.config.cache_classes }) - middleware.use('::ActionDispatch::Cookies') - middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) - middleware.use('::ActionDispatch::Flash', :if => lambda { ActionController::Base.session_store }) - middleware.use(lambda { Rails::Rack::Metal.new(Rails.application.config.paths.app.metals.to_a, Rails.application.config.metals) }) - middleware.use('ActionDispatch::ParamsParser') - middleware.use('::Rack::MethodOverride') - middleware.use('::ActionDispatch::Head') - end - end - - attr_reader :middleware - - def initialize(base = nil) - if base - @options = base.options.dup - @middleware = base.middleware.dup - else - @options = Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } - @middleware = self.class.default_middleware_stack - end - end - - def respond_to?(name) - super || name.to_s =~ config_key_regexp - end - - protected - - attr_reader :options - - private - - def method_missing(name, *args, &blk) - if name.to_s =~ config_key_regexp - return $2 == '=' ? @options[$1] = args.first : @options[$1] + module Configuration + module Shared + def middleware + @@default_middleware_stack ||= ActionDispatch::MiddlewareStack.new.tap do |middleware| + middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { Rails.application.config.serve_static_assets }) + middleware.use('::Rack::Lock', :if => lambda { !ActionController::Base.allow_concurrency }) + middleware.use('::Rack::Runtime') + middleware.use('::Rails::Rack::Logger') + middleware.use('::ActionDispatch::ShowExceptions', lambda { ActionController::Base.consider_all_requests_local }) + middleware.use('::ActionDispatch::Callbacks', lambda { !Rails.application.config.cache_classes }) + middleware.use('::ActionDispatch::Cookies') + middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) + middleware.use('::ActionDispatch::Flash', :if => lambda { ActionController::Base.session_store }) + middleware.use(lambda { Rails::Rack::Metal.new(Rails.application.config.paths.app.metals.to_a, Rails.application.config.metals) }) + middleware.use('ActionDispatch::ParamsParser') + middleware.use('::Rack::MethodOverride') + middleware.use('::ActionDispatch::Head') + end end - super - end - - def config_key_regexp - bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|') - /^(#{bits})(?:=)?$/ - end - - def config_keys - ([ :active_support, :action_view ] + - Railtie.plugin_names).map { |n| n.to_s }.uniq - end - end - - class Configuration < Railtie::Configuration - attr_accessor :after_initialize_blocks, :cache_classes, :colorize_logging, - :consider_all_requests_local, :dependency_loading, :filter_parameters, - :load_once_paths, :logger, :metals, :plugins, - :preload_frameworks, :reload_plugins, :serve_static_assets, - :time_zone, :whiny_nils - - attr_writer :cache_store, :controller_paths, - :database_configuration_file, :eager_load_paths, - :i18n, :load_paths, :log_level, :log_path, :paths, - :routes_configuration_file, :view_path - - def initialize(base = nil) - super - @load_once_paths = [] - @after_initialize_blocks = [] - @filter_parameters = [] - @dependency_loading = true - @serve_static_assets = true - end - - def after_initialize(&blk) - @after_initialize_blocks << blk if blk - end - - def root - @root ||= begin - call_stack = caller.map { |p| p.split(':').first } - root_path = call_stack.detect { |p| p !~ %r[railties/lib/rails|rack/lib/rack] } - root_path = File.dirname(root_path) - - while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/config.ru") - parent = File.dirname(root_path) - root_path = parent != root_path && parent + # Holds generators configuration: + # + # config.generators do |g| + # g.orm :datamapper, :migration => true + # g.template_engine :haml + # g.test_framework :rspec + # end + # + # If you want to disable color in console, do: + # + # config.generators.colorize_logging = false + # + def generators + @@generators ||= Rails::Configuration::Generators.new + if block_given? + yield @@generators + else + @@generators end - - root = File.exist?("#{root_path}/config.ru") ? root_path : Dir.pwd - - RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? - Pathname.new(root).expand_path : - Pathname.new(root).realpath end - end - - def root=(root) - @root = Pathname.new(root).expand_path - end - def paths - @paths ||= begin - paths = Rails::Application::Root.new(root) - paths.app "app", :load_path => true - paths.app.metals "app/metal", :eager_load => true - paths.app.models "app/models", :eager_load => true - paths.app.controllers "app/controllers", builtin_directories, :eager_load => true - paths.app.helpers "app/helpers", :eager_load => true - paths.app.services "app/services", :load_path => true - paths.lib "lib", :load_path => true - paths.vendor "vendor", :load_path => true - paths.vendor.plugins "vendor/plugins" - paths.tmp "tmp" - paths.tmp.cache "tmp/cache" - paths.config "config" - paths.config.locales "config/locales" - paths.config.environments "config/environments", :glob => "#{Rails.env}.rb" - paths + def after_initialize_blocks + @@after_initialize_blocks ||= [] end - end - - def frameworks(*args) - raise "config.frameworks in no longer supported. See the generated " \ - "config/boot.rb for steps on how to limit the frameworks that " \ - "will be loaded" - end - alias frameworks= frameworks - # Enable threaded mode. Allows concurrent requests to controller actions and - # multiple database connections. Also disables automatic dependency loading - # after boot, and disables reloading code on every request, as these are - # fundamentally incompatible with thread safety. - def threadsafe! - self.preload_frameworks = true - self.cache_classes = true - self.dependency_loading = false - - if respond_to?(:action_controller) - action_controller.allow_concurrency = true + def after_initialize(&blk) + after_initialize_blocks << blk if blk end - self - end - - # Loads and returns the contents of the #database_configuration_file. The - # contents of the file are processed via ERB before being sent through - # YAML::load. - def database_configuration - require 'erb' - YAML::load(ERB.new(IO.read(database_configuration_file)).result) - end - - def routes_configuration_file - @routes_configuration_file ||= File.join(root, 'config', 'routes.rb') - end - def builtin_routes_configuration_file - @builtin_routes_configuration_file ||= File.join(RAILTIES_PATH, 'builtin', 'routes.rb') - end - - def controller_paths - @controller_paths ||= begin - paths = [File.join(root, 'app', 'controllers')] - paths.concat builtin_directories - paths + def respond_to?(name) + super || name.to_s =~ config_key_regexp end - end - def cache_store - @cache_store ||= begin - if File.exist?("#{root}/tmp/cache/") - [ :file_store, "#{root}/tmp/cache/" ] - else - :memory_store + private + + def method_missing(name, *args, &blk) + if name.to_s =~ config_key_regexp + return $2 == '=' ? options[$1] = args.first : options[$1] end + super end - end - def database_configuration_file - @database_configuration_file ||= File.join(root, 'config', 'database.yml') - end - - def view_path - @view_path ||= File.join(root, 'app', 'views') - end - - def eager_load_paths - @eager_load_paths ||= ["#{root}/app/*"] - end - - def load_paths - @load_paths ||= begin - paths = [] - - # Add the old mock paths only if the directories exists - paths.concat(Dir["#{root}/test/mocks/#{Rails.env}"]) if File.exists?("#{root}/test/mocks/#{Rails.env}") - - # Followed by the standard includes. - paths.concat %w( - app - app/* - lib - vendor - ).map { |dir| "#{root}/#{dir}" } - - paths.concat builtin_directories + def config_key_regexp + bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|') + /^(#{bits})(?:=)?$/ end - end - def builtin_directories - # Include builtins only in the development environment. - Rails.env.development? ? Dir["#{RAILTIES_PATH}/builtin/*/"] : [] - end - - def log_path - @log_path ||= File.join(root, 'log', "#{Rails.env}.log") - end - - def log_level - @log_level ||= Rails.env.production? ? :info : :debug - end - - def time_zone - @time_zone ||= "UTC" - end - - def i18n - @i18n ||= begin - i18n = ActiveSupport::OrderedOptions.new - i18n.load_path = [] - - if File.exist?(File.join(root, 'config', 'locales')) - i18n.load_path << Dir[File.join(root, 'config', 'locales', '*.{rb,yml}')] - i18n.load_path.flatten! - end - - i18n + def config_keys + (Railtie.plugin_names + Engine.plugin_names).map { |n| n.to_s }.uniq end - end - def environment_path - "#{root}/config/environments/#{Rails.env}.rb" - end - - # Holds generators configuration: - # - # config.generators do |g| - # g.orm :datamapper, :migration => true - # g.template_engine :haml - # g.test_framework :rspec - # end - # - # If you want to disable color in console, do: - # - # config.generators.colorize_logging = false - # - def generators - @generators ||= Generators.new - if block_given? - yield @generators - else - @generators + def options + @@options ||= Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } end end - # Allow Notifications queue to be modified or add subscriptions: - # - # config.notifications.queue = MyNewQueue.new - # - # config.notifications.subscribe /action_dispatch.show_exception/ do |*args| - # ExceptionDeliver.deliver_exception(args) - # end - # - def notifications - ActiveSupport::Notifications - end - class Generators #:nodoc: attr_accessor :aliases, :options, :colorize_logging @@ -319,5 +107,74 @@ module Rails end end end + + module Deprecated + def frameworks(*args) + raise "config.frameworks in no longer supported. See the generated " \ + "config/boot.rb for steps on how to limit the frameworks that " \ + "will be loaded" + end + alias :frameworks= :frameworks + + def view_path=(value) + ActiveSupport::Deprecation.warn "config.view_path= is deprecated, " << + "please do config.paths.app.views= instead", caller + paths.app.views = value + end + + def view_path + ActiveSupport::Deprecation.warn "config.view_path is deprecated, " << + "please do config.paths.app.views instead", caller + paths.app.views.to_a.first + end + + def routes_configuration_file=(value) + ActiveSupport::Deprecation.warn "config.routes_configuration_file= is deprecated, " << + "please do config.paths.config.routes= instead", caller + paths.config.routes = value + end + + def routes_configuration_file + ActiveSupport::Deprecation.warn "config.routes_configuration_file is deprecated, " << + "please do config.paths.config.routes instead", caller + paths.config.routes.to_a.first + end + + def database_configuration_file=(value) + ActiveSupport::Deprecation.warn "config.database_configuration_file= is deprecated, " << + "please do config.paths.config.database= instead", caller + paths.config.database = value + end + + def database_configuration_file + ActiveSupport::Deprecation.warn "config.database_configuration_file is deprecated, " << + "please do config.paths.config.database instead", caller + paths.config.database.to_a.first + end + + def log_path=(value) + ActiveSupport::Deprecation.warn "config.log_path= is deprecated, " << + "please do config.paths.log= instead", caller + paths.config.log = value + end + + def log_path + ActiveSupport::Deprecation.warn "config.log_path is deprecated, " << + "please do config.paths.log instead", caller + paths.config.log.to_a.first + end + + def controller_paths=(value) + ActiveSupport::Deprecation.warn "config.controller_paths= is deprecated, " << + "please do config.paths.app.controllers= instead", caller + paths.app.controllers = value + end + + def controller_paths + ActiveSupport::Deprecation.warn "config.controller_paths is deprecated, " << + "please do config.paths.app.controllers instead", caller + paths.app.controllers.to_a.uniq + end + end end end diff --git a/railties/lib/rails/console_app.rb b/railties/lib/rails/console_app.rb index 2c4a7a51e8..98f7f774a9 100644 --- a/railties/lib/rails/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -26,7 +26,6 @@ end #reloads the environment def reload! puts "Reloading..." - ActionDispatch::Callbacks.new(lambda {}, true) - Rails.application.reload_routes! + ActionDispatch::Callbacks.new(lambda {}, true).call({}) true end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb new file mode 100644 index 0000000000..d972050dc9 --- /dev/null +++ b/railties/lib/rails/engine.rb @@ -0,0 +1,111 @@ +require 'active_support/core_ext/module/delegation' +require 'rails/railtie' + +module Rails + class Engine < Railtie + autoload :Configurable, "rails/engine/configurable" + autoload :Configuration, "rails/engine/configuration" + + class << self + attr_accessor :called_from + + def inherited(base) + unless abstract_railtie?(base) + base.called_from = begin + call_stack = caller.map { |p| p.split(':').first } + File.dirname(call_stack.detect { |p| p !~ %r[railties/lib/rails|rack/lib/rack] }) + end + end + + super + end + + def find_root_with_flag(flag, default=nil) + root_path = self.called_from + + while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}") + parent = File.dirname(root_path) + root_path = parent != root_path && parent + end + + root = File.exist?("#{root_path}/#{flag}") ? root_path : default + raise "Could not find root path for #{self}" unless root + + RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? + Pathname.new(root).expand_path : Pathname.new(root).realpath + end + end + + delegate :middleware, :paths, :root, :to => :config + + def load_tasks + super + config.paths.lib.tasks.to_a.sort.each { |ext| load(ext) } + end + + # Add configured load paths to ruby load paths and remove duplicates. + initializer :set_load_path do + config.load_paths.reverse_each do |path| + $LOAD_PATH.unshift(path) if File.directory?(path) + end + $LOAD_PATH.uniq! + end + + # Set the paths from which Rails will automatically load source files, + # and the load_once paths. + initializer :set_autoload_paths do |app| + ActiveSupport::Dependencies.load_paths.concat(config.load_paths) + + if reloadable?(app) + ActiveSupport::Dependencies.load_once_paths.concat(config.load_once_paths) + else + ActiveSupport::Dependencies.load_once_paths.concat(config.load_paths) + end + + # Freeze so future modifications will fail rather than do nothing mysteriously + config.load_paths.freeze + config.load_once_paths.freeze + end + + initializer :add_routing_files do + config.paths.config.routes.to_a.each do |route| + config.action_dispatch.route_files.unshift(route) if File.exists?(route) + end + end + + initializer :add_locales do + config.i18n.load_path.unshift(*config.paths.config.locales.to_a) + end + + initializer :add_view_paths do + views = config.paths.app.views.to_a + ActionController::Base.view_paths.concat(views) if defined?(ActionController) + ActionMailer::Base.view_paths.concat(views) if defined?(ActionMailer) + end + + initializer :load_application_initializers do + config.paths.config.initializers.each do |initializer| + load(initializer) + end + end + + initializer :load_application_classes do |app| + next if $rails_rake_task + + if app.config.cache_classes + config.eager_load_paths.each do |load_path| + matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ + Dir.glob("#{load_path}/**/*.rb").sort.each do |file| + require_dependency file.sub(matcher, '\1') + end + end + end + end + + protected + + def reloadable?(app) + app.config.reload_plugins + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/engine/configurable.rb b/railties/lib/rails/engine/configurable.rb new file mode 100644 index 0000000000..d4b7ecc532 --- /dev/null +++ b/railties/lib/rails/engine/configurable.rb @@ -0,0 +1,24 @@ +module Rails + class Engine + module Configurable + def self.included(base) + base.extend ClassMethods + base.delegate :middleware, :root, :paths, :to => :config + end + + module ClassMethods + def config + @config ||= Engine::Configuration.new(find_root_with_flag("lib")) + end + + def inherited(base) + raise "You cannot inherit from a Rails::Engine child" + end + end + + def config + self.class.config + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb new file mode 100644 index 0000000000..e2fdf125d3 --- /dev/null +++ b/railties/lib/rails/engine/configuration.rb @@ -0,0 +1,48 @@ +require 'rails/railtie/configuration' + +module Rails + class Engine + class Configuration < ::Rails::Railtie::Configuration + attr_reader :root + attr_writer :eager_load_paths, :load_once_paths, :load_paths + + def initialize(root=nil) + @root = root + end + + def paths + @paths ||= begin + paths = Rails::Paths::Root.new(@root) + paths.app "app", :eager_load => true, :glob => "*" + paths.app.controllers "app/controllers", :eager_load => true + paths.app.metals "app/metal", :eager_load => true + paths.app.views "app/views" + paths.lib "lib", :load_path => true + paths.lib.tasks "lib/tasks", :glob => "**/*.rake" + paths.config "config" + paths.config.environment "config/environments", :glob => "#{Rails.env}.rb" + paths.config.initializers "config/initializers", :glob => "**/*.rb" + paths.config.locales "config/locales", :glob => "*.{rb,yml}" + paths.config.routes "config/routes.rb" + paths + end + end + + def root=(value) + @root = paths.path = Pathname.new(value).expand_path + end + + def eager_load_paths + @eager_load_paths ||= paths.eager_load + end + + def load_once_paths + @eager_load_paths ||= paths.load_once + end + + def load_paths + @load_paths ||= paths.load_paths + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 8fcb254590..cea4a0fdf7 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -19,12 +19,6 @@ module Rails @options[:after] end - def global - @options[:global] - end - - alias global? global - def run(*args) @context.instance_exec(*args, &block) end @@ -71,7 +65,7 @@ module Rails def initializers @initializers ||= begin - initializers = self.class.initializers_for(:instance) + initializers = self.class.initializers_chain Collection.new(initializers.map { |i| i.bind(self) }) end end @@ -81,26 +75,23 @@ module Rails @initializers ||= [] end - def initializers_for(scope = :global) + def initializers_chain initializers = Collection.new ancestors.reverse_each do |klass| next unless klass.respond_to?(:initializers) - initializers = initializers + klass.initializers.select { |i| - (scope == :global) == !!i.global? - } + initializers = initializers + klass.initializers end initializers end def initializer(name, opts = {}, &blk) raise ArgumentError, "A block must be passed when defining an initializer" unless blk - @initializers ||= [] - @initializers << Initializer.new(name, nil, opts, &blk) + initializers << Initializer.new(name, nil, opts, &blk) end def run_initializers(*args) return if @ran - initializers_for(:global).each do |initializer| + initializers_chain.each do |initializer| instance_exec(*args, &initializer.block) end @ran = true diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb index b3d105d8c7..88c0c1c68c 100644 --- a/railties/lib/rails/paths.rb +++ b/railties/lib/rails/paths.rb @@ -1,7 +1,7 @@ require 'set' module Rails - class Application + module Paths module PathParent def method_missing(id, *args) name = id.to_s @@ -39,6 +39,7 @@ module Rails all_paths.map { |path| path.paths if path.eager_load? }.compact.flatten.uniq end + # TODO Discover why do we need to call uniq! here def all_paths @all_paths.uniq! @all_paths @@ -48,12 +49,6 @@ module Rails all_paths.map { |path| path.paths if path.load_path? }.compact.flatten.uniq end - def add_to_load_path - load_paths.reverse_each do |path| - $LOAD_PATH.unshift(path) if File.directory?(path) - end - end - def push(*) raise "Application root can only have one physical path" end @@ -74,11 +69,11 @@ module Rails @children = {} @root = root @paths = paths.flatten - @glob = @options[:glob] || "**/*.rb" + @glob = @options.delete(:glob) @load_once = @options[:load_once] @eager_load = @options[:eager_load] - @load_path = @options[:load_path] || @eager_load + @load_path = @options[:load_path] || @eager_load || @load_once @root.all_paths << self end @@ -103,6 +98,7 @@ module Rails def load_once! @load_once = true + @load_path = true end def load_once? @@ -128,10 +124,13 @@ module Rails def paths raise "You need to set a path root" unless @root.path - - @paths.map do |path| - path.index('/') == 0 ? path : File.expand_path(File.join(@root.path, path)) + result = @paths.map do |p| + path = File.expand_path(p, @root.path) + @glob ? Dir[File.join(path, @glob)] : path end + result.flatten! + result.uniq! + result end alias to_a paths diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index c3b81bcd3e..394e634903 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -1,5 +1,13 @@ +require 'rails/engine' + module Rails - class Plugin < Railtie + class Plugin < Engine + def self.inherited(base) + raise "You cannot inherit from Rails::Plugin" + end + + # TODO Right now, if a plugin has an Engine or a Railtie inside it, + # the initializers for this plugin will be executed twice. def self.all(list, paths) plugins = [] paths.each do |path| @@ -17,53 +25,19 @@ module Rails attr_reader :name, :path - def initialize(path) - @name = File.basename(path).to_sym - @path = path - end - - def load_paths - Dir["#{path}/{lib}", "#{path}/app/{models,controllers,helpers}"] + def initialize(root) + @name = File.basename(root).to_sym + config.root = root end - def load_tasks - Dir["#{path}/{tasks,lib/tasks,rails/tasks}/**/*.rake"].sort.each { |ext| load ext } + def config + @config ||= Engine::Configuration.new end - initializer :add_to_load_path, :after => :set_autoload_paths do |app| - load_paths.each do |path| - $LOAD_PATH << path - require "active_support/dependencies" - - ActiveSupport::Dependencies.load_paths << path - - unless app.config.reload_plugins - ActiveSupport::Dependencies.load_once_paths << path - end - end - end - - initializer :load_init_rb, :before => :load_application_initializers do |app| - file = "#{@path}/init.rb" + initializer :load_init_rb do |app| + file = Dir["#{root}/{rails/init,init}.rb"].first config = app.config - eval File.read(file), binding, file if File.file?(file) - end - - initializer :add_view_paths, :after => :initialize_framework_views do - plugin_views = "#{path}/app/views" - if File.directory?(plugin_views) - ActionController::Base.view_paths.concat([plugin_views]) if defined? ActionController - ActionMailer::Base.view_paths.concat([plugin_views]) if defined? ActionMailer - end - end - - # TODO Isn't it supposed to be :after => "action_controller.initialize_routing" ? - initializer :add_routing_file, :after => :initialize_routing do |app| - routing_file = "#{path}/config/routes.rb" - if File.exist?(routing_file) - app.route_configuration_files << routing_file - app.reload_routes! - end + eval(File.read(file), binding, file) if file && File.file?(file) end end end diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index e3297148e5..6424be5a55 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -1,44 +1,73 @@ +require 'rails/initializable' +require 'rails/configuration' + module Rails class Railtie + autoload :Configurable, "rails/railtie/configurable" + autoload :Configuration, "rails/railtie/configuration" + include Initializable - def self.plugin_name(plugin_name = nil) - @plugin_name ||= name.demodulize.underscore - @plugin_name = plugin_name if plugin_name - @plugin_name - end + ABSTRACT_RAILTIES = %w(Rails::Plugin Rails::Engine Rails::Application) - def self.inherited(klass) - @plugins ||= [] - @plugins << klass unless klass == Plugin - end + class << self + def subclasses + @subclasses ||= [] + end - def self.plugins - @plugins - end + def inherited(base) + unless abstract_railtie?(base) + base.send(:include, self::Configurable) if add_configurable?(base) + subclasses << base + end + end - def self.plugin_names - plugins.map { |p| p.plugin_name } - end + # TODO This should be called railtie_name and engine_name + def plugin_name(plugin_name = nil) + @plugin_name ||= name.demodulize.underscore + @plugin_name = plugin_name if plugin_name + @plugin_name + end - def self.config - Configuration.default - end + # TODO Deprecate me + def plugins + subclasses + end - def self.subscriber(subscriber) - Rails::Subscriber.add(plugin_name, subscriber) - end + # TODO Deprecate me + def plugin_names + plugins.map { |p| p.plugin_name } + end - def self.rake_tasks(&blk) - @rake_tasks ||= [] - @rake_tasks << blk if blk - @rake_tasks - end + def subscriber(subscriber) + Rails::Subscriber.add(plugin_name, subscriber) + end + + def rake_tasks(&blk) + @rake_tasks ||= [] + @rake_tasks << blk if blk + @rake_tasks + end + + def generators(&blk) + @generators ||= [] + @generators << blk if blk + @generators + end + + protected + + def abstract_railtie?(base) + ABSTRACT_RAILTIES.include?(base.name) + end - def self.generators(&blk) - @generators ||= [] - @generators << blk if blk - @generators + # Just add configurable behavior if a Configurable module is defined + # and the class is a direct child from self. This is required to avoid + # application or plugins getting class configuration method from Railties + # and/or Engines. + def add_configurable?(base) + defined?(self::Configurable) && base.ancestors[1] == self + end end def rake_tasks @@ -50,12 +79,10 @@ module Rails end def load_tasks - return unless rake_tasks rake_tasks.each { |blk| blk.call } end def load_generators - return unless generators generators.each { |blk| blk.call } end end diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb new file mode 100644 index 0000000000..a2eb938c5a --- /dev/null +++ b/railties/lib/rails/railtie/configurable.rb @@ -0,0 +1,23 @@ +module Rails + class Railtie + module Configurable + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def config + @config ||= Railtie::Configuration.new + end + + def inherited(base) + raise "You cannot inherit from a Rails::Railtie child" + end + end + + def config + self.class.config + end + end + end +end diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb new file mode 100644 index 0000000000..bfb43f7041 --- /dev/null +++ b/railties/lib/rails/railtie/configuration.rb @@ -0,0 +1,9 @@ +require 'rails/configuration' + +module Rails + class Railtie + class Configuration + include Rails::Configuration::Shared + end + end +end
\ No newline at end of file diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 754c0f1839..6686d82f19 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -29,7 +29,7 @@ module ApplicationTests add_to_config <<-RUBY config.root = "#{app_path}" - config.eager_load_paths = "#{app_path}/lib" + config.eager_load_paths << "#{app_path}/lib" RUBY require "#{app_path}/config/environment" diff --git a/railties/test/application/notifications_test.rb b/railties/test/application/notifications_test.rb index db8605edbe..061bb34c19 100644 --- a/railties/test/application/notifications_test.rb +++ b/railties/test/application/notifications_test.rb @@ -1,17 +1,6 @@ require "isolation/abstract_unit" module ApplicationTests - class MyQueue - def publish(name, *args) - raise name - end - - # Not a full queue implementation - def method_missing(name, *args, &blk) - self - end - end - class MockLogger def method_missing(*args) @logged ||= [] @@ -39,22 +28,6 @@ module ApplicationTests ActiveSupport::Notifications.notifier.wait end - test "new queue is set" do - # We don't want to load all frameworks, so remove them and clean up environments. - use_frameworks [] - FileUtils.rm_rf("#{app_path}/config/environments") - - add_to_config <<-RUBY - config.notifications.notifier = ActiveSupport::Notifications::Notifier.new(ApplicationTests::MyQueue.new) - RUBY - - require "#{app_path}/config/environment" - - assert_raise RuntimeError do - ActiveSupport::Notifications.publish('foo') - end - end - test "rails subscribers are added" do add_to_config <<-RUBY config.colorize_logging = false diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index e308cbcb0e..0c7378cb64 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -10,14 +10,14 @@ module InitializableTests attr_accessor :foo, :bar end - initializer :omg, :global => true do + initializer :omg do @foo ||= 0 @foo += 1 end end class Bar < Foo - initializer :bar, :global => true do + initializer :bar do @bar ||= 0 @bar += 1 end @@ -26,7 +26,7 @@ module InitializableTests module Word include Rails::Initializable - initializer :word, :global => true do + initializer :word do $word = "bird" end end @@ -34,11 +34,11 @@ module InitializableTests class Parent include Rails::Initializable - initializer :one, :global => true do + initializer :one do $arr << 1 end - initializer :two, :global => true do + initializer :two do $arr << 2 end end @@ -46,17 +46,17 @@ module InitializableTests class Child < Parent include Rails::Initializable - initializer :three, :before => :one, :global => true do + initializer :three, :before => :one do $arr << 3 end - initializer :four, :after => :one, :global => true do + initializer :four, :after => :one do $arr << 4 end end class Parent - initializer :five, :before => :one, :global => true do + initializer :five, :before => :one do $arr << 5 end end @@ -72,11 +72,11 @@ module InitializableTests $arr << 2 end - initializer :three, :global => true do + initializer :three do $arr << 3 end - initializer :four, :global => true do + initializer :four do $arr << 4 end end @@ -181,13 +181,7 @@ module InitializableTests $arr = [] instance = Instance.new instance.run_initializers - assert_equal [1, 2], $arr - end - - test "running globals" do - $arr = [] - Instance.run_initializers - assert_equal [3, 4], $arr + assert_equal [1, 2, 3, 4], $arr end end diff --git a/railties/test/initializer/initialize_i18n_test.rb b/railties/test/initializer/initialize_i18n_test.rb index 472566378d..64396bddb4 100644 --- a/railties/test/initializer/initialize_i18n_test.rb +++ b/railties/test/initializer/initialize_i18n_test.rb @@ -15,6 +15,7 @@ module InitializerTests config.root = "#{app_path}" config.i18n.load_path << "my/other/locale.yml" RUBY + require "#{app_path}/config/environment" #{RAILS_FRAMEWORK_ROOT}/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml diff --git a/railties/test/initializer/path_test.rb b/railties/test/initializer/path_test.rb index 328dda6d2c..54bdddbdf2 100644 --- a/railties/test/initializer/path_test.rb +++ b/railties/test/initializer/path_test.rb @@ -8,6 +8,7 @@ module InitializerTests build_app boot_rails FileUtils.rm_rf("#{app_path}/config/environments") + app_file "config/environments/development.rb", "" add_to_config <<-RUBY config.root = "#{app_path}" config.after_initialize do @@ -36,19 +37,16 @@ module InitializerTests end test "booting up Rails yields a valid paths object" do - assert_path @paths.app, "app" assert_path @paths.app.metals, "app", "metal" - assert_path @paths.app.models, "app", "models" - assert_path @paths.app.helpers, "app", "helpers" - assert_path @paths.app.services, "app", "services" + assert_path @paths.app.views, "app", "views" assert_path @paths.lib, "lib" assert_path @paths.vendor, "vendor" assert_path @paths.vendor.plugins, "vendor", "plugins" assert_path @paths.tmp, "tmp" assert_path @paths.tmp.cache, "tmp", "cache" assert_path @paths.config, "config" - assert_path @paths.config.locales, "config", "locales" - assert_path @paths.config.environments, "config", "environments" + assert_path @paths.config.locales, "config", "locales", "en.yml" + assert_path @paths.config.environment, "config", "environments", "development.rb" assert_equal root("app", "controllers"), @paths.app.controllers.to_a.first assert_equal Pathname.new(File.dirname(__FILE__)).join("..", "..", "builtin", "rails_info").expand_path, @@ -56,27 +54,22 @@ module InitializerTests end test "booting up Rails yields a list of paths that are eager" do - assert @paths.app.models.eager_load? + assert @paths.app.eager_load? assert @paths.app.controllers.eager_load? - assert @paths.app.helpers.eager_load? assert @paths.app.metals.eager_load? end test "environments has a glob equal to the current environment" do - assert_equal "#{Rails.env}.rb", @paths.config.environments.glob + assert_equal "#{Rails.env}.rb", @paths.config.environment.glob end test "load path includes each of the paths in config.paths as long as the directories exist" do - assert_in_load_path "app" assert_in_load_path "app", "controllers" assert_in_load_path "app", "models" assert_in_load_path "app", "helpers" assert_in_load_path "lib" assert_in_load_path "vendor" - assert_not_in_load_path "app", "views" - assert_not_in_load_path "app", "metal" - assert_not_in_load_path "app", "services" assert_not_in_load_path "config" assert_not_in_load_path "config", "locales" assert_not_in_load_path "config", "environments" @@ -86,17 +79,17 @@ module InitializerTests test "controller paths include builtin in development mode" do Rails.env.replace "development" - assert Rails::Configuration.new.paths.app.controllers.paths.any? { |p| p =~ /builtin/ } + assert Rails::Application::Configuration.new("/").paths.app.controllers.paths.any? { |p| p =~ /builtin/ } end test "controller paths does not have builtin_directories in test mode" do Rails.env.replace "test" - assert !Rails::Configuration.new.paths.app.controllers.paths.any? { |p| p =~ /builtin/ } + assert !Rails::Application::Configuration.new("/").paths.app.controllers.paths.any? { |p| p =~ /builtin/ } end test "controller paths does not have builtin_directories in production mode" do Rails.env.replace "production" - assert !Rails::Configuration.new.paths.app.controllers.paths.any? { |p| p =~ /builtin/ } + assert !Rails::Application::Configuration.new("/").paths.app.controllers.paths.any? { |p| p =~ /builtin/ } end end diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index d60d6177f6..eafe7a64e1 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -3,22 +3,22 @@ require 'rails/paths' class PathsTest < ActiveSupport::TestCase def setup - @root = Rails::Application::Root.new("/foo/bar") + @root = Rails::Paths::Root.new("/foo/bar") end test "the paths object is initialized with the root path" do - root = Rails::Application::Root.new("/fiz/baz") + root = Rails::Paths::Root.new("/fiz/baz") assert_equal "/fiz/baz", root.path end test "the paths object can be initialized with nil" do assert_nothing_raised do - Rails::Application::Root.new(nil) + Rails::Paths::Root.new(nil) end end test "a paths object initialized with nil can be updated" do - root = Rails::Application::Root.new(nil) + root = Rails::Paths::Root.new(nil) root.app = "app" root.path = "/root" assert_equal ["/root/app"], root.app.to_a @@ -30,7 +30,7 @@ class PathsTest < ActiveSupport::TestCase end test "raises exception if root path never set" do - root = Rails::Application::Root.new(nil) + root = Rails::Paths::Root.new(nil) root.app = "app" assert_raises RuntimeError do root.app.to_a @@ -110,7 +110,7 @@ class PathsTest < ActiveSupport::TestCase end test "the root can only have one physical path" do - assert_raise(RuntimeError) { Rails::Application::Root.new(["/fiz", "/biz"]) } + assert_raise(RuntimeError) { Rails::Paths::Root.new(["/fiz", "/biz"]) } assert_raise(RuntimeError) { @root.push "/biz" } assert_raise(RuntimeError) { @root.unshift "/biz" } assert_raise(RuntimeError) { @root.concat ["/biz"]} @@ -193,12 +193,7 @@ class PathsTest < ActiveSupport::TestCase assert_equal 2, @root.eager_load.size end - test "a path should have a glob that defaults to **/*.rb" do - @root.app = "/app" - assert_equal "**/*.rb", @root.app.glob - end - - test "it should be possible to override a path's default glob" do + test "it should be possible to add a path's default glob" do @root.app = "/app" @root.app.glob = "*.rb" assert_equal "*.rb", @root.app.glob @@ -227,4 +222,11 @@ class PathsTest < ActiveSupport::TestCase @root.app.eager_load! assert_equal ["/foo/bar/app"], @root.load_paths end + + test "adding a path to the load once paths also adds it to the load path" do + @root.app = "app" + @root.app.load_once! + assert_equal ["/foo/bar/app"], @root.load_paths + end + end |