mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
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'
This commit is contained in:
@@ -361,7 +361,6 @@ module ActionMailer #:nodoc:
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
|
||||
def mailer_name
|
||||
@mailer_name ||= name.underscore
|
||||
end
|
||||
@@ -725,28 +724,6 @@ module ActionMailer #:nodoc:
|
||||
container.add_part(part)
|
||||
end
|
||||
|
||||
module DeprecatedUrlOptions
|
||||
def default_url_options
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def default_url_options=(val)
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def deprecated_url_options
|
||||
raise "You can no longer call ActionMailer::Base.default_url_options " \
|
||||
"directly. You need to set config.action_mailer.default_url_options. " \
|
||||
"If you are using ActionMailer standalone, you need to include the " \
|
||||
"routing url_helpers directly."
|
||||
end
|
||||
end
|
||||
|
||||
# This module will complain if the user tries to set default_url_options
|
||||
# directly instead of through the config object. In Action Mailer's Railtie,
|
||||
# we include the router's url_helpers, which will override this module.
|
||||
extend DeprecatedUrlOptions
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require "action_mailer"
|
||||
require "rails"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
|
||||
module ActionMailer
|
||||
class Railtie < Rails::Railtie
|
||||
@@ -18,9 +19,11 @@ module ActionMailer
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
include app.routes.url_helpers
|
||||
include AbstractController::UrlFor
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
include app.routes.mounted_helpers(:app)
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,4 +24,5 @@ module AbstractController
|
||||
autoload :Translation
|
||||
autoload :AssetPaths
|
||||
autoload :ViewPaths
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
module AbstractController
|
||||
module Railties
|
||||
module RoutesHelpers
|
||||
def self.with(routes)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
klass.send(:include, namespace._railtie.routes.url_helpers)
|
||||
else
|
||||
klass.send(:include, routes.url_helpers)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -52,6 +52,7 @@ module AbstractController
|
||||
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
include controller._routes.mounted_helpers
|
||||
end
|
||||
|
||||
# TODO: Fix RJS to not require this
|
||||
|
||||
28
actionpack/lib/abstract_controller/url_for.rb
Normal file
28
actionpack/lib/abstract_controller/url_for.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module AbstractController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def _routes
|
||||
nil
|
||||
end
|
||||
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
if _routes
|
||||
super - _routes.named_routes.helper_names
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -221,11 +221,6 @@ module ActionController
|
||||
# Rails 2.x compatibility
|
||||
include ActionController::Compatibility
|
||||
|
||||
def self.inherited(klass)
|
||||
super
|
||||
klass.helper :all if klass.superclass == ActionController::Base
|
||||
end
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,7 +52,11 @@ module ActionController
|
||||
class Metal < AbstractController::Base
|
||||
abstract!
|
||||
|
||||
attr_internal :env
|
||||
attr_internal_writer :env
|
||||
|
||||
def env
|
||||
@_env ||= {}
|
||||
end
|
||||
|
||||
# Returns the last part of the controller's name, underscored, without the ending
|
||||
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
||||
|
||||
@@ -101,8 +101,12 @@ module ActionController
|
||||
|
||||
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
||||
def all_application_helpers
|
||||
all_helpers_from_path(helpers_path)
|
||||
end
|
||||
|
||||
def all_helpers_from_path(path)
|
||||
helpers = []
|
||||
Array.wrap(helpers_path).each do |path|
|
||||
Array.wrap(path).each do |path|
|
||||
extract = /^#{Regexp.quote(path.to_s)}\/?(.*)_helper.rb$/
|
||||
helpers += Dir["#{path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
||||
end
|
||||
|
||||
@@ -2,27 +2,19 @@ module ActionController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
include AbstractController::UrlFor
|
||||
|
||||
def url_options
|
||||
super.reverse_merge(
|
||||
options = {}
|
||||
if _routes.equal?(env["action_dispatch.routes"])
|
||||
options[:script_name] = request.script_name.dup
|
||||
end
|
||||
|
||||
super.merge(options).reverse_merge(
|
||||
:host => request.host_with_port,
|
||||
:protocol => request.protocol,
|
||||
:_path_segments => request.symbolized_path_parameters
|
||||
).merge(:script_name => request.script_name)
|
||||
end
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
super - _routes.named_routes.helper_names
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,8 @@ require "action_dispatch/railtie"
|
||||
require "action_view/railtie"
|
||||
require "active_support/deprecation/proxy_wrappers"
|
||||
require "active_support/deprecation"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
require "action_controller/railties/paths"
|
||||
|
||||
module ActionController
|
||||
class Railtie < Rails::Railtie
|
||||
@@ -47,10 +49,11 @@ module ActionController
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
options.page_cache_directory ||= paths.public.to_a.first
|
||||
options.helpers_path ||= paths.app.helpers.to_a
|
||||
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
include app.routes.url_helpers
|
||||
include app.routes.mounted_helpers(:app)
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
extend ::ActionController::Railties::Paths.with(app)
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
@@ -63,4 +66,4 @@ module ActionController
|
||||
ActionController::Routing::Routes = proxy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
20
actionpack/lib/action_controller/railties/paths.rb
Normal file
20
actionpack/lib/action_controller/railties/paths.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
module ActionController
|
||||
module Railties
|
||||
module Paths
|
||||
def self.with(app)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
klass.helpers_path = namespace._railtie.config.paths.app.helpers.to_a
|
||||
else
|
||||
klass.helpers_path = app.config.helpers_paths
|
||||
end
|
||||
|
||||
klass.helper :all if klass.superclass == ActionController::Base
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,5 @@
|
||||
require "active_support/inflector/methods"
|
||||
require "active_support/dependencies"
|
||||
|
||||
module ActionDispatch
|
||||
class MiddlewareStack < Array
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
require 'rack/utils'
|
||||
|
||||
module ActionDispatch
|
||||
class FileHandler
|
||||
def initialize(at, root)
|
||||
@at, @root = at.chomp('/'), root.chomp('/')
|
||||
@compiled_at = Regexp.compile(/^#{Regexp.escape(at)}/) unless @at.blank?
|
||||
@compiled_root = Regexp.compile(/^#{Regexp.escape(root)}/)
|
||||
@file_server = ::Rack::File.new(root)
|
||||
end
|
||||
|
||||
def match?(path)
|
||||
path = path.dup
|
||||
if @compiled_at.blank? || path.sub!(@compiled_at, '')
|
||||
full_path = File.join(@root, ::Rack::Utils.unescape(path))
|
||||
paths = "#{full_path}#{ext}"
|
||||
|
||||
matches = Dir[paths]
|
||||
match = matches.detect { |m| File.file?(m) }
|
||||
if match
|
||||
match.sub!(@compiled_root, '')
|
||||
match
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@file_server.call(env)
|
||||
end
|
||||
|
||||
def ext
|
||||
@ext ||= begin
|
||||
ext = ::ActionController::Base.page_cache_extension
|
||||
"{,#{ext},/index#{ext}}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Static
|
||||
FILE_METHODS = %w(GET HEAD).freeze
|
||||
|
||||
def initialize(app, root)
|
||||
def initialize(app, roots)
|
||||
@app = app
|
||||
@file_server = ::Rack::File.new(root)
|
||||
@file_handlers = create_file_handlers(roots)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@@ -14,15 +49,10 @@ module ActionDispatch
|
||||
method = env['REQUEST_METHOD']
|
||||
|
||||
if FILE_METHODS.include?(method)
|
||||
if file_exist?(path)
|
||||
return @file_server.call(env)
|
||||
else
|
||||
cached_path = directory_exist?(path) ? "#{path}/index" : path
|
||||
cached_path += ::ActionController::Base.page_cache_extension
|
||||
|
||||
if file_exist?(cached_path)
|
||||
env['PATH_INFO'] = cached_path
|
||||
return @file_server.call(env)
|
||||
@file_handlers.each do |file_handler|
|
||||
if match = file_handler.match?(path)
|
||||
env["PATH_INFO"] = match
|
||||
return file_handler.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -31,14 +61,12 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
private
|
||||
def file_exist?(path)
|
||||
full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path))
|
||||
File.file?(full_path) && File.readable?(full_path)
|
||||
end
|
||||
def create_file_handlers(roots)
|
||||
roots = { '' => roots } unless roots.is_a?(Hash)
|
||||
|
||||
def directory_exist?(path)
|
||||
full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path))
|
||||
File.directory?(full_path) && File.readable?(full_path)
|
||||
roots.map do |at, root|
|
||||
FileHandler.new(at, root) if File.exist?(root)
|
||||
end.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -268,6 +268,7 @@ module ActionDispatch
|
||||
autoload :Mapper, 'action_dispatch/routing/mapper'
|
||||
autoload :Route, 'action_dispatch/routing/route'
|
||||
autoload :RouteSet, 'action_dispatch/routing/route_set'
|
||||
autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
|
||||
autoload :UrlFor, 'action_dispatch/routing/url_for'
|
||||
autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
|
||||
|
||||
|
||||
@@ -261,7 +261,11 @@ module ActionDispatch
|
||||
|
||||
raise "A rack application must be specified" unless path
|
||||
|
||||
options[:as] ||= app_name(app)
|
||||
|
||||
match(path, options.merge(:to => app, :anchor => false, :format => false))
|
||||
|
||||
define_generate_prefix(app, options[:as])
|
||||
self
|
||||
end
|
||||
|
||||
@@ -269,6 +273,40 @@ module ActionDispatch
|
||||
@set.default_url_options = options
|
||||
end
|
||||
alias_method :default_url_options, :default_url_options=
|
||||
|
||||
def with_default_scope(scope, &block)
|
||||
scope(scope) do
|
||||
instance_exec(&block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def app_name(app)
|
||||
return unless app.respond_to?(:routes)
|
||||
|
||||
if app.respond_to?(:railtie_name)
|
||||
app.railtie_name
|
||||
else
|
||||
class_name = app.class.is_a?(Class) ? app.name : app.class.name
|
||||
ActiveSupport::Inflector.underscore(class_name).gsub("/", "_")
|
||||
end
|
||||
end
|
||||
|
||||
def define_generate_prefix(app, name)
|
||||
return unless app.respond_to?(:routes)
|
||||
|
||||
_route = @set.named_routes.routes[name.to_sym]
|
||||
_routes = @set
|
||||
app.routes.define_mounted_helper(name)
|
||||
app.routes.class_eval do
|
||||
define_method :_generate_prefix do |options|
|
||||
prefix_options = options.slice(*_route.segment_keys)
|
||||
# we must actually delete prefix segment keys to avoid passing them to next url_for
|
||||
_route.segment_keys.each { |k| options.delete(k) }
|
||||
_routes.url_helpers.send("#{name}_path", prefix_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module HttpHelpers
|
||||
|
||||
@@ -42,6 +42,18 @@ module ActionDispatch
|
||||
#
|
||||
# edit_polymorphic_path(@post) # => "/posts/1/edit"
|
||||
# polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
|
||||
#
|
||||
# == Using with mounted engines
|
||||
#
|
||||
# If you use mounted engine, there is a possibility that you will need to use
|
||||
# polymorphic_url pointing at engine's routes. To do that, just pass proxy used
|
||||
# to reach engine's routes as a first argument:
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# polymorphic_url([blog, @post]) # it will call blog.post_path(@post)
|
||||
# form_for([blog, @post]) # => "/blog/posts/1
|
||||
#
|
||||
module PolymorphicRoutes
|
||||
# Constructs a call to a named RESTful route for the given record and returns the
|
||||
# resulting URL string. For example:
|
||||
@@ -78,6 +90,9 @@ module ActionDispatch
|
||||
def polymorphic_url(record_or_hash_or_array, options = {})
|
||||
if record_or_hash_or_array.kind_of?(Array)
|
||||
record_or_hash_or_array = record_or_hash_or_array.compact
|
||||
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
|
||||
proxy = record_or_hash_or_array.shift
|
||||
end
|
||||
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
|
||||
end
|
||||
|
||||
@@ -111,7 +126,14 @@ module ActionDispatch
|
||||
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
|
||||
end
|
||||
|
||||
send(named_route, *args)
|
||||
if proxy
|
||||
proxy.send(named_route, *args)
|
||||
else
|
||||
# we need to use url_for, because polymorphic_url can be used in context of other than
|
||||
# current routes (e.g. engine's routes). As named routes from engine are not included
|
||||
# calling engine's named route directly would fail.
|
||||
url_for _routes.url_helpers.__send__("hash_for_#{named_route}", *args)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the path component of a URL for the given record. It uses
|
||||
@@ -155,7 +177,7 @@ module ActionDispatch
|
||||
if parent.is_a?(Symbol) || parent.is_a?(String)
|
||||
parent
|
||||
else
|
||||
ActiveModel::Naming.plural(parent).singularize
|
||||
ActiveModel::Naming.route_key(parent).singularize
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -163,7 +185,7 @@ module ActionDispatch
|
||||
if record.is_a?(Symbol) || record.is_a?(String)
|
||||
route << record
|
||||
else
|
||||
route << ActiveModel::Naming.plural(record)
|
||||
route << ActiveModel::Naming.route_key(record)
|
||||
route = [route.join("_").singularize] if inflection == :singular
|
||||
route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
|
||||
end
|
||||
|
||||
@@ -158,10 +158,17 @@ module ActionDispatch
|
||||
|
||||
# We use module_eval to avoid leaks
|
||||
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
||||
def #{selector}(options = nil) # def hash_for_users_url(options = nil)
|
||||
options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
|
||||
end # end
|
||||
protected :#{selector} # protected :hash_for_users_url
|
||||
def #{selector}(*args)
|
||||
options = args.extract_options!
|
||||
|
||||
if args.any?
|
||||
options[:_positional_args] = args
|
||||
options[:_positional_keys] = #{route.segment_keys.inspect}
|
||||
end
|
||||
|
||||
options ? #{options.inspect}.merge(options) : #{options.inspect}
|
||||
end
|
||||
protected :#{selector}
|
||||
END_EVAL
|
||||
helpers << selector
|
||||
end
|
||||
@@ -185,21 +192,14 @@ module ActionDispatch
|
||||
|
||||
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
||||
def #{selector}(*args)
|
||||
options = #{hash_access_method}(args.extract_options!)
|
||||
|
||||
if args.any?
|
||||
options[:_positional_args] = args
|
||||
options[:_positional_keys] = #{route.segment_keys.inspect}
|
||||
end
|
||||
|
||||
url_for(options)
|
||||
url_for(#{hash_access_method}(*args))
|
||||
end
|
||||
END_EVAL
|
||||
helpers << selector
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :set, :routes, :named_routes
|
||||
attr_accessor :set, :routes, :named_routes, :default_scope
|
||||
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
||||
attr_accessor :default_url_options, :request_class, :valid_conditions
|
||||
|
||||
@@ -230,7 +230,11 @@ module ActionDispatch
|
||||
if block.arity == 1
|
||||
mapper.instance_exec(DeprecatedMapper.new(self), &block)
|
||||
else
|
||||
mapper.instance_exec(&block)
|
||||
if default_scope
|
||||
mapper.with_default_scope(default_scope, &block)
|
||||
else
|
||||
mapper.instance_exec(&block)
|
||||
end
|
||||
end
|
||||
|
||||
finalize! unless @disable_clear_and_finalize
|
||||
@@ -261,6 +265,31 @@ module ActionDispatch
|
||||
named_routes.install(destinations, regenerate_code)
|
||||
end
|
||||
|
||||
module MountedHelpers
|
||||
end
|
||||
|
||||
def mounted_helpers(name = nil)
|
||||
define_mounted_helper(name) if name
|
||||
MountedHelpers
|
||||
end
|
||||
|
||||
def define_mounted_helper(name)
|
||||
return if MountedHelpers.method_defined?(name)
|
||||
|
||||
routes = self
|
||||
MountedHelpers.class_eval do
|
||||
define_method "_#{name}" do
|
||||
RoutesProxy.new(routes, self._routes_context)
|
||||
end
|
||||
end
|
||||
|
||||
MountedHelpers.class_eval <<-RUBY
|
||||
def #{name}
|
||||
@#{name} ||= _#{name}
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
def url_helpers
|
||||
@url_helpers ||= begin
|
||||
routes = self
|
||||
@@ -283,7 +312,7 @@ module ActionDispatch
|
||||
singleton_class.send(:define_method, :_routes) { routes }
|
||||
end
|
||||
|
||||
define_method(:_routes) { routes }
|
||||
define_method(:_routes) { @_routes || routes }
|
||||
end
|
||||
|
||||
helpers
|
||||
@@ -303,10 +332,9 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
class Generator #:nodoc:
|
||||
attr_reader :options, :recall, :set, :script_name, :named_route
|
||||
attr_reader :options, :recall, :set, :named_route
|
||||
|
||||
def initialize(options, recall, set, extras = false)
|
||||
@script_name = options.delete(:script_name)
|
||||
@named_route = options.delete(:use_route)
|
||||
@options = options.dup
|
||||
@recall = recall.dup
|
||||
@@ -401,7 +429,7 @@ module ActionDispatch
|
||||
return [path, params.keys] if @extras
|
||||
|
||||
path << "?#{params.to_query}" if params.any?
|
||||
"#{script_name}#{path}"
|
||||
path
|
||||
rescue Rack::Mount::RoutingError
|
||||
raise_routing_error
|
||||
end
|
||||
@@ -453,7 +481,11 @@ module ActionDispatch
|
||||
Generator.new(options, recall, self, extras).generate
|
||||
end
|
||||
|
||||
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
|
||||
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :script_name]
|
||||
|
||||
def _generate_prefix(options = {})
|
||||
nil
|
||||
end
|
||||
|
||||
def url_for(options)
|
||||
finalize!
|
||||
@@ -464,7 +496,6 @@ module ActionDispatch
|
||||
rewritten_url = ""
|
||||
|
||||
path_segments = options.delete(:_path_segments)
|
||||
|
||||
unless options[:only_path]
|
||||
rewritten_url << (options[:protocol] || "http")
|
||||
rewritten_url << "://" unless rewritten_url.match("://")
|
||||
@@ -476,9 +507,12 @@ module ActionDispatch
|
||||
rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
|
||||
end
|
||||
|
||||
script_name = options.delete(:script_name)
|
||||
path = (script_name.blank? ? _generate_prefix(options) : script_name).to_s
|
||||
|
||||
path_options = options.except(*RESERVED_OPTIONS)
|
||||
path_options = yield(path_options) if block_given?
|
||||
path = generate(path_options, path_segments || {})
|
||||
path << generate(path_options, path_segments || {})
|
||||
|
||||
# ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
|
||||
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
|
||||
|
||||
35
actionpack/lib/action_dispatch/routing/routes_proxy.rb
Normal file
35
actionpack/lib/action_dispatch/routing/routes_proxy.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
class RoutesProxy #:nodoc:
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
|
||||
attr_accessor :scope, :routes
|
||||
alias :_routes :routes
|
||||
|
||||
def initialize(routes, scope)
|
||||
@routes, @scope = routes, scope
|
||||
end
|
||||
|
||||
def url_options
|
||||
scope.send(:_with_routes, routes) do
|
||||
scope.url_options
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
if routes.url_helpers.respond_to?(method)
|
||||
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{method}(*args)
|
||||
options = args.extract_options!
|
||||
args << url_options.merge((options || {}).symbolize_keys)
|
||||
routes.url_helpers.#{method}(*args)
|
||||
end
|
||||
RUBY
|
||||
send(method, *args)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -133,6 +133,18 @@ module ActionDispatch
|
||||
polymorphic_url(options)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def _with_routes(routes)
|
||||
old_routes, @_routes = @_routes, routes
|
||||
yield
|
||||
ensure
|
||||
@_routes = old_routes
|
||||
end
|
||||
|
||||
def _routes_context
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def initialize(env = {})
|
||||
env = Rails.application.env_defaults.merge(env) if defined?(Rails.application)
|
||||
env = Rails.application.env_config.merge(env) if defined?(Rails.application)
|
||||
super(DEFAULT_ENV.merge(env))
|
||||
|
||||
self.host = 'test.host'
|
||||
|
||||
@@ -727,6 +727,9 @@ module ActionView
|
||||
|
||||
source += ".#{ext}" if rewrite_extension?(source, dir, ext)
|
||||
source = "/#{dir}/#{source}" unless source[0] == ?/
|
||||
if controller.respond_to?(:env) && controller.env["action_dispatch.asset_path"]
|
||||
source = rewrite_asset_path(source, controller.env["action_dispatch.asset_path"])
|
||||
end
|
||||
source = rewrite_asset_path(source, config.asset_path)
|
||||
|
||||
has_request = controller.respond_to?(:request)
|
||||
|
||||
@@ -304,12 +304,12 @@ module ActionView
|
||||
object_name = record_or_name_or_array
|
||||
when Array
|
||||
object = record_or_name_or_array.last
|
||||
object_name = options[:as] || ActiveModel::Naming.singular(object)
|
||||
object_name = options[:as] || ActiveModel::Naming.param_key(object)
|
||||
apply_form_for_options!(record_or_name_or_array, options)
|
||||
args.unshift object
|
||||
else
|
||||
object = record_or_name_or_array
|
||||
object_name = options[:as] || ActiveModel::Naming.singular(object)
|
||||
object_name = options[:as] || ActiveModel::Naming.param_key(object)
|
||||
apply_form_for_options!([object], options)
|
||||
args.unshift object
|
||||
end
|
||||
@@ -539,7 +539,7 @@ module ActionView
|
||||
object_name = record
|
||||
else
|
||||
object = record
|
||||
object_name = ActiveModel::Naming.singular(object)
|
||||
object_name = ActiveModel::Naming.param_key(object)
|
||||
end
|
||||
|
||||
builder = options[:builder] || ActionView::Base.default_form_builder
|
||||
@@ -1168,11 +1168,11 @@ module ActionView
|
||||
end
|
||||
when Array
|
||||
object = record_or_name_or_array.last
|
||||
name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
|
||||
name = "#{object_name}#{index}[#{ActiveModel::Naming.param_key(object)}]"
|
||||
args.unshift(object)
|
||||
else
|
||||
object = record_or_name_or_array
|
||||
name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
|
||||
name = "#{object_name}#{index}[#{ActiveModel::Naming.param_key(object)}]"
|
||||
args.unshift(object)
|
||||
end
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ module ActionView
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
include TagHelper
|
||||
|
||||
def _routes_context
|
||||
controller
|
||||
end
|
||||
|
||||
# Need to map default url options to controller one.
|
||||
# def default_url_options(*args) #:nodoc:
|
||||
# controller.send(:default_url_options, *args)
|
||||
|
||||
@@ -25,6 +25,22 @@ class Series < ActiveRecord::Base
|
||||
set_table_name 'projects'
|
||||
end
|
||||
|
||||
module Blog
|
||||
class Post < ActiveRecord::Base
|
||||
set_table_name 'projects'
|
||||
end
|
||||
|
||||
class Blog < ActiveRecord::Base
|
||||
set_table_name 'projects'
|
||||
end
|
||||
|
||||
def self._railtie
|
||||
o = Object.new
|
||||
def o.railtie_name; "blog" end
|
||||
o
|
||||
end
|
||||
end
|
||||
|
||||
class PolymorphicRoutesTest < ActionController::TestCase
|
||||
include SharedTestRoutes.url_helpers
|
||||
self.default_url_options[:host] = 'example.com'
|
||||
@@ -37,6 +53,38 @@ class PolymorphicRoutesTest < ActionController::TestCase
|
||||
@tax = Tax.new
|
||||
@fax = Fax.new
|
||||
@series = Series.new
|
||||
@blog_post = Blog::Post.new
|
||||
@blog_blog = Blog::Blog.new
|
||||
end
|
||||
|
||||
def test_passing_routes_proxy
|
||||
with_namespaced_routes(:blog) do
|
||||
proxy = ActionDispatch::Routing::RoutesProxy.new(_routes, self)
|
||||
@blog_post.save
|
||||
assert_equal "http://example.com/posts/#{@blog_post.id}", polymorphic_url([proxy, @blog_post])
|
||||
end
|
||||
end
|
||||
|
||||
def test_namespaced_model
|
||||
with_namespaced_routes(:blog) do
|
||||
@blog_post.save
|
||||
assert_equal "http://example.com/posts/#{@blog_post.id}", polymorphic_url(@blog_post)
|
||||
end
|
||||
end
|
||||
|
||||
def test_namespaced_model_with_name_the_same_as_namespace
|
||||
with_namespaced_routes(:blog) do
|
||||
@blog_blog.save
|
||||
assert_equal "http://example.com/blogs/#{@blog_blog.id}", polymorphic_url(@blog_blog)
|
||||
end
|
||||
end
|
||||
|
||||
def test_namespaced_model_with_nested_resources
|
||||
with_namespaced_routes(:blog) do
|
||||
@blog_post.save
|
||||
@blog_blog.save
|
||||
assert_equal "http://example.com/blogs/#{@blog_blog.id}/posts/#{@blog_post.id}", polymorphic_url([@blog_blog, @blog_post])
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_record
|
||||
@@ -385,6 +433,22 @@ class PolymorphicRoutesTest < ActionController::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def with_namespaced_routes(name)
|
||||
with_routing do |set|
|
||||
set.draw do
|
||||
scope(:module => name) do
|
||||
resources :blogs do
|
||||
resources :posts
|
||||
end
|
||||
resources :posts
|
||||
end
|
||||
end
|
||||
|
||||
self.class.send(:include, @routes.url_helpers)
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def with_test_routes(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
|
||||
256
actionpack/test/dispatch/prefix_generation_test.rb
Normal file
256
actionpack/test/dispatch/prefix_generation_test.rb
Normal file
@@ -0,0 +1,256 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
module TestGenerationPrefix
|
||||
class WithMountedEngine < ActionDispatch::IntegrationTest
|
||||
require 'rack/test'
|
||||
include Rack::Test::Methods
|
||||
|
||||
class BlogEngine
|
||||
def self.routes
|
||||
@routes ||= begin
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
routes.draw do
|
||||
match "/posts/:id", :to => "inside_engine_generating#show", :as => :post
|
||||
match "/posts", :to => "inside_engine_generating#index", :as => :posts
|
||||
match "/url_to_application", :to => "inside_engine_generating#url_to_application"
|
||||
match "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
|
||||
match "/conflicting_url", :to => "inside_engine_generating#conflicting"
|
||||
end
|
||||
|
||||
routes
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
env['action_dispatch.routes'] = routes
|
||||
routes.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
class RailsApplication
|
||||
def self.routes
|
||||
@routes ||= begin
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
routes.draw do
|
||||
scope "/:omg", :omg => "awesome" do
|
||||
mount BlogEngine => "/blog", :as => "blog_engine"
|
||||
end
|
||||
match "/generate", :to => "outside_engine_generating#index"
|
||||
match "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine"
|
||||
match "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for"
|
||||
match "/conflicting_url", :to => "outside_engine_generating#conflicting"
|
||||
root :to => "outside_engine_generating#index"
|
||||
end
|
||||
|
||||
routes
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
env['action_dispatch.routes'] = routes
|
||||
routes.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
# force draw
|
||||
RailsApplication.routes
|
||||
|
||||
class Post
|
||||
extend ActiveModel::Naming
|
||||
|
||||
def to_param
|
||||
"1"
|
||||
end
|
||||
|
||||
def self.model_name
|
||||
klass = "Post"
|
||||
def klass.name; self end
|
||||
|
||||
ActiveModel::Name.new(klass)
|
||||
end
|
||||
end
|
||||
|
||||
class ::InsideEngineGeneratingController < ActionController::Base
|
||||
include BlogEngine.routes.url_helpers
|
||||
include RailsApplication.routes.mounted_helpers(:app)
|
||||
|
||||
def index
|
||||
render :text => posts_path
|
||||
end
|
||||
|
||||
def show
|
||||
render :text => post_path(:id => params[:id])
|
||||
end
|
||||
|
||||
def url_to_application
|
||||
path = app.url_for( :controller => "outside_engine_generating",
|
||||
:action => "index",
|
||||
:only_path => true)
|
||||
render :text => path
|
||||
end
|
||||
|
||||
def polymorphic_path_for_engine
|
||||
render :text => polymorphic_path(Post.new)
|
||||
end
|
||||
|
||||
def conflicting
|
||||
render :text => "engine"
|
||||
end
|
||||
end
|
||||
|
||||
class ::OutsideEngineGeneratingController < ActionController::Base
|
||||
include BlogEngine.routes.mounted_helpers
|
||||
|
||||
def index
|
||||
render :text => blog_engine.post_path(:id => 1)
|
||||
end
|
||||
|
||||
def polymorphic_path_for_engine
|
||||
render :text => blog_engine.polymorphic_path(Post.new)
|
||||
end
|
||||
|
||||
def polymorphic_with_url_for
|
||||
render :text => blog_engine.url_for(Post.new)
|
||||
end
|
||||
|
||||
def conflicting
|
||||
render :text => "application"
|
||||
end
|
||||
end
|
||||
|
||||
class EngineObject
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
include BlogEngine.routes.url_helpers
|
||||
end
|
||||
|
||||
class AppObject
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
include RailsApplication.routes.url_helpers
|
||||
end
|
||||
|
||||
def app
|
||||
RailsApplication
|
||||
end
|
||||
|
||||
def engine_object
|
||||
@engine_object ||= EngineObject.new
|
||||
end
|
||||
|
||||
def app_object
|
||||
@app_object ||= AppObject.new
|
||||
end
|
||||
|
||||
def setup
|
||||
RailsApplication.routes.default_url_options = {}
|
||||
end
|
||||
|
||||
# Inside Engine
|
||||
test "[ENGINE] generating engine's url use SCRIPT_NAME from request" do
|
||||
get "/pure-awesomeness/blog/posts/1"
|
||||
assert_equal "/pure-awesomeness/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[ENGINE] generating application's url never uses SCRIPT_NAME from request" do
|
||||
get "/pure-awesomeness/blog/url_to_application"
|
||||
assert_equal "/generate", last_response.body
|
||||
end
|
||||
|
||||
test "[ENGINE] generating application's url includes default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
get "/pure-awesomeness/blog/url_to_application"
|
||||
assert_equal "/something/generate", last_response.body
|
||||
end
|
||||
|
||||
test "[ENGINE] generating application's url should give higher priority to default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
get "/pure-awesomeness/blog/url_to_application", {}, 'SCRIPT_NAME' => '/foo'
|
||||
assert_equal "/something/generate", last_response.body
|
||||
end
|
||||
|
||||
test "[ENGINE] generating engine's url with polymorphic path" do
|
||||
get "/pure-awesomeness/blog/polymorphic_path_for_engine"
|
||||
assert_equal "/pure-awesomeness/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[ENGINE] url_helpers from engine have higher priotity than application's url_helpers" do
|
||||
get "/awesome/blog/conflicting_url"
|
||||
assert_equal "engine", last_response.body
|
||||
end
|
||||
|
||||
# Inside Application
|
||||
test "[APP] generating engine's route includes prefix" do
|
||||
get "/generate"
|
||||
assert_equal "/awesome/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[APP] generating engine's route includes default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
get "/generate"
|
||||
assert_equal "/something/awesome/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[APP] generating engine's route should give higher priority to default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
get "/generate", {}, 'SCRIPT_NAME' => "/foo"
|
||||
assert_equal "/something/awesome/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[APP] generating engine's url with polymorphic path" do
|
||||
get "/polymorphic_path_for_engine"
|
||||
assert_equal "/awesome/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
test "[APP] generating engine's url with url_for(@post)" do
|
||||
get "/polymorphic_with_url_for"
|
||||
assert_equal "http://example.org/awesome/blog/posts/1", last_response.body
|
||||
end
|
||||
|
||||
# Inside any Object
|
||||
test "[OBJECT] generating engine's route includes prefix" do
|
||||
assert_equal "/awesome/blog/posts/1", engine_object.post_path(:id => 1)
|
||||
end
|
||||
|
||||
test "[OBJECT] generating engine's route includes dynamic prefix" do
|
||||
assert_equal "/pure-awesomeness/blog/posts/3", engine_object.post_path(:id => 3, :omg => "pure-awesomeness")
|
||||
end
|
||||
|
||||
test "[OBJECT] generating engine's route includes default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
assert_equal "/something/pure-awesomeness/blog/posts/3", engine_object.post_path(:id => 3, :omg => "pure-awesomeness")
|
||||
end
|
||||
|
||||
test "[OBJECT] generating application's route" do
|
||||
assert_equal "/", app_object.root_path
|
||||
end
|
||||
|
||||
test "[OBJECT] generating application's route includes default_url_options[:script_name]" do
|
||||
RailsApplication.routes.default_url_options = {:script_name => "/something"}
|
||||
assert_equal "/something/", app_object.root_path
|
||||
end
|
||||
|
||||
test "[OBJECT] generating engine's route with url_for" do
|
||||
path = engine_object.url_for(:controller => "inside_engine_generating",
|
||||
:action => "show",
|
||||
:only_path => true,
|
||||
:omg => "omg",
|
||||
:id => 1)
|
||||
assert_equal "/omg/blog/posts/1", path
|
||||
end
|
||||
|
||||
test "[OBJECT] generating engine's route with named helpers" do
|
||||
path = engine_object.posts_path
|
||||
assert_equal "/awesome/blog/posts", path
|
||||
|
||||
path = engine_object.posts_url(:host => "example.com")
|
||||
assert_equal "http://example.com/awesome/blog/posts", path
|
||||
end
|
||||
|
||||
test "[OBJECT] generating engine's route with polymorphic_url" do
|
||||
path = engine_object.polymorphic_path(Post.new)
|
||||
assert_equal "/awesome/blog/posts/1", path
|
||||
|
||||
path = engine_object.polymorphic_url(Post.new, :host => "www.example.com")
|
||||
assert_equal "http://www.example.com/awesome/blog/posts/1", path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2151,3 +2151,32 @@ private
|
||||
%(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class TestDefaultScope < ActionController::IntegrationTest
|
||||
module ::Blog
|
||||
class PostsController < ActionController::Base
|
||||
def index
|
||||
render :text => "blog/posts#index"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new
|
||||
DefaultScopeRoutes.default_scope = {:module => :blog}
|
||||
DefaultScopeRoutes.draw do
|
||||
resources :posts
|
||||
end
|
||||
|
||||
def app
|
||||
DefaultScopeRoutes
|
||||
end
|
||||
|
||||
include DefaultScopeRoutes.url_helpers
|
||||
|
||||
def test_default_scope
|
||||
get '/posts'
|
||||
assert_equal "blog/posts#index", @response.body
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class StaticTest < ActiveSupport::TestCase
|
||||
DummyApp = lambda { |env|
|
||||
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
|
||||
}
|
||||
App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public")
|
||||
|
||||
test "serves dynamic content" do
|
||||
module StaticTests
|
||||
def test_serves_dynamic_content
|
||||
assert_equal "Hello, World!", get("/nofile")
|
||||
end
|
||||
|
||||
test "serves static index at root" do
|
||||
def test_serves_static_index_at_root
|
||||
assert_equal "/index.html", get("/index.html")
|
||||
assert_equal "/index.html", get("/index")
|
||||
assert_equal "/index.html", get("/")
|
||||
end
|
||||
|
||||
test "serves static file in directory" do
|
||||
def test_serves_static_file_in_directory
|
||||
assert_equal "/foo/bar.html", get("/foo/bar.html")
|
||||
assert_equal "/foo/bar.html", get("/foo/bar/")
|
||||
assert_equal "/foo/bar.html", get("/foo/bar")
|
||||
end
|
||||
|
||||
test "serves static index file in directory" do
|
||||
def test_serves_static_index_file_in_directory
|
||||
assert_equal "/foo/index.html", get("/foo/index.html")
|
||||
assert_equal "/foo/index.html", get("/foo/")
|
||||
assert_equal "/foo/index.html", get("/foo")
|
||||
@@ -30,6 +25,50 @@ class StaticTest < ActiveSupport::TestCase
|
||||
|
||||
private
|
||||
def get(path)
|
||||
Rack::MockRequest.new(App).request("GET", path).body
|
||||
Rack::MockRequest.new(@app).request("GET", path).body
|
||||
end
|
||||
end
|
||||
|
||||
class StaticTest < ActiveSupport::TestCase
|
||||
DummyApp = lambda { |env|
|
||||
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
|
||||
}
|
||||
App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public")
|
||||
|
||||
def setup
|
||||
@app = App
|
||||
end
|
||||
|
||||
include StaticTests
|
||||
end
|
||||
|
||||
class MultipleDirectorisStaticTest < ActiveSupport::TestCase
|
||||
DummyApp = lambda { |env|
|
||||
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
|
||||
}
|
||||
App = ActionDispatch::Static.new(DummyApp,
|
||||
{ "/" => "#{FIXTURE_LOAD_PATH}/public",
|
||||
"/blog" => "#{FIXTURE_LOAD_PATH}/blog_public",
|
||||
"/foo" => "#{FIXTURE_LOAD_PATH}/non_existing_dir"
|
||||
})
|
||||
|
||||
def setup
|
||||
@app = App
|
||||
end
|
||||
|
||||
include StaticTests
|
||||
|
||||
test "serves files from other mounted directories" do
|
||||
assert_equal "/blog/index.html", get("/blog/index.html")
|
||||
assert_equal "/blog/index.html", get("/blog/index")
|
||||
assert_equal "/blog/index.html", get("/blog/")
|
||||
|
||||
assert_equal "/blog/blog.html", get("/blog/blog/")
|
||||
assert_equal "/blog/blog.html", get("/blog/blog.html")
|
||||
assert_equal "/blog/blog.html", get("/blog/blog")
|
||||
|
||||
assert_equal "/blog/subdir/index.html", get("/blog/subdir/index.html")
|
||||
assert_equal "/blog/subdir/index.html", get("/blog/subdir/")
|
||||
assert_equal "/blog/subdir/index.html", get("/blog/subdir")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@ module TestUrlGeneration
|
||||
end
|
||||
|
||||
test "the request's SCRIPT_NAME takes precedence over the routes'" do
|
||||
get "/foo", {}, 'SCRIPT_NAME' => "/new"
|
||||
get "/foo", {}, 'SCRIPT_NAME' => "/new", 'action_dispatch.routes' => Routes
|
||||
assert_equal "/new/foo", response.body
|
||||
end
|
||||
|
||||
@@ -41,3 +41,4 @@ module TestUrlGeneration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
1
actionpack/test/fixtures/blog_public/.gitignore
vendored
Normal file
1
actionpack/test/fixtures/blog_public/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
absolute/*
|
||||
1
actionpack/test/fixtures/blog_public/blog.html
vendored
Normal file
1
actionpack/test/fixtures/blog_public/blog.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/blog/blog.html
|
||||
1
actionpack/test/fixtures/blog_public/index.html
vendored
Normal file
1
actionpack/test/fixtures/blog_public/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/blog/index.html
|
||||
1
actionpack/test/fixtures/blog_public/subdir/index.html
vendored
Normal file
1
actionpack/test/fixtures/blog_public/subdir/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/blog/subdir/index.html
|
||||
@@ -83,7 +83,7 @@ class Comment
|
||||
def to_key; id ? [id] : nil end
|
||||
def save; @id = 1; @post_id = 1 end
|
||||
def persisted?; @id.present? end
|
||||
def to_param; @id; end
|
||||
def to_param; @id.to_s; end
|
||||
def name
|
||||
@id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}"
|
||||
end
|
||||
@@ -149,3 +149,18 @@ class Author < Comment
|
||||
attr_accessor :post
|
||||
def post_attributes=(attributes); end
|
||||
end
|
||||
|
||||
module Blog
|
||||
def self._railtie
|
||||
self
|
||||
end
|
||||
|
||||
class Post < Struct.new(:title, :id)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
def persisted?
|
||||
id.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -387,6 +387,15 @@ class AssetTagHelperTest < ActionView::TestCase
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_env_asset_path
|
||||
@controller.config.asset_path = "/assets%s"
|
||||
def @controller.env; @_env ||= {} end
|
||||
@controller.env["action_dispatch.asset_path"] = "/omg%s"
|
||||
|
||||
expected_path = "/assets/omg/images/rails.png"
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_proc_asset_id
|
||||
@controller.config.asset_path = Proc.new do |asset_path|
|
||||
"/assets.v12345#{asset_path}"
|
||||
@@ -396,6 +405,20 @@ class AssetTagHelperTest < ActionView::TestCase
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_env_proc_asset_path
|
||||
@controller.config.asset_path = Proc.new do |asset_path|
|
||||
"/assets.v12345#{asset_path}"
|
||||
end
|
||||
|
||||
def @controller.env; @_env ||= {} end
|
||||
@controller.env["action_dispatch.asset_path"] = Proc.new do |asset_path|
|
||||
"/omg#{asset_path}"
|
||||
end
|
||||
|
||||
expected_path = "/assets.v12345/omg/images/rails.png"
|
||||
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
|
||||
end
|
||||
|
||||
def test_image_tag_interpreting_email_cid_correctly
|
||||
# An inline image has no need for an alt tag to be automatically generated from the cid:
|
||||
assert_equal '<img src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid")
|
||||
|
||||
@@ -75,15 +75,39 @@ class FormHelperTest < ActionView::TestCase
|
||||
@post.body = "Back to the hill and over it again!"
|
||||
@post.secret = 1
|
||||
@post.written_on = Date.new(2004, 6, 15)
|
||||
|
||||
@blog_post = Blog::Post.new("And his name will be forty and four.", 44)
|
||||
end
|
||||
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
Routes.draw do
|
||||
resources :posts do
|
||||
resources :comments
|
||||
end
|
||||
|
||||
namespace :admin do
|
||||
resources :posts do
|
||||
resources :comments
|
||||
end
|
||||
end
|
||||
|
||||
match "/foo", :to => "controller#action"
|
||||
root :to => "main#index"
|
||||
end
|
||||
|
||||
def _routes
|
||||
Routes
|
||||
end
|
||||
|
||||
include Routes.url_helpers
|
||||
|
||||
def url_for(object)
|
||||
@url_for_options = object
|
||||
if object.is_a?(Hash)
|
||||
"http://www.example.com"
|
||||
else
|
||||
super
|
||||
if object.is_a?(Hash) && object[:use_route].blank? && object[:controller].blank?
|
||||
object.merge!(:controller => "main", :action => "index")
|
||||
end
|
||||
object
|
||||
super
|
||||
end
|
||||
|
||||
def test_label
|
||||
@@ -628,7 +652,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form accept-charset='UTF-8' action='http://www.example.com' id='create-post' method='post'>" +
|
||||
"<form accept-charset='UTF-8' action='/' id='create-post' method='post'>" +
|
||||
snowman +
|
||||
"<label for='post_title'>The Title</label>" +
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
@@ -653,6 +677,21 @@ class FormHelperTest < ActionView::TestCase
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_isolated_namespaced_model
|
||||
form_for(@blog_post) do |f|
|
||||
concat f.text_field :title
|
||||
concat f.submit('Edit post')
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form accept-charset='UTF-8' action='/posts/44' method='post'>" +
|
||||
snowman +
|
||||
"<label for='post_title'>The Title</label>" +
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='And his name will be forty and four.' />" +
|
||||
"<input name='commit' id='post_submit' type='submit' value='Edit post' />" +
|
||||
"</form>"
|
||||
end
|
||||
|
||||
def test_form_for_with_symbol_object_name
|
||||
form_for(@post, :as => "other_name", :html => { :id => 'create-post' }) do |f|
|
||||
concat f.label(:title, :class => 'post_title')
|
||||
@@ -683,7 +722,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
expected = whole_form("http://www.example.com", "create-post", nil, "put") do
|
||||
expected = whole_form("/", "create-post", nil, "put") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
@@ -702,7 +741,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
expected = whole_form("http://www.example.com", "create-post", nil, :method => "put", :remote => true) do
|
||||
expected = whole_form("/", "create-post", nil, :method => "put", :remote => true) do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
@@ -721,7 +760,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
expected = whole_form("http://www.example.com", nil, nil, :remote => true) do
|
||||
expected = whole_form("/", nil, nil, :remote => true) do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
@@ -738,7 +777,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
concat f.check_box(:secret)
|
||||
end
|
||||
|
||||
expected = whole_form("http://www.example.com", "create-post") do
|
||||
expected = whole_form("/", "create-post") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
@@ -1478,7 +1517,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form accept-charset='UTF-8' action='http://www.example.com' id='create-post' method='post'>" +
|
||||
"<form accept-charset='UTF-8' action='/' id='create-post' method='post'>" +
|
||||
snowman +
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
@@ -1502,7 +1541,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
expected =
|
||||
whole_form("http://www.example.com", "create-post") do
|
||||
whole_form("/", "create-post") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />"
|
||||
@@ -1546,7 +1585,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
txt << %{</div>}
|
||||
end
|
||||
|
||||
def form_text(action = "http://www.example.com", id = nil, html_class = nil, remote = nil)
|
||||
def form_text(action = "/", id = nil, html_class = nil, remote = nil)
|
||||
txt = %{<form accept-charset="UTF-8" action="#{action}"}
|
||||
txt << %{ data-remote="true"} if remote
|
||||
txt << %{ class="#{html_class}"} if html_class
|
||||
@@ -1554,7 +1593,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
txt << %{ method="post">}
|
||||
end
|
||||
|
||||
def whole_form(action = "http://www.example.com", id = nil, html_class = nil, options = nil)
|
||||
def whole_form(action = "/", id = nil, html_class = nil, options = nil)
|
||||
contents = block_given? ? yield : ""
|
||||
|
||||
if options.is_a?(Hash)
|
||||
@@ -1655,7 +1694,7 @@ class FormHelperTest < ActionView::TestCase
|
||||
assert_deprecated do
|
||||
form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
|
||||
end
|
||||
expected = whole_form("http://www.example.com", "some_form", "some_class")
|
||||
expected = whole_form("/", "some_form", "some_class")
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
@@ -1710,14 +1749,14 @@ class FormHelperTest < ActionView::TestCase
|
||||
@comment.save
|
||||
form_for([@post, @comment]) {}
|
||||
|
||||
expected = whole_form(comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_new_object_in_list
|
||||
form_for([@post, @comment]) {}
|
||||
|
||||
expected = whole_form(comments_path(@post), "new_comment", "new_comment")
|
||||
expected = whole_form(post_comments_path(@post), "new_comment", "new_comment")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
@@ -1725,14 +1764,14 @@ class FormHelperTest < ActionView::TestCase
|
||||
@comment.save
|
||||
form_for([:admin, @post, @comment]) {}
|
||||
|
||||
expected = whole_form(admin_comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_new_object_and_namespace_in_list
|
||||
form_for([:admin, @post, @comment]) {}
|
||||
|
||||
expected = whole_form(admin_comments_path(@post), "new_comment", "new_comment")
|
||||
expected = whole_form(admin_post_comments_path(@post), "new_comment", "new_comment")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
@@ -1749,38 +1788,6 @@ class FormHelperTest < ActionView::TestCase
|
||||
end
|
||||
|
||||
protected
|
||||
def comments_path(post)
|
||||
"/posts/#{post.id}/comments"
|
||||
end
|
||||
alias_method :post_comments_path, :comments_path
|
||||
|
||||
def comment_path(post, comment)
|
||||
"/posts/#{post.id}/comments/#{comment.id}"
|
||||
end
|
||||
alias_method :post_comment_path, :comment_path
|
||||
|
||||
def admin_comments_path(post)
|
||||
"/admin/posts/#{post.id}/comments"
|
||||
end
|
||||
alias_method :admin_post_comments_path, :admin_comments_path
|
||||
|
||||
def admin_comment_path(post, comment)
|
||||
"/admin/posts/#{post.id}/comments/#{comment.id}"
|
||||
end
|
||||
alias_method :admin_post_comment_path, :admin_comment_path
|
||||
|
||||
def posts_path
|
||||
"/posts"
|
||||
end
|
||||
|
||||
def post_path(post, options = {})
|
||||
if options[:format]
|
||||
"/posts/#{post.id}.#{options[:format]}"
|
||||
else
|
||||
"/posts/#{post.id}"
|
||||
end
|
||||
end
|
||||
|
||||
def protect_against_forgery?
|
||||
false
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ class PeopleHelperTest < ActionView::TestCase
|
||||
with_test_route_set do
|
||||
person = mock(:name => "David")
|
||||
person.class.extend ActiveModel::Naming
|
||||
expects(:mocha_mock_path).with(person).returns("/people/1")
|
||||
_routes.url_helpers.expects(:hash_for_mocha_mock_path).with(person).returns("/people/1")
|
||||
assert_equal '<a href="/people/1">David</a>', link_to_person(person)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,18 +2,22 @@ require 'active_support/inflector'
|
||||
|
||||
module ActiveModel
|
||||
class Name < String
|
||||
attr_reader :singular, :plural, :element, :collection, :partial_path
|
||||
attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key
|
||||
alias_method :cache_key, :collection
|
||||
|
||||
def initialize(klass)
|
||||
def initialize(klass, namespace = nil)
|
||||
super(klass.name)
|
||||
@unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace
|
||||
|
||||
@klass = klass
|
||||
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
|
||||
@singular = _singularize(self).freeze
|
||||
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
|
||||
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze
|
||||
@human = ActiveSupport::Inflector.humanize(@element).freeze
|
||||
@collection = ActiveSupport::Inflector.tableize(self).freeze
|
||||
@partial_path = "#{@collection}/#{@element}".freeze
|
||||
@param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze
|
||||
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze
|
||||
end
|
||||
|
||||
# Transform the model name into a more humane format, using I18n. By default,
|
||||
@@ -36,6 +40,11 @@ module ActiveModel
|
||||
options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults
|
||||
I18n.translate(defaults.shift, options)
|
||||
end
|
||||
|
||||
private
|
||||
def _singularize(str)
|
||||
ActiveSupport::Inflector.underscore(str).tr('/', '_')
|
||||
end
|
||||
end
|
||||
|
||||
# == Active Model Naming
|
||||
@@ -58,7 +67,8 @@ module ActiveModel
|
||||
# Returns an ActiveModel::Name object for module. It can be
|
||||
# used to retrieve all kinds of naming-related information.
|
||||
def model_name
|
||||
@_model_name ||= ActiveModel::Name.new(self)
|
||||
namespace = self.parents.detect { |n| n.respond_to?(:_railtie) }
|
||||
@_model_name ||= ActiveModel::Name.new(self, namespace)
|
||||
end
|
||||
|
||||
# Returns the plural class name of a record or class. Examples:
|
||||
@@ -85,6 +95,30 @@ module ActiveModel
|
||||
plural(record_or_class) == singular(record_or_class)
|
||||
end
|
||||
|
||||
# Returns string to use while generating route names. It differs for
|
||||
# namespaced models regarding whether it's inside isolated engine.
|
||||
#
|
||||
# For isolated engine:
|
||||
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
|
||||
#
|
||||
# For shared engine:
|
||||
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
|
||||
def self.route_key(record_or_class)
|
||||
model_name_from_record_or_class(record_or_class).route_key
|
||||
end
|
||||
|
||||
# Returns string to use for params names. It differs for
|
||||
# namespaced models regarding whether it's inside isolated engine.
|
||||
#
|
||||
# For isolated engine:
|
||||
# ActiveModel::Naming.route_key(Blog::Post) #=> post
|
||||
#
|
||||
# For shared engine:
|
||||
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_post
|
||||
def self.param_key(record_or_class)
|
||||
model_name_from_record_or_class(record_or_class).param_key
|
||||
end
|
||||
|
||||
private
|
||||
def self.model_name_from_record_or_class(record_or_class)
|
||||
(record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'cases/helper'
|
||||
require 'models/contact'
|
||||
require 'models/sheep'
|
||||
require 'models/track_back'
|
||||
require 'models/blog_post'
|
||||
|
||||
class NamingTest < ActiveModel::TestCase
|
||||
def setup
|
||||
@@ -29,6 +30,86 @@ class NamingTest < ActiveModel::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class NamingWithNamespacedModelInIsolatedNamespaceTest < ActiveModel::TestCase
|
||||
def setup
|
||||
@model_name = ActiveModel::Name.new(Blog::Post, Blog)
|
||||
end
|
||||
|
||||
def test_singular
|
||||
assert_equal 'blog_post', @model_name.singular
|
||||
end
|
||||
|
||||
def test_plural
|
||||
assert_equal 'blog_posts', @model_name.plural
|
||||
end
|
||||
|
||||
def test_element
|
||||
assert_equal 'post', @model_name.element
|
||||
end
|
||||
|
||||
def test_collection
|
||||
assert_equal 'blog/posts', @model_name.collection
|
||||
end
|
||||
|
||||
def test_partial_path
|
||||
assert_equal 'blog/posts/post', @model_name.partial_path
|
||||
end
|
||||
|
||||
def test_human
|
||||
assert_equal 'Post', @model_name.human
|
||||
end
|
||||
|
||||
def test_route_key
|
||||
assert_equal 'posts', @model_name.route_key
|
||||
end
|
||||
|
||||
def test_param_key
|
||||
assert_equal 'post', @model_name.param_key
|
||||
end
|
||||
|
||||
def test_recognizing_namespace
|
||||
assert_equal 'Post', Blog::Post.model_name.instance_variable_get("@unnamespaced")
|
||||
end
|
||||
end
|
||||
|
||||
class NamingWithNamespacedModelInSharedNamespaceTest < ActiveModel::TestCase
|
||||
def setup
|
||||
@model_name = ActiveModel::Name.new(Blog::Post)
|
||||
end
|
||||
|
||||
def test_singular
|
||||
assert_equal 'blog_post', @model_name.singular
|
||||
end
|
||||
|
||||
def test_plural
|
||||
assert_equal 'blog_posts', @model_name.plural
|
||||
end
|
||||
|
||||
def test_element
|
||||
assert_equal 'post', @model_name.element
|
||||
end
|
||||
|
||||
def test_collection
|
||||
assert_equal 'blog/posts', @model_name.collection
|
||||
end
|
||||
|
||||
def test_partial_path
|
||||
assert_equal 'blog/posts/post', @model_name.partial_path
|
||||
end
|
||||
|
||||
def test_human
|
||||
assert_equal 'Post', @model_name.human
|
||||
end
|
||||
|
||||
def test_route_key
|
||||
assert_equal 'blog_posts', @model_name.route_key
|
||||
end
|
||||
|
||||
def test_param_key
|
||||
assert_equal 'blog_post', @model_name.param_key
|
||||
end
|
||||
end
|
||||
|
||||
class NamingHelpersTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@klass = Contact
|
||||
@@ -36,6 +117,8 @@ class NamingHelpersTest < Test::Unit::TestCase
|
||||
@singular = 'contact'
|
||||
@plural = 'contacts'
|
||||
@uncountable = Sheep
|
||||
@route_key = 'contacts'
|
||||
@param_key = 'contact'
|
||||
end
|
||||
|
||||
def test_singular
|
||||
@@ -54,6 +137,22 @@ class NamingHelpersTest < Test::Unit::TestCase
|
||||
assert_equal @plural, plural(@klass)
|
||||
end
|
||||
|
||||
def test_route_key
|
||||
assert_equal @route_key, route_key(@record)
|
||||
end
|
||||
|
||||
def test_route_key_for_class
|
||||
assert_equal @route_key, route_key(@klass)
|
||||
end
|
||||
|
||||
def test_param_key
|
||||
assert_equal @param_key, param_key(@record)
|
||||
end
|
||||
|
||||
def test_param_key_for_class
|
||||
assert_equal @param_key, param_key(@klass)
|
||||
end
|
||||
|
||||
def test_uncountable
|
||||
assert uncountable?(@uncountable), "Expected 'sheep' to be uncoutable"
|
||||
assert !uncountable?(@klass), "Expected 'contact' to be countable"
|
||||
|
||||
13
activemodel/test/models/blog_post.rb
Normal file
13
activemodel/test/models/blog_post.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module Blog
|
||||
def self._railtie
|
||||
Object.new
|
||||
end
|
||||
|
||||
def self.table_name_prefix
|
||||
"blog_"
|
||||
end
|
||||
|
||||
class Post
|
||||
extend ActiveModel::Naming
|
||||
end
|
||||
end
|
||||
@@ -383,6 +383,37 @@ module ActiveRecord
|
||||
connection.send(method, *arguments, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def copy(destination, sources)
|
||||
copied = []
|
||||
|
||||
sources.each do |scope, path|
|
||||
destination_migrations = ActiveRecord::Migrator.migrations(destination)
|
||||
source_migrations = ActiveRecord::Migrator.migrations(path)
|
||||
last = destination_migrations.last
|
||||
|
||||
source_migrations.each do |migration|
|
||||
next if destination_migrations.any? { |m| m.name == migration.name && m.scope == scope.to_s }
|
||||
|
||||
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
|
||||
last = migration
|
||||
|
||||
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
|
||||
FileUtils.cp(migration.filename, new_path)
|
||||
copied << new_path
|
||||
end
|
||||
end
|
||||
|
||||
copied
|
||||
end
|
||||
|
||||
def next_migration_number(number)
|
||||
if ActiveRecord::Base.timestamped_migrations
|
||||
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
|
||||
else
|
||||
"%.3d" % number
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -390,7 +421,7 @@ module ActiveRecord
|
||||
# until they are needed
|
||||
class MigrationProxy
|
||||
|
||||
attr_accessor :name, :version, :filename
|
||||
attr_accessor :name, :version, :filename, :scope
|
||||
|
||||
delegate :migrate, :announce, :write, :to=>:migration
|
||||
|
||||
@@ -409,6 +440,8 @@ module ActiveRecord
|
||||
|
||||
class Migrator#:nodoc:
|
||||
class << self
|
||||
attr_writer :migrations_path
|
||||
|
||||
def migrate(migrations_path, target_version = nil)
|
||||
case
|
||||
when target_version.nil?
|
||||
@@ -441,10 +474,6 @@ module ActiveRecord
|
||||
self.new(direction, migrations_path, target_version).run
|
||||
end
|
||||
|
||||
def migrations_path
|
||||
'db/migrate'
|
||||
end
|
||||
|
||||
def schema_migrations_table_name
|
||||
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
|
||||
end
|
||||
@@ -468,6 +497,38 @@ module ActiveRecord
|
||||
name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
|
||||
end
|
||||
|
||||
def migrations_path
|
||||
@migrations_path ||= 'db/migrate'
|
||||
end
|
||||
|
||||
def migrations(path)
|
||||
files = Dir["#{path}/[0-9]*_*.rb"]
|
||||
|
||||
migrations = files.inject([]) do |klasses, file|
|
||||
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
|
||||
|
||||
raise IllegalMigrationNameError.new(file) unless version
|
||||
version = version.to_i
|
||||
|
||||
if klasses.detect { |m| m.version == version }
|
||||
raise DuplicateMigrationVersionError.new(version)
|
||||
end
|
||||
|
||||
if klasses.detect { |m| m.name == name.camelize && m.scope == scope }
|
||||
raise DuplicateMigrationNameError.new(name.camelize)
|
||||
end
|
||||
|
||||
migration = MigrationProxy.new
|
||||
migration.name = name.camelize
|
||||
migration.version = version
|
||||
migration.filename = file
|
||||
migration.scope = scope
|
||||
klasses << migration
|
||||
end
|
||||
|
||||
migrations.sort_by(&:version)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def move(direction, migrations_path, steps)
|
||||
@@ -546,30 +607,7 @@ module ActiveRecord
|
||||
|
||||
def migrations
|
||||
@migrations ||= begin
|
||||
files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
|
||||
|
||||
migrations = files.inject([]) do |klasses, file|
|
||||
version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
|
||||
|
||||
raise IllegalMigrationNameError.new(file) unless version
|
||||
version = version.to_i
|
||||
|
||||
if klasses.detect { |m| m.version == version }
|
||||
raise DuplicateMigrationVersionError.new(version)
|
||||
end
|
||||
|
||||
if klasses.detect { |m| m.name == name.camelize }
|
||||
raise DuplicateMigrationNameError.new(name.camelize)
|
||||
end
|
||||
|
||||
migration = MigrationProxy.new
|
||||
migration.name = name.camelize
|
||||
migration.version = version
|
||||
migration.filename = file
|
||||
klasses << migration
|
||||
end
|
||||
|
||||
migrations = migrations.sort_by { |m| m.version }
|
||||
migrations = self.class.migrations(@migrations_path)
|
||||
down? ? migrations.reverse : migrations
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,29 @@ namespace :db do
|
||||
task :load_config => :rails_env do
|
||||
require 'active_record'
|
||||
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
||||
ActiveRecord::Migrator.migrations_path = Rails.application.config.paths.db.migrate.to_a.first
|
||||
end
|
||||
|
||||
desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
|
||||
task :copy_migrations => :load_config do
|
||||
to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip }
|
||||
railties = {}
|
||||
Rails.application.railties.all do |railtie|
|
||||
next unless to_load == :all || to_load.include?(railtie.railtie_name)
|
||||
|
||||
if railtie.config.respond_to?(:paths) && railtie.config.paths.db
|
||||
railties[railtie.railtie_name] = railtie.config.paths.db.migrate.to_a.first
|
||||
end
|
||||
end
|
||||
|
||||
copied = ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_path, railties)
|
||||
|
||||
if copied.blank?
|
||||
puts "No migrations were copied, project is up to date."
|
||||
else
|
||||
puts "The following migrations were copied:"
|
||||
puts copied.map{ |path| File.basename(path) }.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
namespace :create do
|
||||
@@ -139,7 +162,7 @@ namespace :db do
|
||||
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
|
||||
task :migrate => :environment do
|
||||
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
||||
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
||||
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
@@ -162,7 +185,7 @@ namespace :db do
|
||||
task :up => :environment do
|
||||
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
||||
raise "VERSION is required" unless version
|
||||
ActiveRecord::Migrator.run(:up, "db/migrate/", version)
|
||||
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_path, version)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
@@ -170,7 +193,7 @@ namespace :db do
|
||||
task :down => :environment do
|
||||
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
||||
raise "VERSION is required" unless version
|
||||
ActiveRecord::Migrator.run(:down, "db/migrate/", version)
|
||||
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_path, version)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
@@ -208,14 +231,14 @@ namespace :db do
|
||||
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
||||
task :rollback => :environment do
|
||||
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
||||
ActiveRecord::Migrator.rollback('db/migrate/', step)
|
||||
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_path, step)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
||||
task :forward => :environment do
|
||||
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
||||
ActiveRecord::Migrator.forward('db/migrate/', step)
|
||||
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_path, step)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
@@ -260,7 +283,7 @@ namespace :db do
|
||||
# desc "Raises an error if there are pending migrations"
|
||||
task :abort_if_pending_migrations => :environment do
|
||||
if defined? ActiveRecord
|
||||
pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
|
||||
pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_path).pending_migrations
|
||||
|
||||
if pending_migrations.any?
|
||||
puts "You have #{pending_migrations.size} pending migrations:"
|
||||
|
||||
@@ -14,6 +14,12 @@ module ActiveRecord
|
||||
def self.base_root
|
||||
File.dirname(__FILE__)
|
||||
end
|
||||
|
||||
# Implement the required interface for Rails::Generators::Migration.
|
||||
def self.next_migration_number(dirname) #:nodoc:
|
||||
next_migration_number = current_migration_number(dirname) + 1
|
||||
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,3 +83,21 @@ begin
|
||||
ensure
|
||||
$stdout = original_stdout
|
||||
end
|
||||
|
||||
class << Time
|
||||
unless method_defined? :now_before_time_travel
|
||||
alias_method :now_before_time_travel, :now
|
||||
end
|
||||
|
||||
def now
|
||||
(@now ||= nil) || now_before_time_travel
|
||||
end
|
||||
|
||||
def travel_to(time, &block)
|
||||
@now = time
|
||||
block.call
|
||||
ensure
|
||||
@now = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1875,5 +1875,130 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class CopyMigrationsTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
end
|
||||
|
||||
def clear
|
||||
ActiveRecord::Base.timestamped_migrations = true
|
||||
to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
|
||||
File.delete(*to_delete)
|
||||
end
|
||||
|
||||
def test_copying_migrations_without_timestamps
|
||||
ActiveRecord::Base.timestamped_migrations = false
|
||||
@migrations_path = MIGRATIONS_ROOT + "/valid"
|
||||
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
||||
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
|
||||
assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
|
||||
assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied
|
||||
|
||||
files_count = Dir[@migrations_path + "/*.rb"].length
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
|
||||
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
||||
assert copied.empty?
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
|
||||
def test_copying_migrations_without_timestamps_from_2_sources
|
||||
ActiveRecord::Base.timestamped_migrations = false
|
||||
@migrations_path = MIGRATIONS_ROOT + "/valid"
|
||||
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
||||
|
||||
sources = ActiveSupport::OrderedHash.new
|
||||
sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy"
|
||||
ActiveRecord::Migration.copy(@migrations_path, sources)
|
||||
assert File.exists?(@migrations_path + "/4_people_have_hobbies.omg.rb")
|
||||
assert File.exists?(@migrations_path + "/5_people_have_descriptions.omg.rb")
|
||||
assert File.exists?(@migrations_path + "/6_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/7_people_have_descriptions.bukkits.rb")
|
||||
|
||||
files_count = Dir[@migrations_path + "/*.rb"].length
|
||||
ActiveRecord::Migration.copy(@migrations_path, sources)
|
||||
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
|
||||
def test_copying_migrations_with_timestamps
|
||||
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
||||
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
||||
|
||||
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
|
||||
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
||||
expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
|
||||
@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
|
||||
assert_equal expected, copied
|
||||
|
||||
files_count = Dir[@migrations_path + "/*.rb"].length
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
|
||||
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
||||
assert copied.empty?
|
||||
end
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
|
||||
def test_copying_migrations_with_timestamps_from_2_sources
|
||||
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
||||
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
||||
|
||||
sources = ActiveSupport::OrderedHash.new
|
||||
sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
|
||||
|
||||
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, sources)
|
||||
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.omg.rb")
|
||||
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.omg.rb")
|
||||
assert File.exists?(@migrations_path + "/20100726101012_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/20100726101013_people_have_descriptions.bukkits.rb")
|
||||
assert_equal 4, copied.length
|
||||
|
||||
files_count = Dir[@migrations_path + "/*.rb"].length
|
||||
ActiveRecord::Migration.copy(@migrations_path, sources)
|
||||
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
||||
end
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
|
||||
def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
|
||||
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
|
||||
@existing_migrations = Dir[@migrations_path + "/*.rb"]
|
||||
|
||||
Time.travel_to(created_at = Time.utc(2010, 2, 20, 10, 10, 10)) do
|
||||
ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
|
||||
assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
|
||||
|
||||
files_count = Dir[@migrations_path + "/*.rb"].length
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
|
||||
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
|
||||
assert copied.empty?
|
||||
end
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
|
||||
def test_copying_migrations_to_empty_directory
|
||||
@migrations_path = MIGRATIONS_ROOT + "/empty"
|
||||
@existing_migrations = []
|
||||
|
||||
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
|
||||
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
|
||||
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
|
||||
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
|
||||
assert_equal 2, copied.length
|
||||
end
|
||||
ensure
|
||||
clear
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
0
activerecord/test/migrations/empty/.gitkeep
Normal file
0
activerecord/test/migrations/empty/.gitkeep
Normal file
@@ -0,0 +1,9 @@
|
||||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "hobbies", :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "hobbies"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "description", :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "description"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "hobbies", :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "hobbies"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "description", :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "description"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "last_name", :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "last_name"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class WeNeedReminders < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table("reminders") do |t|
|
||||
t.column :content, :text
|
||||
t.column :remind_at, :datetime
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "reminders"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class InnocentJointable < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table("people_reminders", :id => false) do |t|
|
||||
t.column :reminder_id, :integer
|
||||
t.column :person_id, :integer
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "people_reminders"
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
23
railties/lib/rails/engine/railties.rb
Normal file
23
railties/lib/rails/engine/railties.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
174
railties/test/railties/mounted_engine_test.rb
Normal file
174
railties/test/railties/mounted_engine_test.rb
Normal file
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user