Skip to content

Commit

Permalink
Merge pull request #10099 from wangjohn/railties_each_with_config_2nd…
Browse files Browse the repository at this point in the history
…_attempt

Created way of cloning a Rails::Application instance
  • Loading branch information
spastorino committed Jul 1, 2013
2 parents 18099b0 + 08dc924 commit 534271d
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 7 deletions.
85 changes: 78 additions & 7 deletions railties/lib/rails/application.rb
Expand Up @@ -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'
Expand All @@ -61,12 +84,16 @@ class Application < Engine

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
Expand All @@ -75,14 +102,28 @@ def inherited(base)

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.
Expand Down Expand Up @@ -141,6 +182,30 @@ def env_config
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,
Expand All @@ -158,7 +223,9 @@ def env_config
# 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:
Expand Down Expand Up @@ -202,6 +269,10 @@ def config #:nodoc:
@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
Expand Down
148 changes: 148 additions & 0 deletions railties/test/application/multiple_applications_test.rb
@@ -0,0 +1,148 @@
require 'isolation/abstract_unit'

module ApplicationTests
class MultipleApplicationsTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation

def setup
build_app(initializers: true)
boot_rails
require "#{rails_root}/config/environment"
end

def teardown
teardown_app
end

def test_cloning_an_application_makes_a_shallow_copy_of_config
clone = Rails.application.clone

assert_equal Rails.application.config, clone.config, "The cloned application should get a copy of the config"
assert_equal Rails.application.config.secret_key_base, clone.config.secret_key_base, "The base secret key on the config should be the same"
end

def test_initialization_of_multiple_copies_of_same_application
application1 = AppTemplate::Application.new
application2 = AppTemplate::Application.new

assert_not_equal Rails.application.object_id, application1.object_id, "New applications should not be the same as the original application"
assert_not_equal Rails.application.object_id, application2.object_id, "New applications should not be the same as the original application"
end

def test_initialization_of_application_with_previous_config
application1 = AppTemplate::Application.new(config: Rails.application.config)
application2 = AppTemplate::Application.new

assert_equal Rails.application.config, application1.config, "Creating a new application while setting an initial config should result in the same config"
assert_not_equal Rails.application.config, application2.config, "New applications without setting an initial config should not have the same config"
end

def test_initialization_of_application_with_previous_railties
application1 = AppTemplate::Application.new(railties: Rails.application.railties)
application2 = AppTemplate::Application.new

assert_equal Rails.application.railties, application1.railties
assert_not_equal Rails.application.railties, application2.railties
end

def test_initialize_new_application_with_all_previous_initialization_variables
application1 = AppTemplate::Application.new(
config: Rails.application.config,
railties: Rails.application.railties,
routes_reloader: Rails.application.routes_reloader,
reloaders: Rails.application.reloaders,
routes: Rails.application.routes,
helpers: Rails.application.helpers,
app_env_config: Rails.application.env_config
)

assert_equal Rails.application.config, application1.config
assert_equal Rails.application.railties, application1.railties
assert_equal Rails.application.routes_reloader, application1.routes_reloader
assert_equal Rails.application.reloaders, application1.reloaders
assert_equal Rails.application.routes, application1.routes
assert_equal Rails.application.helpers, application1.helpers
assert_equal Rails.application.env_config, application1.env_config
end

def test_rake_tasks_defined_on_different_applications_go_to_the_same_class
$run_count = 0

application1 = AppTemplate::Application.new
application1.rake_tasks do
$run_count += 1
end

application2 = AppTemplate::Application.new
application2.rake_tasks do
$run_count += 1
end

require "#{app_path}/config/environment"

assert_equal 0, $run_count, "The count should stay at zero without any calls to the rake tasks"
require 'rake'
require 'rake/testtask'
require 'rdoc/task'
Rails.application.load_tasks
assert_equal 2, $run_count, "Calling a rake task should result in two increments to the count"
end

def test_multiple_applications_can_be_initialized
assert_nothing_raised { AppTemplate::Application.new }
end

def test_initializers_run_on_different_applications_go_to_the_same_class
application1 = AppTemplate::Application.new
$run_count = 0

AppTemplate::Application.initializer :init0 do
$run_count += 1
end

application1.initializer :init1 do
$run_count += 1
end

AppTemplate::Application.new.initializer :init2 do
$run_count += 1
end

assert_equal 0, $run_count, "Without loading the initializers, the count should be 0"

# Set config.eager_load to false so that a eager_load warning doesn't pop up
AppTemplate::Application.new { config.eager_load = false }.initialize!

assert_equal 3, $run_count, "There should have been three initializers that incremented the count"
end

def test_runners_run_on_different_applications_go_to_the_same_class
$run_count = 0
AppTemplate::Application.runner { $run_count += 1 }
AppTemplate::Application.new.runner { $run_count += 1 }

assert_equal 0, $run_count, "Without loading the runners, the count should be 0"
Rails.application.load_runner
assert_equal 2, $run_count, "There should have been two runners that increment the count"
end

def test_isolate_namespace_on_an_application
assert_nil Rails.application.railtie_namespace, "Before isolating namespace, the railtie namespace should be nil"
Rails.application.isolate_namespace(AppTemplate)
assert_equal Rails.application.railtie_namespace, AppTemplate, "After isolating namespace, we should have a namespace"
end

def test_inserting_configuration_into_application
app = AppTemplate::Application.new(config: Rails.application.config)
new_config = Rails::Application::Configuration.new("root_of_application")
new_config.secret_key_base = "some_secret_key_dude"
app.config.secret_key_base = "a_different_secret_key"

assert_equal "a_different_secret_key", app.config.secret_key_base, "The configuration's secret key should be set."
app.config = new_config
assert_equal "some_secret_key_dude", app.config.secret_key_base, "The configuration's secret key should have changed."
assert_equal "root_of_application", app.config.root, "The root should have changed to the new config's root."
assert_equal new_config, app.config, "The application's config should have changed to the new config."
end
end
end

0 comments on commit 534271d

Please sign in to comment.