From 08dc92421a3c08383dd9cd4c2c62966e8d4c9560 Mon Sep 17 00:00:00 2001
From: wangjohn <wangjohn@mit.edu>
Date: Thu, 4 Apr 2013 21:25:31 -0400
Subject: Allowing multiple rails applications in the same ruby instance.

This change provides the ability to create a new application with a
configuration which can be specified.
---
 railties/lib/rails/application.rb | 85 +++++++++++++++++++++++++++++++++++----
 1 file changed, 78 insertions(+), 7 deletions(-)

(limited to 'railties/lib')

diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index b5c5a6191f..6fd01ee768 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -51,6 +51,29 @@ module Rails
   #   10) Run config.before_eager_load and eager_load! if eager_load is true
   #   11) Run config.after_initialize callbacks
   #
+  # == Multiple Applications
+  #
+  # If you decide to define multiple applications, then the first application
+  # that is initialized will be set to +Rails.application+, unless you override
+  # it with a different application.
+  #
+  # To create a new application, you can instantiate a new instance of a class
+  # that has already been created:
+  #
+  #   class Application < Rails::Application
+  #   end
+  #
+  #   first_application  = Application.new
+  #   second_application = Application.new(config: first_application.config)
+  #
+  # In the above example, the configuration from the first application was used
+  # to initialize the second application. You can also use the +initialize_copy+
+  # on one of the applications to create a copy of the application which shares
+  # the configuration.
+  #
+  # If you decide to define rake tasks, runners, or initializers in an
+  # application other than +Rails.application+, then you must run those
+  # these manually.
   class Application < Engine
     autoload :Bootstrap,              'rails/application/bootstrap'
     autoload :Configuration,          'rails/application/configuration'
@@ -61,12 +84,16 @@ module Rails
 
     class << self
       def inherited(base)
-        raise "You cannot have more than one Rails::Application" if Rails.application
         super
-        Rails.application = base.instance
-        Rails.application.add_lib_to_load_path!
-        ActiveSupport.run_load_hooks(:before_configuration, base.instance)
+        Rails.application ||= base.instance
       end
+
+      # Makes the +new+ method public.
+      #
+      # Note that Rails::Application inherits from Rails::Engine, which
+      # inherits from Rails::Railtie and the +new+ method on Rails::Railtie is
+      # private
+      public :new
     end
 
     attr_accessor :assets, :sandbox
@@ -75,14 +102,28 @@ module Rails
 
     delegate :default_url_options, :default_url_options=, to: :routes
 
-    def initialize
-      super
+    INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders,
+                         :routes, :helpers, :app_env_config] # :nodoc:
+
+    def initialize(initial_variable_values = {}, &block)
+      super()
       @initialized      = false
       @reloaders        = []
       @routes_reloader  = nil
       @app_env_config   = nil
       @ordered_railties = nil
       @railties         = nil
+
+      add_lib_to_load_path!
+      ActiveSupport.run_load_hooks(:before_configuration, self)
+
+      initial_variable_values.each do |variable_name, value|
+        if INITIAL_VARIABLES.include?(variable_name)
+          instance_variable_set("@#{variable_name}", value)
+        end
+      end
+
+      instance_eval(&block) if block_given?
     end
 
     # Returns true if the application is initialized.
@@ -141,6 +182,30 @@ module Rails
       end
     end
 
+    # If you try to define a set of rake tasks on the instance, these will get
+    # passed up to the rake tasks defined on the application's class.
+    def rake_tasks(&block)
+      self.class.rake_tasks(&block)
+    end
+
+    # Sends the initializers to the +initializer+ method defined in the
+    # Rails::Initializable module. Each Rails::Application class has its own
+    # set of initializers, as defined by the Initializable module.
+    def initializer(name, opts={}, &block)
+      self.class.initializer(name, opts, &block)
+    end
+
+    # Sends any runner called in the instance of a new application up
+    # to the +runner+ method defined in Rails::Railtie.
+    def runner(&blk)
+      self.class.runner(&blk)
+    end
+
+    # Sends the +isolate_namespace+ method up to the class method.
+    def isolate_namespace(mod)
+      self.class.isolate_namespace(mod)
+    end
+
     ## Rails internal API
 
     # This method is called just after an application inherits from Rails::Application,
@@ -158,7 +223,9 @@ module Rails
     # you need to load files in lib/ during the application configuration as well.
     def add_lib_to_load_path! #:nodoc:
       path = File.join config.root, 'lib'
-      $LOAD_PATH.unshift(path) if File.exists?(path)
+      if File.exists?(path) && !$LOAD_PATH.include?(path)
+        $LOAD_PATH.unshift(path)
+      end
     end
 
     def require_environment! #:nodoc:
@@ -202,6 +269,10 @@ module Rails
       @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
     end
 
+    def config=(configuration) #:nodoc:
+      @config = configuration
+    end
+
     def to_app #:nodoc:
       self
     end
-- 
cgit v1.2.3