aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/engine.rb
diff options
context:
space:
mode:
Diffstat (limited to 'railties/lib/rails/engine.rb')
-rw-r--r--railties/lib/rails/engine.rb110
1 files changed, 63 insertions, 47 deletions
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 83cee28fa3..5757d235d2 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -2,11 +2,12 @@ require 'rails/railtie'
require 'rails/engine/railties'
require 'active_support/core_ext/module/delegation'
require 'pathname'
+require 'thread'
module Rails
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
# functionality and share it with other applications or within a larger packaged application.
- # Since Rails 3.0, every <tt>Rails::Application</tt> is just an engine, which allows for simple
+ # Every <tt>Rails::Application</tt> is just an engine, which allows for simple
# feature and application sharing.
#
# Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
@@ -15,10 +16,9 @@ module Rails
#
# == Creating an Engine
#
- # In Rails versions prior to 3.0, your gems automatically behaved as engines, however,
- # this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
- # behave as an engine, you have to specify an +Engine+ for it somewhere inside
- # your plugin's +lib+ folder (similar to how we specify a +Railtie+):
+ # If you want a gem to behave as an engine, you have to specify an +Engine+
+ # for it somewhere inside your plugin's +lib+ folder (similar to how we
+ # specify a +Railtie+):
#
# # lib/my_engine.rb
# module MyEngine
@@ -69,10 +69,9 @@ module Rails
#
# == Paths
#
- # Since Rails 3.0, applications and engines have more flexible path configuration (as
- # opposed to the previous hardcoded path configuration). This means that you are not
- # required to place your controllers at <tt>app/controllers</tt>, but in any place
- # which you find convenient.
+ # Applications and engines have flexible path configuration, meaning that you
+ # are not required to place your controllers at <tt>app/controllers</tt>, but
+ # in any place which you find convenient.
#
# For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
# You can set that as an option:
@@ -206,42 +205,51 @@ module Rails
# With such an engine, everything that is inside the +MyEngine+ module will be isolated from
# the application.
#
- # Consider such controller:
+ # Consider this controller:
#
# module MyEngine
# class FooController < ActionController::Base
# end
# end
#
- # If an engine is marked as isolated, +FooController+ has access only to helpers from +Engine+ and
- # <tt>url_helpers</tt> from <tt>MyEngine::Engine.routes</tt>.
+ # If the +MyEngine+ engine is marked as isolated, +FooController+ only has
+ # access to helpers from +MyEngine+, and <tt>url_helpers</tt> from
+ # <tt>MyEngine::Engine.routes</tt>.
#
- # The next thing that changes in isolated engines is the behavior of routes. Normally, when you namespace
- # your controllers, you also need to namespace all your routes. With an isolated engine,
- # the namespace is applied by default, so you can ignore it in routes:
+ # The next thing that changes in isolated engines is the behavior of routes.
+ # Normally, when you namespace your controllers, you also need to namespace
+ # the related routes. With an isolated engine, the engine's namespace is
+ # automatically applied, so you don't need to specify it explicity in your
+ # routes:
#
# MyEngine::Engine.routes.draw do
# resources :articles
# end
#
- # The routes above will automatically point to <tt>MyEngine::ArticlesController</tt>. Furthermore, you don't
- # need to use longer url helpers like <tt>my_engine_articles_path</tt>. Instead, you should simply use
- # <tt>articles_path</tt> as you would do with your application.
+ # If +MyEngine+ is isolated, The routes above will point to
+ # <tt>MyEngine::ArticlesController</tt>. You also don't need to use longer
+ # url helpers like +my_engine_articles_path+. Instead, you should simply use
+ # +articles_path+, like you would do with your main application.
#
- # To make that behavior consistent with other parts of the framework, an isolated engine also has influence on
- # <tt>ActiveModel::Naming</tt>. When you use a namespaced model, like <tt>MyEngine::Article</tt>, it will normally
- # use the prefix "my_engine". In an isolated engine, the prefix will be omitted in url helpers and
- # form fields for convenience.
+ # To make this behavior consistent with other parts of the framework,
+ # isolated engines also have an effect on <tt>ActiveModel::Naming</tt>. In a
+ # normal Rails app, when you use a namespaced model such as
+ # <tt>Namespace::Article</tt>, <tt>ActiveModel::Naming</tt> will generate
+ # names with the prefix "namespace". In an isolated engine, the prefix will
+ # be omitted in url helpers and form fields, for convenience.
#
- # polymorphic_url(MyEngine::Article.new) # => "articles_path"
+ # polymorphic_url(MyEngine::Article.new)
+ # # => "articles_path" # not "my_engine_articles_path"
#
# form_for(MyEngine::Article.new) do
# text_field :title # => <input type="text" name="article[title]" id="article_title" />
# end
#
- # Additionally, an isolated engine will set its name according to namespace, so
- # MyEngine::Engine.engine_name will be "my_engine". It will also set MyEngine.table_name_prefix
- # to "my_engine_", changing the MyEngine::Article model to use the my_engine_articles table.
+ # Additionally, an isolated engine will set its own name according to its
+ # namespace, so <tt>MyEngine::Engine.engine_name</tt> will return
+ # "my_engine". It will also set +MyEngine.table_name_prefix+ to "my_engine_",
+ # meaning for example that <tt>MyEngine::Article</tt> will use the
+ # +my_engine_articles+ database table by default.
#
# == Using Engine's routes outside Engine
#
@@ -350,12 +358,7 @@ module Rails
Rails::Railtie::Configuration.eager_load_namespaces << base
base.called_from = begin
- call_stack = if Kernel.respond_to?(:caller_locations)
- caller_locations.map { |l| l.absolute_path || l.path }
- else
- # Remove the line number from backtraces making sure we don't leave anything behind
- caller.map { |p| p.sub(/:\d+.*/, '') }
- end
+ call_stack = caller_locations.map { |l| l.absolute_path || l.path }
File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
end
@@ -405,7 +408,7 @@ module Rails
end
end
- # Finds engine with given path
+ # Finds engine with given path.
def find(path)
expanded_path = File.expand_path path
Rails::Engine.subclasses.each do |klass|
@@ -427,6 +430,7 @@ module Rails
@env_config = nil
@helpers = nil
@routes = nil
+ @app_build_lock = Mutex.new
super
end
@@ -497,10 +501,13 @@ module Rails
# Returns the underlying rack application for this engine.
def app
- @app ||= begin
- config.middleware = config.middleware.merge_into(default_middleware_stack)
- config.middleware.build(endpoint)
- end
+ @app || @app_build_lock.synchronize {
+ @app ||= begin
+ stack = default_middleware_stack
+ config.middleware = build_middleware.merge_into(stack)
+ config.middleware.build(endpoint)
+ end
+ }
end
# Returns the endpoint for this engine. If none is registered,
@@ -511,18 +518,13 @@ module Rails
# Define the Rack API for this engine.
def call(env)
- env.merge!(env_config)
- if env['SCRIPT_NAME']
- env[routes.env_key] = env['SCRIPT_NAME'].dup
- end
- app.call(env)
+ req = build_request env
+ app.call req.env
end
# Defines additional Rack env configuration that is added on each call.
def env_config
- @env_config ||= {
- 'action_dispatch.routes' => routes
- }
+ @env_config ||= {}
end
# Defines the routes for this engine. If a block is given to
@@ -559,7 +561,7 @@ module Rails
# and the load_once paths.
#
# This needs to be an initializer, since it needs to run once
- # per engine and get the engine as a block parameter
+ # per engine and get the engine as a block parameter.
initializer :set_autoload_paths, before: :bootstrap_hook do
ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
@@ -582,7 +584,7 @@ module Rails
# I18n load paths are a special case since the ones added
# later have higher priority.
initializer :add_locales do
- config.i18n.railties_load_path.concat(paths["config/locales"].existent)
+ config.i18n.railties_load_path << paths["config/locales"]
end
initializer :add_view_paths do
@@ -684,5 +686,19 @@ module Rails
def _all_load_paths #:nodoc:
@_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
end
+
+ private
+
+ def build_request(env)
+ env.merge!(env_config)
+ req = ActionDispatch::Request.new env
+ req.routes = routes
+ req.engine_script_name = req.script_name
+ req
+ end
+
+ def build_middleware
+ config.middleware
+ end
end
end