aboutsummaryrefslogtreecommitdiffstats
path: root/railties
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2010-09-04 00:31:35 +0200
committerJosé Valim <jose.valim@gmail.com>2010-09-04 00:31:43 +0200
commit23a9455962f0362cf242ffa96db7a9e7fdb0804b (patch)
treec046d1285d078649db6833fa1b73a6c23f7f16ea /railties
parent63032c1162e96d3380168ca63ac42aa044dbebd6 (diff)
parentc3c1a1e14859e6716970283caeab0c4c3720862e (diff)
downloadrails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.tar.gz
rails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.tar.bz2
rails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.zip
This commit merges most of the work done by Piotr Sarnacki in his Ruby Summer of Code project.
His work brings several capabilities from app to engines, as routes, middleware stack, asset handling and much more. Please check Rails::Engine documentation for more refenrences. Merge remote branch 'drogus/engines'
Diffstat (limited to 'railties')
-rw-r--r--railties/lib/rails.rb5
-rw-r--r--railties/lib/rails/application.rb66
-rw-r--r--railties/lib/rails/application/bootstrap.rb7
-rw-r--r--railties/lib/rails/application/configurable.rb19
-rw-r--r--railties/lib/rails/application/configuration.rb32
-rw-r--r--railties/lib/rails/application/railties.rb26
-rw-r--r--railties/lib/rails/configuration.rb81
-rw-r--r--railties/lib/rails/engine.rb278
-rw-r--r--railties/lib/rails/engine/configurable.rb25
-rw-r--r--railties/lib/rails/engine/configuration.rb12
-rw-r--r--railties/lib/rails/engine/railties.rb23
-rw-r--r--railties/lib/rails/plugin.rb15
-rw-r--r--railties/lib/rails/railtie.rb29
-rw-r--r--railties/lib/rails/railtie/configurable.rb26
-rw-r--r--railties/lib/rails/railtie/configuration.rb10
-rw-r--r--railties/test/application/configuration_test.rb44
-rw-r--r--railties/test/application/initializers/frameworks_test.rb1
-rw-r--r--railties/test/application/loading_test.rb22
-rw-r--r--railties/test/railties/engine_test.rb494
-rw-r--r--railties/test/railties/mounted_engine_test.rb174
-rw-r--r--railties/test/railties/railtie_test.rb16
-rw-r--r--railties/test/railties/shared_tests.rb49
22 files changed, 1189 insertions, 265 deletions
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 7c41367a84..3663910281 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -94,10 +94,5 @@ module Rails
def public_path
application && application.paths.public.to_a.first
end
-
- def public_path=(path)
- ActiveSupport::Deprecation.warn "Setting Rails.public_path= is deprecated. " <<
- "Please set paths.public = in config/application.rb instead.", caller
- end
end
end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 5b26333486..8631a5df3e 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -41,24 +41,6 @@ module Rails
autoload :Railties, 'rails/application/railties'
class << self
- private :new
-
- def configure(&block)
- class_eval(&block)
- end
-
- def instance
- if self == Rails::Application
- if Rails.application
- ActiveSupport::Deprecation.warn "Calling a method in Rails::Application is deprecated, " <<
- "please call it directly in your application constant #{Rails.application.class.name}.", caller
- end
- Rails.application
- else
- @@instance ||= new
- end
- end
-
def inherited(base)
raise "You cannot have more than one Rails::Application" if Rails.application
super
@@ -66,19 +48,9 @@ module Rails
Rails.application.add_lib_to_load_path!
ActiveSupport.run_load_hooks(:before_configuration, base.instance)
end
-
- def respond_to?(*args)
- super || instance.respond_to?(*args)
- end
-
- protected
-
- def method_missing(*args, &block)
- instance.send(*args, &block)
- end
end
- delegate :middleware, :to => :config
+ delegate :default_url_options, :default_url_options=, :to => :routes
# This method is called just after an application inherits from Rails::Application,
# allowing the developer to load classes in lib and use them during application
@@ -108,14 +80,6 @@ module Rails
super
end
- def routes
- @routes ||= ActionDispatch::Routing::RouteSet.new
- end
-
- def railties
- @railties ||= Railties.new(config)
- end
-
def routes_reloader
@routes_reloader ||= ActiveSupport::FileUpdateChecker.new([]){ reload_routes! }
end
@@ -131,7 +95,9 @@ module Rails
end
def initialize!
+ raise "Application has been already initialized." if @initialized
run_initializers(self)
+ @initialized = true
self
end
@@ -156,38 +122,32 @@ module Rails
self
end
- def app
- @app ||= begin
- config.middleware = config.middleware.merge_into(default_middleware_stack)
- config.middleware.build(routes)
- end
- end
alias :build_middleware_stack :app
- def call(env)
- app.call(env.reverse_merge!(env_defaults))
- end
-
- def env_defaults
- @env_defaults ||= {
+ def env_config
+ @env_config ||= super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
- "action_dispatch.secret_token" => config.secret_token
- }
+ "action_dispatch.secret_token" => config.secret_token,
+ "action_dispatch.asset_path" => nil
+ })
end
def initializers
initializers = Bootstrap.initializers_for(self)
- railties.all { |r| initializers += r.initializers }
initializers += super
initializers += Finisher.initializers_for(self)
initializers
end
+ def config
+ @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
+ end
+
protected
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |middleware|
- middleware.use ::ActionDispatch::Static, paths.public.to_a.first if config.serve_static_assets
+ middleware.use ::ActionDispatch::Static, config.static_asset_paths if config.serve_static_assets
middleware.use ::Rack::Lock if !config.allow_concurrency
middleware.use ::Rack::Runtime
middleware.use ::Rails::Rack::Logger
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index 44e26b5713..e39b3bc705 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -6,10 +6,7 @@ module Rails
module Bootstrap
include Initializable
- initializer :load_environment_config do
- environment = config.paths.config.environments.to_a.first
- require environment if environment
- end
+ initializer :load_environment_hook do end
initializer :load_active_support do
require 'active_support/dependencies'
@@ -73,4 +70,4 @@ module Rails
end
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/application/configurable.rb b/railties/lib/rails/application/configurable.rb
deleted file mode 100644
index f598e33965..0000000000
--- a/railties/lib/rails/application/configurable.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Rails
- class Application
- module Configurable
- def self.included(base)
- base.extend ClassMethods
- end
-
- module ClassMethods
- def inherited(base)
- raise "You cannot inherit from a Rails::Application child"
- end
- end
-
- def config
- @config ||= Application::Configuration.new(self.class.find_root_with_flag("config.ru", Dir.pwd))
- 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
index c3418e0d80..477bbbc1e7 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -1,16 +1,13 @@
-require 'active_support/deprecation'
require 'active_support/core_ext/string/encoding'
require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
- include ::Rails::Configuration::Deprecated
-
attr_accessor :allow_concurrency, :cache_classes, :cache_store,
:encoding, :consider_all_requests_local, :dependency_loading,
- :filter_parameters, :log_level, :logger, :middleware,
- :plugins, :preload_frameworks, :reload_plugins,
+ :filter_parameters, :log_level, :logger,
+ :preload_frameworks, :reload_plugins,
:secret_token, :serve_static_assets, :session_options,
:time_zone, :whiny_nils
@@ -28,6 +25,22 @@ module Rails
@middleware = app_middleware
end
+ def asset_path=(value)
+ action_mailer.asset_path = value if respond_to?(:action_mailer) && action_mailer
+ action_controller.asset_path = value if respond_to?(:action_controller) && action_controller
+ super(value)
+ end
+
+ def asset_host=(value)
+ action_mailer.asset_host = value if action_mailer
+ action_controller.asset_host = value if action_controller
+ super(value)
+ end
+
+ def compiled_asset_path
+ "/"
+ end
+
def encoding=(value)
@encoding = value
if "ruby".encoding_aware?
@@ -48,19 +61,10 @@ module Rails
paths.app.controllers << builtin_controller if builtin_controller
paths.config.database "config/database.yml"
paths.config.environment "config/environment.rb"
- paths.config.environments "config/environments", :glob => "#{Rails.env}.rb"
paths.lib.templates "lib/templates"
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", :autoload => true, :glob => Rails.env
- end
paths
end
diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb
index b3e6693f89..c1d2de571f 100644
--- a/railties/lib/rails/application/railties.rb
+++ b/railties/lib/rails/application/railties.rb
@@ -1,31 +1,21 @@
-module Rails
- class Application
- class Railties
- # TODO Write tests for this behavior extracted from Application
- def initialize(config)
- @config = config
- end
+require 'rails/engine/railties'
+module Rails
+ class Application < Engine
+ class Railties < Rails::Engine::Railties
def all(&block)
- @all ||= railties + engines + plugins
+ @all ||= railties + engines + super
@all.each(&block) if block
@all
end
def railties
- @railties ||= ::Rails::Railtie.subclasses.map(&:new)
+ @railties ||= ::Rails::Railtie.subclasses.map(&:instance)
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
+ @engines ||= ::Rails::Engine.subclasses.map(&:instance)
end
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index e5af12b901..8369795e71 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -71,86 +71,5 @@ 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 paths.app.views= instead", caller
- paths.app.views = value
- end
-
- def view_path
- ActiveSupport::Deprecation.warn "config.view_path is deprecated, " <<
- "please do 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 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 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 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 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 paths.log= instead", caller
- paths.config.log = value
- end
-
- def log_path
- ActiveSupport::Deprecation.warn "config.log_path is deprecated, " <<
- "please do 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 paths.app.controllers= instead", caller
- paths.app.controllers = value
- end
-
- def controller_paths
- ActiveSupport::Deprecation.warn "config.controller_paths is deprecated, " <<
- "please do paths.app.controllers instead", caller
- paths.app.controllers.to_a.uniq
- end
-
- def cookie_secret=(value)
- ActiveSupport::Deprecation.warn "config.cookie_secret= is deprecated, " <<
- "please use config.secret_token= instead", caller
- self.secret_token = value
- end
-
- def cookie_secret
- ActiveSupport::Deprecation.warn "config.cookie_secret is deprecated, " <<
- "please use config.secret_token instead", caller
- self.secret_token
- end
- end
end
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 555bc9dbc8..e10980a6d9 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -2,6 +2,7 @@ require 'rails/railtie'
require 'active_support/core_ext/module/delegation'
require 'pathname'
require 'rbconfig'
+require 'rails/engine/railties'
module Rails
# Rails::Engine allows you to wrap a specific Rails application and share it accross
@@ -86,14 +87,172 @@ module Rails
# all folders under "app" are automatically added to the load path. So if you have
# "app/observers", it's added by default.
#
+ # == Endpoint
+ #
+ # Engine can be also a rack application. It can be useful if you have a rack application that
+ # you would like to wrap with Engine and provide some of the Engine's features.
+ #
+ # To do that, use endpoint method:
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # endpoint MyRackApplication
+ # end
+ # end
+ #
+ # Now you can mount your engine in application's routes just like that:
+ #
+ # MyRailsApp::Application.routes.draw do
+ # mount MyEngine::Engine => "/engine"
+ # end
+ #
+ # == Middleware stack
+ #
+ # As Engine can now be rack endpoint, it can also have a middleware stack. The usage is exactly
+ # the same as in application:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # middleware.use SomeMiddleware
+ # end
+ # end
+ #
+ # == Routes
+ #
+ # If you don't specify endpoint, routes will be used as default endpoint. You can use them
+ # just like you use application's routes:
+ #
+ # # ENGINE/config/routes.rb
+ # MyEngine::Engine.routes.draw do
+ # match "/" => "posts#index"
+ # end
+ #
+ # == Mount priority
+ #
+ # Note that now there can be more than one router in you application and it's better to avoid
+ # passing requests through many routers. Consider such situation:
+ #
+ # MyRailsApp::Application.routes.draw do
+ # mount MyEngine::Engine => "/blog"
+ # match "/blog/omg" => "main#omg"
+ # end
+ #
+ # MyEngine is mounted at "/blog" path and additionaly "/blog/omg" points application's controller.
+ # In such situation request to "/blog/omg" will go through MyEngine and if there is no such route
+ # in Engine's routes, it will be dispatched to "main#omg". It's much better to swap that:
+ #
+ # MyRailsApp::Application.routes.draw do
+ # match "/blog/omg" => "main#omg"
+ # mount MyEngine::Engine => "/blog"
+ # end
+ #
+ # Now, Engine will get only requests that were not handled by application.
+ #
+ # == Asset path
+ #
+ # When you use engine with its own public directory, you will probably want to copy or symlink it
+ # to application's public directory. To simplify generating paths for assets, you can set asset_path
+ # for an Engine:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # config.asset_path = "/my_engine/%s"
+ # end
+ # end
+ #
+ # With such config, asset paths will be automatically modified inside Engine:
+ # image_path("foo.jpg") #=> "/my_engine/images/foo.jpg"
+ #
+ # == Engine name
+ #
+ # There are some places where engine's name is used.
+ # * routes: when you mount engine with mount(MyEngine::Engine => '/my_engine'), it's used as default :as option
+ # * migrations: when you copy engine's migrations, they will be decorated with suffix based on engine_name, for example:
+ # 2010010203121314_create_users.my_engine.rb
+ #
+ # Engine name is set by default based on class name. For MyEngine::Engine it will be my_engine_engine.
+ # You can change it manually it manually using engine_name method:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # engine_name "my_engine"
+ # end
+ # end
+ #
+ # == Namespaced Engine
+ #
+ # Normally, when you create controllers, helpers and models inside engine, they are treated
+ # as they would be created inside application. One of the cosequences of that is including
+ # application's helpers and url_helpers inside controller. Sometimes, especially when your
+ # engine provides its own routes, you don't want that. To isolate engine's stuff from application
+ # you can use namespace method:
+ #
+ # module MyEngine
+ # class Engine < Rails::Engine
+ # namespace MyEngine
+ # end
+ # end
+ #
+ # With such Engine, everything that is inside MyEngine module, will be isolated from application.
+ #
+ # Consider such controller:
+ #
+ # module MyEngine
+ # class FooController < ActionController::Base
+ # end
+ # end
+ #
+ # If engine is marked as namespaced, FooController has access only to helpers from engine and
+ # url_helpers from MyEngine::Engine.routes.
+ #
+ # Additionaly namespaced engine will set its name according to namespace, so in that case:
+ # MyEngine::Engine.engine_name #=> "my_engine"
+ # and it will set MyEngine.table_name_prefix to "my_engine_"
+ #
+ # == Using Engine's routes outside Engine
+ #
+ # Since you can mount engine inside application's routes now, you do not have direct access to engine's
+ # url_helpers inside application. When you mount Engine in application's routes special helper is
+ # created to allow doing that. Consider such scenario:
+ #
+ # # APP/config/routes.rb
+ # MyApplication::Application.routes.draw do
+ # mount MyEngine::Engine => "/my_engine", :as => "my_engine"
+ # match "/foo" => "foo#index"
+ # end
+ #
+ # Now, you can use my_engine helper:
+ #
+ # class FooController < ApplicationController
+ # def index
+ # my_engine.root_url #=> /my_engine/
+ # end
+ # end
+ #
+ # There is also 'app' helper that gives you access to application's routes inside Engine:
+ #
+ # module MyEngine
+ # class BarController
+ # app.foo_path #=> /foo
+ # end
+ # end
+ #
+ # Note that :as option takes engine_name as default, so most of the time you can ommit it.
+ #
+ # If you want to generate url to engine's route using polymorphic_url, you can also use that helpers.
+ #
+ # Let's say that you want to create a form pointing to one of the engine's routes. All you need to do
+ # is passing helper as the first element in array with attributes for url:
+ #
+ # form_for([my_engine, @user])
+ #
+ # This code will use my_engine.user_path(@user) to generate proper route.
+ #
class Engine < Railtie
autoload :Configurable, "rails/engine/configurable"
autoload :Configuration, "rails/engine/configuration"
class << self
- attr_accessor :called_from
-
- # TODO Remove this. It's deprecated.
+ attr_accessor :called_from, :namespaced
alias :engine_name :railtie_name
def inherited(base)
@@ -122,9 +281,40 @@ module Rails
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ?
Pathname.new(root).expand_path : Pathname.new(root).realpath
end
+
+ def endpoint(endpoint = nil)
+ @endpoint = endpoint if endpoint
+ @endpoint
+ end
+
+ def namespace(mod)
+ # TODO: extract that into a module
+ engine_name(generate_railtie_name(mod))
+
+ _railtie = self
+ name = engine_name
+ mod.singleton_class.instance_eval do
+ define_method(:_railtie) do
+ _railtie
+ end
+
+ define_method(:table_name_prefix) do
+ "#{name}_"
+ end
+ end
+
+ self.routes.default_scope = {:module => name}
+
+ self.namespaced = true
+ end
+
+ def namespaced?
+ !!namespaced
+ end
end
- delegate :paths, :root, :to => :config
+ delegate :middleware, :root, :paths, :to => :config
+ delegate :engine_name, :namespaced?, :to => "self.class"
def load_tasks
super
@@ -140,6 +330,47 @@ module Rails
end
end
+ def railties
+ @railties ||= self.class::Railties.new(config)
+ end
+
+ def app
+ @app ||= begin
+ config.middleware = config.middleware.merge_into(default_middleware_stack)
+ config.middleware.build(endpoint)
+ end
+ end
+
+ def endpoint
+ self.class.endpoint || routes
+ end
+
+ def call(env)
+ app.call(env.merge!(env_config))
+ end
+
+ def env_config
+ @env_config ||= {
+ 'action_dispatch.routes' => routes,
+ 'action_dispatch.asset_path' => config.asset_path
+ }
+ end
+
+ def routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new
+ end
+
+ def initializers
+ initializers = []
+ railties.all { |r| initializers += r.initializers }
+ initializers += super
+ initializers
+ end
+
+ def config
+ @config ||= Engine::Configuration.new(find_root_with_flag("lib"))
+ end
+
# Add configured load paths to ruby load paths and remove duplicates.
initializer :set_load_path, :before => :bootstrap_hook do
_all_load_paths.reverse_each do |path|
@@ -196,6 +427,27 @@ module Rails
end
end
+ initializer :load_environment_config, :before => :load_environment_hook do
+ environment = config.paths.config.environments.to_a.first
+ require environment if environment
+ end
+
+ initializer :append_asset_paths do
+ config.asset_path = "/#{engine_name}%s" unless config.asset_path
+
+ public_path = config.paths.public.to_a.first
+ if config.compiled_asset_path && File.exist?(public_path)
+ config.static_asset_paths[config.compiled_asset_path] = public_path
+ end
+ end
+
+ initializer :prepend_helpers_path do
+ unless namespaced?
+ config.helpers_paths = [] unless config.respond_to?(:helpers_paths)
+ config.helpers_paths = config.paths.app.helpers.to_a + config.helpers_paths
+ end
+ end
+
initializer :load_config_initializers do
paths.config.initializers.to_a.sort.each do |initializer|
load(initializer)
@@ -208,6 +460,24 @@ module Rails
end
protected
+ def find_root_with_flag(flag, default=nil)
+ root_path = self.class.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
+
+ Config::CONFIG['host_os'] =~ /mswin|mingw/ ?
+ Pathname.new(root).expand_path : Pathname.new(root).realpath
+ end
+
+ def default_middleware_stack
+ ActionDispatch::MiddlewareStack.new
+ end
def _all_autoload_paths
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
diff --git a/railties/lib/rails/engine/configurable.rb b/railties/lib/rails/engine/configurable.rb
deleted file mode 100644
index 9a370f0abb..0000000000
--- a/railties/lib/rails/engine/configurable.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Rails
- class Engine
- module Configurable
- def self.included(base)
- base.extend ClassMethods
- end
-
- module ClassMethods
- delegate :middleware, :root, :paths, :to => :config
-
- 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
index 521ed95447..3ac8911ba8 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -5,10 +5,13 @@ module Rails
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
+ attr_accessor :middleware, :plugins, :asset_path
def initialize(root=nil)
super()
@root = root
+ @middleware = Rails::Configuration::MiddlewareStackProxy.new
+ @helpers_paths = []
end
def paths
@@ -26,9 +29,14 @@ module Rails
paths.config.initializers "config/initializers", :glob => "**/*.rb"
paths.config.locales "config/locales", :glob => "*.{rb,yml}"
paths.config.routes "config/routes.rb"
+ paths.config.environments "config/environments", :glob => "#{Rails.env}.rb"
paths.public "public"
paths.public.javascripts "public/javascripts"
paths.public.stylesheets "public/stylesheets"
+ paths.vendor "vendor", :load_path => true
+ paths.vendor.plugins "vendor/plugins"
+ paths.db "db"
+ paths.db.migrate "db/migrate"
paths
end
end
@@ -48,6 +56,10 @@ module Rails
def autoload_paths
@autoload_paths ||= paths.autoload_paths
end
+
+ def compiled_asset_path
+ asset_path % "" if asset_path
+ end
end
end
end
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
new file mode 100644
index 0000000000..389a7602c6
--- /dev/null
+++ b/railties/lib/rails/engine/railties.rb
@@ -0,0 +1,23 @@
+module Rails
+ class Engine < Railtie
+ class Railties
+ # TODO Write tests for this behavior extracted from Application
+ def initialize(config)
+ @config = config
+ end
+
+ def all(&block)
+ @all ||= plugins
+ @all.each(&block) if block
+ @all
+ 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
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 8d5132a5ca..c07ff2f9cf 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -18,6 +18,10 @@ module Rails
# root during the boot process.
#
class Plugin < Engine
+ def self.global_plugins
+ @global_plugins ||= []
+ end
+
def self.inherited(base)
raise "You cannot inherit from Rails::Plugin"
end
@@ -28,6 +32,11 @@ module Rails
Dir["#{path}/*"].each do |plugin_path|
plugin = new(plugin_path)
next unless list.include?(plugin.name) || list.include?(:all)
+ if global_plugins.include?(plugin.name)
+ warn "WARNING: plugin #{plugin.name} from #{path} was not loaded. Plugin with the same name has been already loaded."
+ next
+ end
+ global_plugins << plugin.name
plugins << plugin
end
end
@@ -39,6 +48,10 @@ module Rails
attr_reader :name, :path
+ def railtie_name
+ name.to_s
+ end
+
def load_tasks
super
load_deprecated_tasks
@@ -78,6 +91,8 @@ module Rails
ActiveSupport::Deprecation.warn "Use toplevel init.rb; rails/init.rb is deprecated: #{initrb}"
end
config = app.config
+ # TODO: think about evaling initrb in context of Engine (currently it's
+ # always evaled in context of Rails::Application)
eval(File.read(initrb), binding, initrb)
end
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 58b0d851f7..09650789ac 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -1,7 +1,6 @@
require 'rails/initializable'
require 'rails/configuration'
require 'active_support/inflector'
-require 'active_support/deprecation'
module Rails
# Railtie is the core of the Rails Framework and provides several hooks to extend
@@ -131,25 +130,19 @@ module Rails
ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Plugin Rails::Engine Rails::Application)
class << self
+ private :new
+
def subclasses
@subclasses ||= []
end
def inherited(base)
unless base.abstract_railtie?
- base.send(:include, self::Configurable)
+ base.send(:include, Railtie::Configurable)
subclasses << base
end
end
- def railtie_name(*)
- ActiveSupport::Deprecation.warn "railtie_name is deprecated and has no effect", caller
- end
-
- def log_subscriber(*)
- ActiveSupport::Deprecation.warn "log_subscriber is deprecated and has no effect", caller
- end
-
def rake_tasks(&blk)
@rake_tasks ||= []
@rake_tasks << blk if blk
@@ -171,6 +164,22 @@ module Rails
def abstract_railtie?
ABSTRACT_RAILTIES.include?(name)
end
+
+ def railtie_name(name = nil)
+ @railtie_name = name.to_s if name
+ @railtie_name ||= generate_railtie_name(self.name)
+ end
+
+ protected
+ def generate_railtie_name(class_or_module)
+ ActiveSupport::Inflector.underscore(class_or_module).gsub("/", "_")
+ end
+ end
+
+ delegate :railtie_name, :to => "self.class"
+
+ def config
+ @config ||= Railtie::Configuration.new
end
def eager_load!
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
index a2eb938c5a..b6d4ed2312 100644
--- a/railties/lib/rails/railtie/configurable.rb
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -6,17 +6,29 @@ module Rails
end
module ClassMethods
- def config
- @config ||= Railtie::Configuration.new
- end
+ delegate :config, :to => :instance
def inherited(base)
- raise "You cannot inherit from a Rails::Railtie child"
+ raise "You cannot inherit from a #{self.superclass.name} child"
end
- end
- def config
- self.class.config
+ def instance
+ @instance ||= new
+ end
+
+ def respond_to?(*args)
+ super || instance.respond_to?(*args)
+ end
+
+ def configure(&block)
+ class_eval(&block)
+ end
+
+ protected
+
+ def method_missing(*args, &block)
+ instance.send(*args, &block)
+ end
end
end
end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 4e6f94c534..f09e3940cc 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -5,6 +5,7 @@ module Rails
class Configuration
def initialize
@@options ||= {}
+ @@static_asset_paths = ActiveSupport::OrderedHash.new
end
# This allows you to modify the application's middlewares from Engines.
@@ -65,6 +66,13 @@ module Rails
super || @@options.key?(name.to_sym)
end
+ # static_asset_paths is a Hash containing asset_paths
+ # with associated public folders, like:
+ # { "/" => "/app/public", "/my_engine" => "app/engines/my_engine/public" }
+ def static_asset_paths
+ @@static_asset_paths
+ end
+
private
def method_missing(name, *args, &blk)
@@ -78,4 +86,4 @@ module Rails
end
end
end
-end \ No newline at end of file
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index d63d25b42e..6bf56f7052 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -26,18 +26,17 @@ module ApplicationTests
FileUtils.rm_rf(new_app) if File.directory?(new_app)
end
- test "Rails::Application.instance is nil until app is initialized" do
+ test "Rails.application is nil until app is initialized" do
require 'rails'
- assert_nil Rails::Application.instance
+ assert_nil Rails.application
require "#{app_path}/config/environment"
- assert_equal AppTemplate::Application.instance, Rails::Application.instance
+ assert_equal AppTemplate::Application.instance, Rails.application
end
- test "Rails::Application responds to all instance methods" do
+ test "Rails.application responds to all instance methods" do
require "#{app_path}/config/environment"
- assert_respond_to Rails::Application, :routes_reloader
- assert_equal Rails::Application.routes_reloader, Rails.application.routes_reloader
- assert_equal Rails::Application.routes_reloader, AppTemplate::Application.routes_reloader
+ assert_respond_to Rails.application, :routes_reloader
+ assert_equal Rails.application.routes_reloader, AppTemplate::Application.routes_reloader
end
test "Rails::Application responds to paths" do
@@ -125,22 +124,6 @@ module ApplicationTests
assert !ActionController.autoload?(:RecordIdentifier)
end
- test "runtime error is raised if config.frameworks= is used" do
- add_to_config "config.frameworks = []"
-
- assert_raises RuntimeError do
- require "#{app_path}/config/environment"
- end
- end
-
- test "runtime error is raised if config.frameworks is used" do
- add_to_config "config.frameworks -= []"
-
- assert_raises RuntimeError do
- require "#{app_path}/config/environment"
- end
- end
-
test "filter_parameters should be able to set via config.filter_parameters" do
add_to_config <<-RUBY
config.filter_parameters += [ :foo, 'bar', lambda { |key, value|
@@ -277,5 +260,20 @@ module ApplicationTests
get "/"
assert_not_equal res, last_response.body
end
+
+ test "config.asset_path is not passed through env" do
+ make_basic_app do |app|
+ app.config.asset_path = "/omg%s"
+ end
+
+ class ::OmgController < ActionController::Base
+ def index
+ render :inline => "<%= image_path('foo.jpg') %>"
+ end
+ end
+
+ get "/"
+ assert_equal "/omg/images/foo.jpg", last_response.body
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 4ff10091b1..6e9ceb6ef7 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -61,6 +61,7 @@ module ApplicationTests
require "#{app_path}/config/environment"
assert Foo.method_defined?(:foo_path)
+ assert Foo.method_defined?(:app)
assert_equal ["notify"], Foo.action_methods
end
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index ecf7904c39..a2abf642b8 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -42,6 +42,23 @@ class LoadingTest < Test::Unit::TestCase
User
end
+ test "load config/environments/environment before Bootstrap initializers" do
+ app_file "config/environments/development.rb", <<-RUBY
+ AppTemplate::Application.configure do
+ config.development_environment_loaded = true
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.before_initialize do
+ config.loaded = config.development_environment_loaded
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+ assert ::AppTemplate::Application.config.loaded
+ end
+
def test_descendants_are_cleaned_on_each_request_without_cache_classes
add_to_config <<-RUBY
config.cache_classes = false
@@ -72,6 +89,11 @@ class LoadingTest < Test::Unit::TestCase
assert_equal [], ActiveRecord::Base.descendants
end
+ test "initialize_cant_be_called_twice" do
+ require "#{app_path}/config/environment"
+ assert_raise(RuntimeError) { ::AppTemplate::Application.initialize! }
+ end
+
protected
def setup_ar!
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 7410a10712..0cc729907e 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1,8 +1,23 @@
require "isolation/abstract_unit"
require "railties/shared_tests"
+require 'stringio'
module RailtiesTest
class EngineTest < Test::Unit::TestCase
+ # TODO: it's copied from generators/test_case, maybe make a module with such helpers?
+ def capture(stream)
+ begin
+ stream = stream.to_s
+ eval "$#{stream} = StringIO.new"
+ yield
+ result = eval("$#{stream}").string
+ ensure
+ eval("$#{stream} = #{stream.upcase}")
+ end
+
+ result
+ end
+
include ActiveSupport::Testing::Isolation
include SharedTests
@@ -13,6 +28,7 @@ module RailtiesTest
plugin.write "lib/bukkits.rb", <<-RUBY
class Bukkits
class Engine < ::Rails::Engine
+ railtie_name "bukkits"
end
end
RUBY
@@ -50,5 +66,483 @@ module RailtiesTest
assert index < initializers.index { |i| i.name == :build_middleware_stack }
end
+
+
+ class Upcaser
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ response = @app.call(env)
+ response[2].upcase!
+ response
+ end
+ end
+
+ test "engine is a rack app and can have his own middleware stack" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, 'Hello World'] }
+
+ config.middleware.use ::RailtiesTest::EngineTest::Upcaser
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ mount(Bukkits::Engine => "/bukkits")
+ end
+ RUBY
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/bukkits")
+ response = Rails.application.call(env)
+
+ assert_equal "HELLO WORLD", response[2]
+ end
+
+ test "it provides routes as default endpoint" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ match "/foo" => lambda { |env| [200, {'Content-Type' => 'text/html'}, 'foo'] }
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ mount(Bukkits::Engine => "/bukkits")
+ end
+ RUBY
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/bukkits/foo")
+ response = Rails.application.call(env)
+
+ assert_equal "foo", response[2]
+ end
+
+ test "engine can load its own plugins" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @plugin.write "vendor/plugins/yaffle/init.rb", <<-RUBY
+ config.yaffle_loaded = true
+ RUBY
+
+ boot_rails
+
+ assert Bukkits::Engine.config.yaffle_loaded
+ end
+
+ test "engine does not load plugins that already exists in application" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @plugin.write "vendor/plugins/yaffle/init.rb", <<-RUBY
+ config.engine_yaffle_loaded = true
+ RUBY
+
+ app_file "vendor/plugins/yaffle/init.rb", <<-RUBY
+ config.app_yaffle_loaded = true
+ RUBY
+
+ warnings = capture(:stderr) { boot_rails }
+
+ assert !warnings.empty?
+ assert !Bukkits::Engine.config.respond_to?(:engine_yaffle_loaded)
+ assert Rails.application.config.app_yaffle_loaded
+ end
+
+ test "it loads its environment file" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+ @plugin.write "config/environments/development.rb", <<-RUBY
+ Bukkits::Engine.configure do
+ config.environment_loaded = true
+ end
+ RUBY
+
+ boot_rails
+
+ assert Bukkits::Engine.config.environment_loaded
+ end
+
+ test "it passes router in env" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, 'hello'] }
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/")
+ response = Bukkits::Engine.call(env)
+
+ assert_equal Bukkits::Engine.routes, env['action_dispatch.routes']
+
+ env = Rack::MockRequest.env_for("/")
+ response = Rails.application.call(env)
+
+ assert_equal Rails.application.routes, env['action_dispatch.routes']
+ end
+
+ test "it allows to set asset_path" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ end
+ end
+ RUBY
+
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ match "/foo" => "foo#index"
+ end
+ RUBY
+
+ @plugin.write "app/controllers/foo_controller.rb", <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ render :index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/foo/index.html.erb", <<-RUBY
+ <%= compute_public_path("/foo", "") %>
+ <%= image_path("foo.png") %>
+ <%= javascript_include_tag("foo") %>
+ <%= stylesheet_link_tag("foo") %>
+ RUBY
+
+
+ app_file "app/controllers/bar_controller.rb", <<-RUBY
+ class BarController < ActionController::Base
+ def index
+ render :index
+ end
+ end
+ RUBY
+
+ app_file "app/views/bar/index.html.erb", <<-RUBY
+ <%= compute_public_path("/foo", "") %>
+ RUBY
+
+ add_to_config 'config.asset_path = "/omg%s"'
+
+ @plugin.write 'public/touch.txt', <<-RUBY
+ touch
+ RUBY
+
+ boot_rails
+
+ # should set asset_path with engine name by default
+ assert_equal "/bukkits_engine%s", ::Bukkits::Engine.config.asset_path
+
+ ::Bukkits::Engine.config.asset_path = "/bukkits%s"
+
+ env = Rack::MockRequest.env_for("/foo")
+ response = Bukkits::Engine.call(env)
+ stripped_body = response[2].body.split("\n").map(&:strip).join("\n")
+
+ expected = "/omg/bukkits/foo\n" +
+ "/omg/bukkits/images/foo.png\n" +
+ "<script src=\"/omg/bukkits/javascripts/foo.js\" type=\"text/javascript\"></script>\n" +
+ "<link href=\"/omg/bukkits/stylesheets/foo.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
+ assert_equal expected, stripped_body
+ end
+
+ test "engine's files are served via ActionDispatch::Static" do
+ add_to_config "config.serve_static_assets = true"
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ engine_name :bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "public/bukkits.html", "/bukkits/bukkits.html"
+ app_file "public/app.html", "/app.html"
+ app_file "public/bukkits/file_from_app.html", "/bukkits/file_from_app.html"
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/app.html")
+ response = Rails.application.call(env)
+ assert_equal response[2].path, File.join(app_path, "public/app.html")
+
+ env = Rack::MockRequest.env_for("/bukkits/bukkits.html")
+ response = Rails.application.call(env)
+ assert_equal response[2].path, File.join(@plugin.path, "public/bukkits.html")
+
+ env = Rack::MockRequest.env_for("/bukkits/file_from_app.html")
+ response = Rails.application.call(env)
+ assert_equal response[2].path, File.join(app_path, "public/bukkits/file_from_app.html")
+ end
+
+ test "shared engine should include application's helpers and own helpers" do
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match "/foo" => "bukkits/foo#index", :as => "foo"
+ match "/foo/show" => "bukkits/foo#show"
+ match "/foo/bar" => "bukkits/foo#bar"
+ end
+ RUBY
+
+ app_file "app/helpers/some_helper.rb", <<-RUBY
+ module SomeHelper
+ def something
+ "Something... Something... Something..."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/bar_helper.rb", <<-RUBY
+ module BarHelper
+ def bar
+ "It's a bar."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
+ class Bukkits::FooController < ActionController::Base
+ def index
+ render :inline => "<%= something %>"
+ end
+
+ def show
+ render :text => foo_path
+ end
+
+ def bar
+ render :inline => "<%= bar %>"
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/foo")
+ response = Rails.application.call(env)
+ assert_equal "Something... Something... Something...", response[2].body
+
+ env = Rack::MockRequest.env_for("/foo/show")
+ response = Rails.application.call(env)
+ assert_equal "/foo", response[2].body
+
+ env = Rack::MockRequest.env_for("/foo/bar")
+ response = Rails.application.call(env)
+ assert_equal "It's a bar.", response[2].body
+ end
+
+ test "isolated engine should include only its own routes and helpers" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ namespace Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "app/models/bukkits/post.rb", <<-RUBY
+ module Bukkits
+ class Post
+ extend ActiveModel::Naming
+
+ def to_param
+ "1"
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match "/bar" => "bar#index", :as => "bar"
+ mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ match "/foo" => "foo#index", :as => "foo"
+ match "/foo/show" => "foo#show"
+ match "/from_app" => "foo#from_app"
+ match "/routes_helpers_in_view" => "foo#routes_helpers_in_view"
+ match "/polymorphic_path_without_namespace" => "foo#polymorphic_path_without_namespace"
+ resources :posts
+ end
+ RUBY
+
+ app_file "app/helpers/some_helper.rb", <<-RUBY
+ module SomeHelper
+ def something
+ "Something... Something... Something..."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/engine_helper.rb", <<-RUBY
+ module EngineHelper
+ def help_the_engine
+ "Helped."
+ end
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
+ class Bukkits::FooController < ActionController::Base
+ def index
+ render :inline => "<%= help_the_engine %>"
+ end
+
+ def show
+ render :text => foo_path
+ end
+
+ def from_app
+ render :inline => "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
+ end
+
+ def routes_helpers_in_view
+ render :inline => "<%= foo_path %>, <%= app.bar_path %>"
+ end
+
+ def polymorphic_path_without_namespace
+ render :text => polymorphic_path(Post.new)
+ end
+ end
+ RUBY
+
+ @plugin.write "app/mailers/bukkits/my_mailer.rb", <<-RUBY
+ module Bukkits
+ class MyMailer < ActionMailer::Base
+ end
+ end
+ RUBY
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ assert_equal "bukkits_", Bukkits.table_name_prefix
+ assert_equal "bukkits", Bukkits::Engine.engine_name
+ assert_equal Bukkits._railtie, Bukkits::Engine
+ assert ::Bukkits::MyMailer.method_defined?(:foo_path)
+ assert !::Bukkits::MyMailer.method_defined?(:bar_path)
+
+ env = Rack::MockRequest.env_for("/bukkits/from_app")
+ response = AppTemplate::Application.call(env)
+ assert_equal "false", response[2].body
+
+ env = Rack::MockRequest.env_for("/bukkits/foo/show")
+ response = AppTemplate::Application.call(env)
+ assert_equal "/bukkits/foo", response[2].body
+
+ env = Rack::MockRequest.env_for("/bukkits/foo")
+ response = AppTemplate::Application.call(env)
+ assert_equal "Helped.", response[2].body
+
+ env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view")
+ response = AppTemplate::Application.call(env)
+ assert_equal "/bukkits/foo, /bar", response[2].body
+
+ env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace")
+ response = AppTemplate::Application.call(env)
+ assert_equal "/bukkits/posts/1", response[2].body
+ end
+
+ test "isolated engine should avoid namespace in names if that's possible" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ namespace Bukkits
+ end
+ end
+ RUBY
+
+ @plugin.write "app/models/bukkits/post.rb", <<-RUBY
+ module Bukkits
+ class Post
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+ attr_accessor :title
+
+ def to_param
+ "1"
+ end
+
+ def persisted?
+ false
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ resources :posts
+ end
+ RUBY
+
+ @plugin.write "app/controllers/bukkits/posts_controller.rb", <<-RUBY
+ class Bukkits::PostsController < ActionController::Base
+ def new
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkits/posts/new.html.erb", <<-RUBY
+ <%= form_for(Bukkits::Post.new) do |f| %>
+ <%= f.text_field :title %>
+ <% end %>
+ RUBY
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ boot_rails
+
+ env = Rack::MockRequest.env_for("/bukkits/posts/new")
+ response = AppTemplate::Application.call(env)
+ assert response[2].body =~ /name="post\[title\]"/
+ end
end
end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
new file mode 100644
index 0000000000..36dd01198f
--- /dev/null
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -0,0 +1,174 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class ApplicationRoutingTest < Test::Unit::TestCase
+ require 'rack/test'
+ include Rack::Test::Methods
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
+ @plugin = engine "blog"
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do |map|
+ match "/engine_route" => "application_generating#engine_route"
+ match "/engine_route_in_view" => "application_generating#engine_route_in_view"
+ match "/url_for_engine_route" => "application_generating#url_for_engine_route"
+ match "/polymorphic_route" => "application_generating#polymorphic_route"
+ scope "/:user", :user => "anonymous" do
+ mount Blog::Engine => "/blog"
+ end
+ root :to => 'main#index'
+ end
+ RUBY
+
+ @plugin.write "app/models/blog/post.rb", <<-RUBY
+ module Blog
+ class Post
+ extend ActiveModel::Naming
+
+ def id
+ 44
+ end
+
+ def to_param
+ id.to_s
+ end
+
+ def new_record?
+ false
+ end
+ end
+ end
+ RUBY
+
+ @plugin.write "lib/blog.rb", <<-RUBY
+ module Blog
+ class Engine < ::Rails::Engine
+ namespace(Blog)
+ end
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Blog::Engine.routes.draw do
+ resources :posts
+ match '/generate_application_route', :to => 'posts#generate_application_route'
+ match '/application_route_in_view', :to => 'posts#application_route_in_view'
+ end
+ RUBY
+
+ @plugin.write "app/controllers/blog/posts_controller.rb", <<-RUBY
+ module Blog
+ class PostsController < ActionController::Base
+ def index
+ render :text => blog.post_path(1)
+ end
+
+ def generate_application_route
+ path = app.url_for(:controller => "/main",
+ :action => "index",
+ :only_path => true)
+ render :text => path
+ end
+
+ def application_route_in_view
+ render :inline => "<%= app.root_path %>"
+ end
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/application_generating_controller.rb", <<-RUBY
+ class ApplicationGeneratingController < ActionController::Base
+ def engine_route
+ render :text => blog.posts_path
+ end
+
+ def engine_route_in_view
+ render :inline => "<%= blog.posts_path %>"
+ end
+
+ def url_for_engine_route
+ render :text => blog.url_for(:controller => "blog/posts", :action => "index", :user => "john", :only_path => true)
+ end
+
+ def polymorphic_route
+ render :text => polymorphic_url([blog, Blog::Post.new])
+ end
+ end
+ RUBY
+
+ boot_rails
+ end
+
+ def app
+ @app ||= begin
+ require "#{app_path}/config/environment"
+ Rails.application
+ end
+ end
+
+ def reset_script_name!
+ Rails.application.routes.default_url_options = {}
+ end
+
+ def script_name(script_name)
+ Rails.application.routes.default_url_options = {:script_name => script_name}
+ end
+
+ test "routes generation in engine and application" do
+ # test generating engine's route from engine
+ get "/john/blog/posts"
+ assert_equal "/john/blog/posts/1", last_response.body
+
+ # test generating engine's route from engine with default_url_options
+ script_name "/foo"
+ get "/john/blog/posts", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/john/blog/posts/1", last_response.body
+ reset_script_name!
+
+ # test generating engine's route from application
+ get "/engine_route"
+ assert_equal "/anonymous/blog/posts", last_response.body
+
+ get "/engine_route_in_view"
+ assert_equal "/anonymous/blog/posts", last_response.body
+
+ get "/url_for_engine_route"
+ assert_equal "/john/blog/posts", last_response.body
+
+ # test generating engine's route from application with default_url_options
+ script_name "/foo"
+ get "/engine_route", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/anonymous/blog/posts", last_response.body
+
+ script_name "/foo"
+ get "/url_for_engine_route", {}, 'SCRIPT_NAME' => "/foo"
+ assert_equal "/foo/john/blog/posts", last_response.body
+ reset_script_name!
+
+ # test generating application's route from engine
+ get "/someone/blog/generate_application_route"
+ assert_equal "/", last_response.body
+
+ get "/somone/blog/application_route_in_view"
+ assert_equal "/", last_response.body
+
+ # test generating application's route from engine with default_url_options
+ script_name "/foo"
+ get "/someone/blog/generate_application_route", {}, 'SCRIPT_NAME' => '/foo'
+ assert_equal "/foo/", last_response.body
+ reset_script_name!
+
+ # test polymorphic routes
+ get "/polymorphic_route"
+ assert_equal "http://example.org/anonymous/blog/posts/44", last_response.body
+ end
+ end
+end
+
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 6715003d3d..406d5d764f 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -19,6 +19,22 @@ module RailtiesTest
assert !Rails::Railtie.respond_to?(:config)
end
+ test "Railtie provides railtie_name" do
+ begin
+ class ::Foo < Rails::Railtie ; end
+ assert_equal "foo", ::Foo.railtie_name
+ ensure
+ Object.send(:remove_const, :"Foo")
+ end
+ end
+
+ test "railtie_name can be set manualy" do
+ class Foo < Rails::Railtie
+ railtie_name "bar"
+ end
+ assert_equal "bar", Foo.railtie_name
+ end
+
test "cannot inherit from a railtie" do
class Foo < Rails::Railtie ; end
assert_raise RuntimeError do
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index ce7c55c11c..6aae17c237 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -10,6 +10,55 @@ module RailtiesTest
@app ||= Rails.application
end
+ def test_copying_migrations
+ @plugin.write "db/migrate/1_create_users.rb", <<-RUBY
+ class CreateUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ @plugin.write "db/migrate/2_add_last_name_to_users.rb", <<-RUBY
+ class AddLastNameToUsers < ActiveRecord::Migration
+ end
+ RUBY
+
+ app_file "db/migrate/1_create_sessions.rb", <<-RUBY
+ class CreateSessions < ActiveRecord::Migration
+ end
+ RUBY
+
+ yaffle = plugin "acts_as_yaffle", "::LEVEL = config.log_level" do |plugin|
+ plugin.write "lib/acts_as_yaffle.rb", "class ActsAsYaffle; end"
+ end
+
+ yaffle.write "db/migrate/1_create_yaffles.rb", <<-RUBY
+ class CreateYaffles < ActiveRecord::Migration
+ end
+ RUBY
+
+ add_to_config "ActiveRecord::Base.timestamped_migrations = false"
+
+ Dir.chdir(app_path) do
+ output = `rake db:copy_migrations FROM=bukkits`
+
+ assert File.exists?("#{app_path}/db/migrate/2_create_users.bukkits.rb")
+ assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb")
+ assert_match /2_create_users/, output
+ assert_match /3_add_last_name_to_users/, output
+ assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length
+
+ output = `rake db:copy_migrations`
+
+ assert File.exists?("#{app_path}/db/migrate/4_create_yaffles.acts_as_yaffle.rb")
+ assert_match /4_create_yaffles/, output
+
+ migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length
+ output = `rake db:copy_migrations`
+
+ assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length
+ assert_match /No migrations were copied/, output
+ end
+ end
+
def test_puts_its_lib_directory_on_load_path
boot_rails
require "another"