mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge branch 'master' of github.com:lifo/docrails
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
*.gem
|
||||
pkg
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
debug.log
|
||||
doc/rdoc
|
||||
activemodel/doc
|
||||
|
||||
@@ -737,13 +737,13 @@ module ActionMailer #:nodoc:
|
||||
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 " \
|
||||
"url_helpers of a router directly."
|
||||
"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 url_helpers of the router, which will override this module
|
||||
# we include the router's url_helpers, which will override this module.
|
||||
extend DeprecatedUrlOptions
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# encoding: utf-8
|
||||
require 'abstract_unit'
|
||||
require 'active_support/time'
|
||||
|
||||
class BaseTest < ActiveSupport::TestCase
|
||||
# TODO Add some tests for implicity layout render and url helpers
|
||||
|
||||
@@ -28,7 +28,7 @@ class UrlTestMailer < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerUrlTest < Test::Unit::TestCase
|
||||
class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
|
||||
def encode( text, charset="UTF-8" )
|
||||
quoted_printable( text, charset )
|
||||
@@ -57,10 +57,12 @@ class ActionMailerUrlTest < Test::Unit::TestCase
|
||||
|
||||
def test_signed_up_with_url
|
||||
UrlTestMailer.delivery_method = :test
|
||||
|
||||
AppRoutes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
|
||||
|
||||
assert_deprecated do
|
||||
AppRoutes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
|
||||
end
|
||||
end
|
||||
|
||||
expected = new_mail
|
||||
|
||||
@@ -27,5 +27,5 @@ Gem::Specification.new do |s|
|
||||
s.add_dependency('rack-test', '~> 0.5.4')
|
||||
#s.add_dependency('rack-mount', '~> 0.6.6')
|
||||
s.add_dependency('tzinfo', '~> 0.3.16')
|
||||
s.add_dependency('erubis', '~> 2.6.5')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
end
|
||||
|
||||
@@ -50,8 +50,8 @@ module AbstractController
|
||||
if controller.respond_to?(:_helpers)
|
||||
include controller._helpers
|
||||
|
||||
if controller.respond_to?(:_router)
|
||||
include controller._router.url_helpers
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
end
|
||||
|
||||
# TODO: Fix RJS to not require this
|
||||
|
||||
@@ -60,17 +60,11 @@ module ActionController
|
||||
include ActionController::Compatibility
|
||||
|
||||
def self.inherited(klass)
|
||||
::ActionController::Base.subclasses << klass.to_s
|
||||
super
|
||||
klass.helper :all
|
||||
end
|
||||
|
||||
def self.subclasses
|
||||
@subclasses ||= []
|
||||
end
|
||||
|
||||
config_accessor :asset_host, :asset_path
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,17 +12,17 @@ module ActionController
|
||||
).merge(:script_name => request.script_name)
|
||||
end
|
||||
|
||||
def _router
|
||||
raise "In order to use #url_for, you must include the helpers of a particular " \
|
||||
"router. For instance, `include Rails.application.routes.url_helpers"
|
||||
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 - _router.named_routes.helper_names
|
||||
super - _routes.named_routes.helper_names
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,6 @@ require "rails"
|
||||
require "action_controller"
|
||||
require "action_dispatch/railtie"
|
||||
require "action_view/railtie"
|
||||
require "active_support/core_ext/class/subclasses"
|
||||
require "active_support/deprecation/proxy_wrappers"
|
||||
require "active_support/deprecation"
|
||||
|
||||
|
||||
@@ -64,6 +64,11 @@ module ActionDispatch
|
||||
super(key.to_s, value)
|
||||
end
|
||||
|
||||
def clear
|
||||
load_for_write!
|
||||
super
|
||||
end
|
||||
|
||||
def to_hash
|
||||
load_for_read!
|
||||
h = {}.replace(self)
|
||||
|
||||
@@ -31,7 +31,7 @@ module ActionDispatch
|
||||
# Think of creating routes as drawing a map for your requests. The map tells
|
||||
# them where to go based on some predefined pattern:
|
||||
#
|
||||
# AppName::Applications.routes.draw do |map|
|
||||
# AppName::Application.routes.draw do |map|
|
||||
# Pattern 1 tells some request to go to one place
|
||||
# Pattern 2 tell them to go to another
|
||||
# ...
|
||||
@@ -62,7 +62,7 @@ module ActionDispatch
|
||||
#
|
||||
# redirect_to show_item_path(:id => 25)
|
||||
#
|
||||
# Use <tt>root</tt> as a shorthand to name a route for the root path "".
|
||||
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
|
||||
#
|
||||
# # In routes.rb
|
||||
# root :to => 'blogs#index'
|
||||
@@ -72,7 +72,7 @@ module ActionDispatch
|
||||
#
|
||||
# # and provide these named routes
|
||||
# root_url # => 'http://www.example.com/'
|
||||
# root_path # => ''
|
||||
# root_path # => '/'
|
||||
#
|
||||
# Note: when using +controller+, the route is simply named after the
|
||||
# method you call on the block parameter rather than map.
|
||||
@@ -91,9 +91,7 @@ module ActionDispatch
|
||||
#
|
||||
# Routes can generate pretty URLs. For example:
|
||||
#
|
||||
# match '/articles/:year/:month/:day', :constraints => {
|
||||
# :controller => 'articles',
|
||||
# :action => 'find_by_date',
|
||||
# match '/articles/:year/:month/:day' => 'articles#find_by_id', :constraints => {
|
||||
# :year => /\d{4}/,
|
||||
# :month => /\d{1,2}/,
|
||||
# :day => /\d{1,2}/
|
||||
@@ -167,7 +165,7 @@ module ActionDispatch
|
||||
#
|
||||
# You can reload routes if you feel you must:
|
||||
#
|
||||
# Rails::Application.reload_routes!
|
||||
# Rails.application.reload_routes!
|
||||
#
|
||||
# This will clear all named routes and reload routes.rb if the file has been modified from
|
||||
# last load. To absolutely force reloading, use <tt>reload!</tt>.
|
||||
|
||||
@@ -19,9 +19,9 @@ module ActionDispatch
|
||||
|
||||
def in_memory_controller_namespaces
|
||||
namespaces = Set.new
|
||||
ActionController::Base.subclasses.each do |klass|
|
||||
controller_name = klass.underscore
|
||||
namespaces << controller_name.split('/')[0...-1].join('/')
|
||||
ActionController::Base.descendants.each do |klass|
|
||||
next if klass.anonymous?
|
||||
namespaces << klass.name.underscore.split('/')[0...-1].join('/')
|
||||
end
|
||||
namespaces.delete('')
|
||||
namespaces
|
||||
@@ -31,7 +31,7 @@ module ActionDispatch
|
||||
class DeprecatedMapper #:nodoc:
|
||||
def initialize(set) #:nodoc:
|
||||
ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
|
||||
"Please check how to update your router file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
||||
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
||||
@set = set
|
||||
end
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :app
|
||||
|
||||
def initialize(app, constraints, request)
|
||||
@app, @constraints, @request = app, constraints, request
|
||||
end
|
||||
@@ -63,6 +65,16 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
if path.match(':controller')
|
||||
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
||||
|
||||
# Add a default constraint for :controller path segments that matches namespaced
|
||||
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
||||
# GET /admin/products/show/1
|
||||
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
||||
options.reverse_merge!(:controller => /.+?/)
|
||||
end
|
||||
|
||||
path = normalize_path(path)
|
||||
path_without_format = path.sub(/\(\.:format\)$/, '')
|
||||
|
||||
@@ -133,8 +145,11 @@ module ActionDispatch
|
||||
defaults[:controller] ||= default_controller
|
||||
defaults[:action] ||= default_action
|
||||
|
||||
defaults.delete(:controller) if defaults[:controller].blank?
|
||||
defaults.delete(:action) if defaults[:action].blank?
|
||||
defaults.delete(:controller) if defaults[:controller].blank? || defaults[:controller].is_a?(Regexp)
|
||||
defaults.delete(:action) if defaults[:action].blank? || defaults[:action].is_a?(Regexp)
|
||||
|
||||
defaults[:controller] = defaults[:controller].to_s if defaults.key?(:controller)
|
||||
defaults[:action] = defaults[:action].to_s if defaults.key?(:action)
|
||||
|
||||
if defaults[:controller].blank? && segment_keys.exclude?("controller")
|
||||
raise ArgumentError, "missing :controller"
|
||||
@@ -183,15 +198,15 @@ module ActionDispatch
|
||||
|
||||
def default_controller
|
||||
if @options[:controller]
|
||||
@options[:controller].to_s
|
||||
@options[:controller]
|
||||
elsif @scope[:controller]
|
||||
@scope[:controller].to_s
|
||||
@scope[:controller]
|
||||
end
|
||||
end
|
||||
|
||||
def default_action
|
||||
if @options[:action]
|
||||
@options[:action].to_s
|
||||
@options[:action]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -431,12 +446,16 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def merge_options_scope(parent, child)
|
||||
(parent || {}).merge(child)
|
||||
(parent || {}).except(*override_keys(child)).merge(child)
|
||||
end
|
||||
|
||||
def merge_shallow_scope(parent, child)
|
||||
child ? true : false
|
||||
end
|
||||
|
||||
def override_keys(child)
|
||||
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
|
||||
end
|
||||
end
|
||||
|
||||
module Resources
|
||||
@@ -444,7 +463,7 @@ module ActionDispatch
|
||||
# a path appended since they fit properly in their scope level.
|
||||
VALID_ON_OPTIONS = [:new, :collection, :member]
|
||||
CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
|
||||
MERGE_FROM_SCOPE_OPTIONS = [:shallow, :constraints]
|
||||
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
|
||||
|
||||
class Resource #:nodoc:
|
||||
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
|
||||
@@ -464,9 +483,9 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
def actions
|
||||
if only = options[:only]
|
||||
if only = @options[:only]
|
||||
Array(only).map(&:to_sym)
|
||||
elsif except = options[:except]
|
||||
elsif except = @options[:except]
|
||||
default_actions - Array(except).map(&:to_sym)
|
||||
else
|
||||
default_actions
|
||||
@@ -489,68 +508,31 @@ module ActionDispatch
|
||||
singular
|
||||
end
|
||||
|
||||
alias_method :nested_name, :member_name
|
||||
|
||||
# Checks for uncountable plurals, and appends "_index" if they're.
|
||||
def collection_name
|
||||
singular == plural ? "#{plural}_index" : plural
|
||||
end
|
||||
|
||||
def shallow?
|
||||
options[:shallow] ? true : false
|
||||
end
|
||||
|
||||
def constraints
|
||||
options[:constraints] || {}
|
||||
end
|
||||
|
||||
def id_constraint?
|
||||
options[:id] && options[:id].is_a?(Regexp) || constraints[:id] && constraints[:id].is_a?(Regexp)
|
||||
end
|
||||
|
||||
def id_constraint
|
||||
options[:id] || constraints[:id]
|
||||
end
|
||||
|
||||
def collection_options
|
||||
(options || {}).dup.tap do |opts|
|
||||
opts.delete(:id)
|
||||
opts[:constraints] = options[:constraints].dup if options[:constraints]
|
||||
opts[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
|
||||
end
|
||||
end
|
||||
|
||||
def nested_path
|
||||
"#{path}/:#{singular}_id"
|
||||
end
|
||||
|
||||
def nested_options
|
||||
{}.tap do |opts|
|
||||
opts[:as] = member_name
|
||||
opts["#{singular}_id".to_sym] = id_constraint if id_constraint?
|
||||
opts[:options] = { :shallow => shallow? } unless options[:shallow].nil?
|
||||
end
|
||||
end
|
||||
|
||||
def resource_scope
|
||||
[{ :controller => controller }]
|
||||
{ :controller => controller }
|
||||
end
|
||||
|
||||
def collection_scope
|
||||
[path, collection_options]
|
||||
path
|
||||
end
|
||||
|
||||
def member_scope
|
||||
["#{path}/:id", options]
|
||||
"#{path}/:id"
|
||||
end
|
||||
|
||||
def new_scope(new_path)
|
||||
["#{path}/#{new_path}"]
|
||||
"#{path}/#{new_path}"
|
||||
end
|
||||
|
||||
def nested_scope
|
||||
[nested_path, nested_options]
|
||||
"#{path}/:#{singular}_id"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class SingletonResource < Resource #:nodoc:
|
||||
@@ -559,7 +541,7 @@ module ActionDispatch
|
||||
def initialize(entities, options)
|
||||
@name = entities.to_s
|
||||
@path = options.delete(:path) || @name
|
||||
@controller = (options.delete(:controller) || @name.to_s.pluralize).to_s
|
||||
@controller = (options.delete(:controller) || plural).to_s
|
||||
@as = options.delete(:as)
|
||||
@options = options
|
||||
end
|
||||
@@ -569,24 +551,10 @@ module ActionDispatch
|
||||
end
|
||||
alias :collection_name :member_name
|
||||
|
||||
def nested_path
|
||||
def member_scope
|
||||
path
|
||||
end
|
||||
|
||||
def nested_options
|
||||
{}.tap do |opts|
|
||||
opts[:as] = member_name
|
||||
opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil?
|
||||
end
|
||||
end
|
||||
|
||||
def shallow?
|
||||
false
|
||||
end
|
||||
|
||||
def member_scope
|
||||
[path, options]
|
||||
end
|
||||
alias :nested_scope :member_scope
|
||||
end
|
||||
|
||||
def initialize(*args) #:nodoc:
|
||||
@@ -693,18 +661,18 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
with_scope_level(:nested) do
|
||||
if parent_resource.shallow?
|
||||
if shallow?
|
||||
with_exclusive_scope do
|
||||
if @scope[:shallow_path].blank?
|
||||
scope(*parent_resource.nested_scope) { yield }
|
||||
scope(parent_resource.nested_scope, nested_options) { yield }
|
||||
else
|
||||
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
|
||||
scope(*parent_resource.nested_scope) { yield }
|
||||
scope(parent_resource.nested_scope, nested_options) { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
scope(*parent_resource.nested_scope) { yield }
|
||||
scope(parent_resource.nested_scope, nested_options) { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -723,6 +691,10 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
def shallow?
|
||||
parent_resource.instance_of?(Resource) && @scope[:shallow]
|
||||
end
|
||||
|
||||
def match(*args)
|
||||
options = args.extract_options!.dup
|
||||
options[:anchor] = true unless options.key?(:anchor)
|
||||
@@ -800,16 +772,17 @@ module ActionDispatch
|
||||
return true
|
||||
end
|
||||
|
||||
if path_names = options.delete(:path_names)
|
||||
scope(:path_names => path_names) do
|
||||
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
||||
unless scope_options.empty?
|
||||
scope(scope_options) do
|
||||
send(method, resources.pop, options, &block)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
scope_options = @scope.slice(*MERGE_FROM_SCOPE_OPTIONS).delete_if{ |k,v| v.blank? }
|
||||
options.reverse_merge!(scope_options) unless scope_options.empty?
|
||||
options.reverse_merge!(@scope[:options]) unless @scope[:options].blank?
|
||||
unless action_options?(options)
|
||||
options.merge!(scope_action_options) if scope_action_options?
|
||||
end
|
||||
|
||||
if resource_scope?
|
||||
nested do
|
||||
@@ -821,6 +794,18 @@ module ActionDispatch
|
||||
false
|
||||
end
|
||||
|
||||
def action_options?(options)
|
||||
options[:only] || options[:except]
|
||||
end
|
||||
|
||||
def scope_action_options?
|
||||
@scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
|
||||
end
|
||||
|
||||
def scope_action_options
|
||||
@scope[:options].slice(:only, :except)
|
||||
end
|
||||
|
||||
def resource_scope?
|
||||
[:resource, :resources].include?(@scope[:scope_level])
|
||||
end
|
||||
@@ -853,7 +838,7 @@ module ActionDispatch
|
||||
|
||||
def resource_scope(resource)
|
||||
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
|
||||
scope(*parent_resource.resource_scope) do
|
||||
scope(parent_resource.resource_scope) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
@@ -861,7 +846,7 @@ module ActionDispatch
|
||||
|
||||
def new_scope
|
||||
with_scope_level(:new) do
|
||||
scope(*parent_resource.new_scope(action_path(:new))) do
|
||||
scope(parent_resource.new_scope(action_path(:new))) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
@@ -869,7 +854,7 @@ module ActionDispatch
|
||||
|
||||
def collection_scope
|
||||
with_scope_level(:collection) do
|
||||
scope(*parent_resource.collection_scope) do
|
||||
scope(parent_resource.collection_scope) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
@@ -877,18 +862,33 @@ module ActionDispatch
|
||||
|
||||
def member_scope
|
||||
with_scope_level(:member) do
|
||||
scope(*parent_resource.member_scope) do
|
||||
scope(parent_resource.member_scope) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nested_options
|
||||
{}.tap do |options|
|
||||
options[:as] = parent_resource.member_name
|
||||
options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
|
||||
end
|
||||
end
|
||||
|
||||
def id_constraint?
|
||||
@scope[:id].is_a?(Regexp) || (@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp))
|
||||
end
|
||||
|
||||
def id_constraint
|
||||
@scope[:id] || @scope[:constraints][:id]
|
||||
end
|
||||
|
||||
def canonical_action?(action, flag)
|
||||
flag && CANONICAL_ACTIONS.include?(action)
|
||||
end
|
||||
|
||||
def shallow_scoping?
|
||||
parent_resource && parent_resource.shallow? && @scope[:scope_level] == :member
|
||||
shallow? && @scope[:scope_level] == :member
|
||||
end
|
||||
|
||||
def path_for_action(action, path)
|
||||
@@ -932,7 +932,7 @@ module ActionDispatch
|
||||
collection_name = parent_resource.collection_name
|
||||
member_name = parent_resource.member_name
|
||||
name_prefix = "#{name_prefix}_" if name_prefix.present?
|
||||
end
|
||||
end
|
||||
|
||||
case @scope[:scope_level]
|
||||
when :collection
|
||||
|
||||
@@ -26,7 +26,7 @@ module ActionDispatch
|
||||
return [404, {'X-Cascade' => 'pass'}, []]
|
||||
end
|
||||
|
||||
controller.action(params[:action]).call(env)
|
||||
dispatch(controller, params[:action], env)
|
||||
end
|
||||
|
||||
def prepare_params!(params)
|
||||
@@ -34,29 +34,43 @@ module ActionDispatch
|
||||
split_glob_param!(params) if @glob_param
|
||||
end
|
||||
|
||||
def controller(params, raise_error=true)
|
||||
# If this is a default_controller (i.e. a controller specified by the user)
|
||||
# we should raise an error in case it's not found, because it usually means
|
||||
# an user error. However, if the controller was retrieved through a dynamic
|
||||
# segment, as in :controller(/:action), we should simply return nil and
|
||||
# delegate the control back to Rack cascade. Besides, if this is not a default
|
||||
# controller, it means we should respect the @scope[:module] parameter.
|
||||
def controller(params, default_controller=true)
|
||||
if params && params.key?(:controller)
|
||||
controller_param = params[:controller]
|
||||
unless controller = @controllers[controller_param]
|
||||
controller_name = "#{controller_param.camelize}Controller"
|
||||
controller = @controllers[controller_param] =
|
||||
ActiveSupport::Dependencies.ref(controller_name)
|
||||
end
|
||||
|
||||
controller.get
|
||||
controller_reference(controller_param)
|
||||
end
|
||||
rescue NameError => e
|
||||
raise ActionController::RoutingError, e.message, e.backtrace if raise_error
|
||||
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
|
||||
end
|
||||
|
||||
private
|
||||
def merge_default_action!(params)
|
||||
params[:action] ||= 'index'
|
||||
end
|
||||
private
|
||||
|
||||
def split_glob_param!(params)
|
||||
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
|
||||
def controller_reference(controller_param)
|
||||
unless controller = @controllers[controller_param]
|
||||
controller_name = "#{controller_param.camelize}Controller"
|
||||
controller = @controllers[controller_param] =
|
||||
ActiveSupport::Dependencies.ref(controller_name)
|
||||
end
|
||||
controller.get
|
||||
end
|
||||
|
||||
def dispatch(controller, action, env)
|
||||
controller.action(action).call(env)
|
||||
end
|
||||
|
||||
def merge_default_action!(params)
|
||||
params[:action] ||= 'index'
|
||||
end
|
||||
|
||||
def split_glob_param!(params)
|
||||
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
|
||||
end
|
||||
end
|
||||
|
||||
# A NamedRouteCollection instance is a collection of named routes, and also
|
||||
@@ -268,10 +282,10 @@ module ActionDispatch
|
||||
# Yes plz - JP
|
||||
included do
|
||||
routes.install_helpers(self)
|
||||
singleton_class.send(:define_method, :_router) { routes }
|
||||
singleton_class.send(:define_method, :_routes) { routes }
|
||||
end
|
||||
|
||||
define_method(:_router) { routes }
|
||||
define_method(:_routes) { routes }
|
||||
end
|
||||
|
||||
helpers
|
||||
@@ -302,7 +316,6 @@ module ActionDispatch
|
||||
@extras = extras
|
||||
|
||||
normalize_options!
|
||||
normalize_recall!
|
||||
normalize_controller_action_id!
|
||||
use_relative_controller!
|
||||
controller.sub!(%r{^/}, '') if controller
|
||||
@@ -319,7 +332,11 @@ module ActionDispatch
|
||||
|
||||
def use_recall_for(key)
|
||||
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
|
||||
@options[key] = @recall.delete(key)
|
||||
if named_route_exists?
|
||||
@options[key] = @recall.delete(key) if segment_keys.include?(key)
|
||||
else
|
||||
@options[key] = @recall.delete(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -343,15 +360,6 @@ module ActionDispatch
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_recall!
|
||||
# If the target route is not a standard route then remove controller and action
|
||||
# from the options otherwise they will appear in the url parameters
|
||||
if block_or_proc_route_target?
|
||||
recall.delete(:controller) unless segment_keys.include?(:controller)
|
||||
recall.delete(:action) unless segment_keys.include?(:action)
|
||||
end
|
||||
end
|
||||
|
||||
# This pulls :controller, :action, and :id out of the recall.
|
||||
# The recall key is only used if there is no key in the options
|
||||
# or if the key in the options is identical. If any of
|
||||
@@ -424,12 +432,8 @@ module ActionDispatch
|
||||
named_route && set.named_routes[named_route]
|
||||
end
|
||||
|
||||
def block_or_proc_route_target?
|
||||
named_route_exists? && !set.named_routes[named_route].app.is_a?(Dispatcher)
|
||||
end
|
||||
|
||||
def segment_keys
|
||||
named_route_exists? ? set.named_routes[named_route].segment_keys : []
|
||||
set.named_routes[named_route].segment_keys
|
||||
end
|
||||
end
|
||||
|
||||
@@ -474,7 +478,7 @@ module ActionDispatch
|
||||
path_options = yield(path_options) if block_given?
|
||||
path = generate(path_options, path_segments || {})
|
||||
|
||||
# ROUTES TODO: This can be called directly, so script_name should probably be set in the router
|
||||
# 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)
|
||||
rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
|
||||
|
||||
@@ -506,6 +510,8 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
dispatcher = route.app
|
||||
dispatcher = dispatcher.app while dispatcher.is_a?(Mapper::Constraints)
|
||||
|
||||
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
|
||||
dispatcher.prepare_params!(params)
|
||||
return params
|
||||
|
||||
@@ -128,7 +128,7 @@ module ActionDispatch
|
||||
when String
|
||||
options
|
||||
when nil, Hash
|
||||
_router.url_for(url_options.merge((options || {}).symbolize_keys))
|
||||
_routes.url_for(url_options.merge((options || {}).symbolize_keys))
|
||||
else
|
||||
polymorphic_url(options)
|
||||
end
|
||||
|
||||
@@ -173,7 +173,7 @@ module ActionView #:nodoc:
|
||||
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
|
||||
|
||||
class_attribute :helpers
|
||||
class_attribute :_router
|
||||
class_attribute :_routes
|
||||
|
||||
class << self
|
||||
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
|
||||
|
||||
@@ -12,7 +12,7 @@ module ActionView
|
||||
# and controllers.
|
||||
module UrlHelper
|
||||
# This helper may be included in any class that includes the
|
||||
# URL helpers of a router (router.url_helpers). Some methods
|
||||
# URL helpers of a routes (routes.url_helpers). Some methods
|
||||
# provided here will only work in the4 context of a request
|
||||
# (link_to_unless_current, for instance), which must be provided
|
||||
# as a method called #request on the context.
|
||||
|
||||
@@ -151,7 +151,7 @@ module ActionView
|
||||
@view ||= begin
|
||||
view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller)
|
||||
view.singleton_class.send :include, _helpers
|
||||
view.singleton_class.send :include, @controller._router.url_helpers
|
||||
view.singleton_class.send :include, @controller._routes.url_helpers
|
||||
view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash"
|
||||
view.extend(Locals)
|
||||
view.locals = self.locals
|
||||
@@ -192,13 +192,13 @@ module ActionView
|
||||
end
|
||||
end
|
||||
|
||||
def _router
|
||||
@controller._router if @controller.respond_to?(:_router)
|
||||
def _routes
|
||||
@controller._routes if @controller.respond_to?(:_routes)
|
||||
end
|
||||
|
||||
def method_missing(selector, *args)
|
||||
if @controller.respond_to?(:_router) &&
|
||||
@controller._router.named_routes.helpers.include?(selector)
|
||||
if @controller.respond_to?(:_routes) &&
|
||||
@controller._routes.named_routes.helpers.include?(selector)
|
||||
@controller.__send__(selector, *args)
|
||||
else
|
||||
super
|
||||
|
||||
@@ -41,7 +41,7 @@ require 'pp' # require 'pp' early to prevent hidden_methods from not picking up
|
||||
module Rails
|
||||
end
|
||||
|
||||
# Monkey patch the old router initialization to be silenced.
|
||||
# Monkey patch the old routes initialization to be silenced.
|
||||
class ActionDispatch::Routing::DeprecatedMapper
|
||||
def initialize_with_silencer(*args)
|
||||
ActiveSupport::Deprecation.silence { initialize_without_silencer(*args) }
|
||||
@@ -182,13 +182,16 @@ class ActionController::IntegrationTest < ActiveSupport::TestCase
|
||||
|
||||
self.app = build_app
|
||||
|
||||
class StubDispatcher
|
||||
def self.new(*args)
|
||||
lambda { |env|
|
||||
params = env['action_dispatch.request.path_parameters']
|
||||
controller, action = params[:controller], params[:action]
|
||||
[200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
|
||||
}
|
||||
# Stub Rails dispatcher so it does not get controller references and
|
||||
# simply return the controller#action as Rack::Body.
|
||||
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
|
||||
protected
|
||||
def controller_reference(controller_param)
|
||||
controller_param
|
||||
end
|
||||
|
||||
def dispatch(controller, action, env)
|
||||
[200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -275,9 +278,9 @@ end
|
||||
|
||||
class ActionController::Base
|
||||
def self.test_routes(&block)
|
||||
router = ActionDispatch::Routing::RouteSet.new
|
||||
router.draw(&block)
|
||||
include router.url_helpers
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
routes.draw(&block)
|
||||
include routes.url_helpers
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -36,6 +36,15 @@ class RespondToController < ActionController::Base
|
||||
type.all { render :text => "Nothing" }
|
||||
end
|
||||
end
|
||||
|
||||
def json_xml_or_html
|
||||
respond_to do |type|
|
||||
type.json { render :text => 'JSON' }
|
||||
type.xml { render :xml => 'XML' }
|
||||
type.html { render :text => 'HTML' }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def forced_xml
|
||||
request.format = :xml
|
||||
@@ -364,6 +373,17 @@ class RespondToControllerTest < ActionController::TestCase
|
||||
get :handle_any_any
|
||||
assert_equal 'Whatever you ask for, I got it', @response.body
|
||||
end
|
||||
|
||||
def test_browser_check_with_any_any
|
||||
@request.accept = "application/json, application/xml"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'JSON', @response.body
|
||||
|
||||
@request.accept = "application/json, application/xml, */*"
|
||||
get :json_xml_or_html
|
||||
assert_equal 'HTML', @response.body
|
||||
end
|
||||
|
||||
|
||||
def test_rjs_type_skips_layout
|
||||
@request.accept = "text/javascript"
|
||||
|
||||
@@ -120,7 +120,7 @@ module AbstractController
|
||||
|
||||
def test_relative_url_root_is_respected
|
||||
# ROUTES TODO: Tests should not have to pass :relative_url_root directly. This
|
||||
# should probably come from the router.
|
||||
# should probably come from routes.
|
||||
|
||||
add_host!
|
||||
assert_equal('https://www.basecamphq.com/subdir/c/a/i',
|
||||
|
||||
@@ -45,7 +45,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
|
||||
match 'account/login', :to => redirect("/login")
|
||||
|
||||
match 'account/overview'
|
||||
constraints(lambda { |req| true }) do
|
||||
match 'account/overview'
|
||||
end
|
||||
|
||||
match '/account/nested/overview'
|
||||
match 'sign_in' => "sessions#new"
|
||||
|
||||
@@ -298,8 +301,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
|
||||
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
|
||||
|
||||
resource :token, :module => :api
|
||||
scope :module => :api do
|
||||
resource :token
|
||||
resources :errors, :shallow => true do
|
||||
resources :notices
|
||||
end
|
||||
@@ -310,11 +313,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
match '/' => 'mes#index'
|
||||
end
|
||||
|
||||
namespace :private do
|
||||
root :to => redirect('/private/index')
|
||||
match "index", :to => 'private#index'
|
||||
end
|
||||
|
||||
get "(/:username)/followers" => "followers#index"
|
||||
get "/groups(/user/:username)" => "groups#index"
|
||||
get "(/user/:username)/photos" => "photos#index"
|
||||
@@ -325,7 +323,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
match "whatever/:controller(/:action(/:id))"
|
||||
match "whatever/:controller(/:action(/:id))", :id => /\d+/
|
||||
|
||||
resource :profile do
|
||||
get :settings
|
||||
@@ -348,6 +346,63 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
namespace :private do
|
||||
root :to => redirect('/private/index')
|
||||
match "index", :to => 'private#index'
|
||||
end
|
||||
|
||||
scope :only => [:index, :show] do
|
||||
namespace :only do
|
||||
resources :clubs do
|
||||
resources :players
|
||||
resource :chairman
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scope :except => [:new, :create, :edit, :update, :destroy] do
|
||||
namespace :except do
|
||||
resources :clubs do
|
||||
resources :players
|
||||
resource :chairman
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scope :only => :show do
|
||||
namespace :only do
|
||||
resources :sectors, :only => :index do
|
||||
resources :companies do
|
||||
scope :only => :index do
|
||||
resources :divisions
|
||||
end
|
||||
scope :except => [:show, :update, :destroy] do
|
||||
resources :departments
|
||||
end
|
||||
end
|
||||
resource :leader
|
||||
resources :managers, :except => [:show, :update, :destroy]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scope :except => :index do
|
||||
namespace :except do
|
||||
resources :sectors, :except => [:show, :update, :destroy] do
|
||||
resources :companies do
|
||||
scope :except => [:show, :update, :destroy] do
|
||||
resources :divisions
|
||||
end
|
||||
scope :only => :index do
|
||||
resources :departments
|
||||
end
|
||||
end
|
||||
resource :leader
|
||||
resources :managers, :only => :index
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/
|
||||
end
|
||||
end
|
||||
@@ -470,6 +525,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_namespace_with_controller_segment
|
||||
assert_raise(ArgumentError) do
|
||||
self.class.stub_controllers do |routes|
|
||||
routes.draw do
|
||||
namespace :admin do
|
||||
match '/:controller(/:action(/:id(.:format)))'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_session_singleton_resource
|
||||
with_test_routes do
|
||||
get '/session'
|
||||
@@ -1240,7 +1307,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
assert_equal 'pass', @response.headers['X-Cascade']
|
||||
get '/products/0001/images'
|
||||
assert_equal 'images#index', @response.body
|
||||
get '/products/0001/images/1'
|
||||
get '/products/0001/images/0001'
|
||||
assert_equal 'images#show', @response.body
|
||||
|
||||
get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'}
|
||||
@@ -1285,6 +1352,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_url_generator_for_namespaced_generic_route
|
||||
with_test_routes do
|
||||
get 'whatever/foo/bar/show'
|
||||
assert_equal 'foo/bar#show', @response.body
|
||||
|
||||
get 'whatever/foo/bar/show/1'
|
||||
assert_equal 'foo/bar#show', @response.body
|
||||
|
||||
assert_equal 'http://www.example.com/whatever/foo/bar/show',
|
||||
url_for(:controller => "foo/bar", :action => "show")
|
||||
|
||||
assert_equal 'http://www.example.com/whatever/foo/bar/show/1',
|
||||
url_for(:controller => "foo/bar", :action => "show", :id => '1')
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_recognizes_account_overview
|
||||
with_test_routes do
|
||||
assert_recognizes({:controller => "account", :action => "overview"}, "/account/overview")
|
||||
@@ -1628,6 +1711,182 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_should_be_read_from_scope
|
||||
with_test_routes do
|
||||
get '/only/clubs'
|
||||
assert_equal 'only/clubs#index', @response.body
|
||||
assert_equal '/only/clubs', only_clubs_path
|
||||
|
||||
get '/only/clubs/1/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_only_club_path(:id => '1') }
|
||||
|
||||
get '/only/clubs/1/players'
|
||||
assert_equal 'only/players#index', @response.body
|
||||
assert_equal '/only/clubs/1/players', only_club_players_path(:club_id => '1')
|
||||
|
||||
get '/only/clubs/1/players/2/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') }
|
||||
|
||||
get '/only/clubs/1/chairman'
|
||||
assert_equal 'only/chairmen#show', @response.body
|
||||
assert_equal '/only/clubs/1/chairman', only_club_chairman_path(:club_id => '1')
|
||||
|
||||
get '/only/clubs/1/chairman/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_should_be_read_from_scope
|
||||
with_test_routes do
|
||||
get '/except/clubs'
|
||||
assert_equal 'except/clubs#index', @response.body
|
||||
assert_equal '/except/clubs', except_clubs_path
|
||||
|
||||
get '/except/clubs/1/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_except_club_path(:id => '1') }
|
||||
|
||||
get '/except/clubs/1/players'
|
||||
assert_equal 'except/players#index', @response.body
|
||||
assert_equal '/except/clubs/1/players', except_club_players_path(:club_id => '1')
|
||||
|
||||
get '/except/clubs/1/players/2/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_except_club_player_path(:club_id => '1', :id => '2') }
|
||||
|
||||
get '/except/clubs/1/chairman'
|
||||
assert_equal 'except/chairmen#show', @response.body
|
||||
assert_equal '/except/clubs/1/chairman', except_club_chairman_path(:club_id => '1')
|
||||
|
||||
get '/except/clubs/1/chairman/edit'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { edit_except_club_chairman_path(:club_id => '1') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_option_should_override_scope
|
||||
with_test_routes do
|
||||
get '/only/sectors'
|
||||
assert_equal 'only/sectors#index', @response.body
|
||||
assert_equal '/only/sectors', only_sectors_path
|
||||
|
||||
get '/only/sectors/1'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { only_sector_path(:id => '1') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_option_should_not_inherit
|
||||
with_test_routes do
|
||||
get '/only/sectors/1/companies/2'
|
||||
assert_equal 'only/companies#show', @response.body
|
||||
assert_equal '/only/sectors/1/companies/2', only_sector_company_path(:sector_id => '1', :id => '2')
|
||||
|
||||
get '/only/sectors/1/leader'
|
||||
assert_equal 'only/leaders#show', @response.body
|
||||
assert_equal '/only/sectors/1/leader', only_sector_leader_path(:sector_id => '1')
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_option_should_override_scope
|
||||
with_test_routes do
|
||||
get '/except/sectors'
|
||||
assert_equal 'except/sectors#index', @response.body
|
||||
assert_equal '/except/sectors', except_sectors_path
|
||||
|
||||
get '/except/sectors/1'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { except_sector_path(:id => '1') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_option_should_not_inherit
|
||||
with_test_routes do
|
||||
get '/except/sectors/1/companies/2'
|
||||
assert_equal 'except/companies#show', @response.body
|
||||
assert_equal '/except/sectors/1/companies/2', except_sector_company_path(:sector_id => '1', :id => '2')
|
||||
|
||||
get '/except/sectors/1/leader'
|
||||
assert_equal 'except/leaders#show', @response.body
|
||||
assert_equal '/except/sectors/1/leader', except_sector_leader_path(:sector_id => '1')
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_option_should_override_scoped_only
|
||||
with_test_routes do
|
||||
get '/only/sectors/1/managers'
|
||||
assert_equal 'only/managers#index', @response.body
|
||||
assert_equal '/only/sectors/1/managers', only_sector_managers_path(:sector_id => '1')
|
||||
|
||||
get '/only/sectors/1/managers/2'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { only_sector_manager_path(:sector_id => '1', :id => '2') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_option_should_override_scoped_except
|
||||
with_test_routes do
|
||||
get '/except/sectors/1/managers'
|
||||
assert_equal 'except/managers#index', @response.body
|
||||
assert_equal '/except/sectors/1/managers', except_sector_managers_path(:sector_id => '1')
|
||||
|
||||
get '/except/sectors/1/managers/2'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { except_sector_manager_path(:sector_id => '1', :id => '2') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_scope_should_override_parent_scope
|
||||
with_test_routes do
|
||||
get '/only/sectors/1/companies/2/divisions'
|
||||
assert_equal 'only/divisions#index', @response.body
|
||||
assert_equal '/only/sectors/1/companies/2/divisions', only_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
|
||||
|
||||
get '/only/sectors/1/companies/2/divisions/3'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { only_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_scope_should_override_parent_scope
|
||||
with_test_routes do
|
||||
get '/except/sectors/1/companies/2/divisions'
|
||||
assert_equal 'except/divisions#index', @response.body
|
||||
assert_equal '/except/sectors/1/companies/2/divisions', except_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
|
||||
|
||||
get '/except/sectors/1/companies/2/divisions/3'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { except_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_except_scope_should_override_parent_only_scope
|
||||
with_test_routes do
|
||||
get '/only/sectors/1/companies/2/departments'
|
||||
assert_equal 'only/departments#index', @response.body
|
||||
assert_equal '/only/sectors/1/companies/2/departments', only_sector_company_departments_path(:sector_id => '1', :company_id => '2')
|
||||
|
||||
get '/only/sectors/1/companies/2/departments/3'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { only_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_scope_should_override_parent_except_scope
|
||||
with_test_routes do
|
||||
get '/except/sectors/1/companies/2/departments'
|
||||
assert_equal 'except/departments#index', @response.body
|
||||
assert_equal '/except/sectors/1/companies/2/departments', except_sector_company_departments_path(:sector_id => '1', :company_id => '2')
|
||||
|
||||
get '/except/sectors/1/companies/2/departments/3'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raise(NoMethodError) { except_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_routes
|
||||
yield
|
||||
|
||||
@@ -30,6 +30,11 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
render :text => "id: #{request.session_options[:id]}"
|
||||
end
|
||||
|
||||
def call_session_clear
|
||||
session.clear
|
||||
head :ok
|
||||
end
|
||||
|
||||
def call_reset_session
|
||||
reset_session
|
||||
head :ok
|
||||
@@ -175,6 +180,23 @@ class CookieStoreTest < ActionController::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_session_value_after_session_clear
|
||||
with_test_route_set do
|
||||
get '/set_session_value'
|
||||
assert_response :success
|
||||
session_payload = response.body
|
||||
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
|
||||
headers['Set-Cookie']
|
||||
|
||||
get '/call_session_clear'
|
||||
assert_response :success
|
||||
|
||||
get '/get_session_value'
|
||||
assert_response :success
|
||||
assert_equal 'foo: nil', response.body
|
||||
end
|
||||
end
|
||||
|
||||
def test_persistent_session_id
|
||||
with_test_route_set do
|
||||
cookies[SessionKey] = SignedBar
|
||||
|
||||
@@ -2,24 +2,24 @@ require 'abstract_unit'
|
||||
|
||||
module TestUrlGeneration
|
||||
class WithMountPoint < ActionDispatch::IntegrationTest
|
||||
Router = ActionDispatch::Routing::RouteSet.new
|
||||
Router.draw { match "/foo", :to => "my_route_generating#index", :as => :foo }
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
Routes.draw { match "/foo", :to => "my_route_generating#index", :as => :foo }
|
||||
|
||||
class ::MyRouteGeneratingController < ActionController::Base
|
||||
include Router.url_helpers
|
||||
include Routes.url_helpers
|
||||
def index
|
||||
render :text => foo_path
|
||||
end
|
||||
end
|
||||
|
||||
include Router.url_helpers
|
||||
include Routes.url_helpers
|
||||
|
||||
def _router
|
||||
Router
|
||||
def _routes
|
||||
Routes
|
||||
end
|
||||
|
||||
def app
|
||||
Router
|
||||
Routes
|
||||
end
|
||||
|
||||
test "generating URLS normally" do
|
||||
@@ -30,7 +30,7 @@ module TestUrlGeneration
|
||||
assert_equal "/bar/foo", foo_path(:script_name => "/bar")
|
||||
end
|
||||
|
||||
test "the request's SCRIPT_NAME takes precedence over the router's" do
|
||||
test "the request's SCRIPT_NAME takes precedence over the routes'" do
|
||||
get "/foo", {}, 'SCRIPT_NAME' => "/new"
|
||||
assert_equal "/new/foo", response.body
|
||||
end
|
||||
|
||||
@@ -406,6 +406,14 @@ end
|
||||
class UrlHelperControllerTest < ActionController::TestCase
|
||||
class UrlHelperController < ActionController::Base
|
||||
test_routes do |map|
|
||||
match 'url_helper_controller_test/url_helper/show/:id',
|
||||
:to => 'url_helper_controller_test/url_helper#show',
|
||||
:as => :show
|
||||
|
||||
match 'url_helper_controller_test/url_helper/profile/:name',
|
||||
:to => 'url_helper_controller_test/url_helper#show',
|
||||
:as => :profile
|
||||
|
||||
match 'url_helper_controller_test/url_helper/show_named_route',
|
||||
:to => 'url_helper_controller_test/url_helper#show_named_route',
|
||||
:as => :show_named_route
|
||||
@@ -418,6 +426,14 @@ class UrlHelperControllerTest < ActionController::TestCase
|
||||
:as => :normalize_recall_params
|
||||
end
|
||||
|
||||
def show
|
||||
if params[:name]
|
||||
render :inline => 'ok'
|
||||
else
|
||||
redirect_to profile_path(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def show_url_for
|
||||
render :inline => "<%= url_for :controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for' %>"
|
||||
end
|
||||
@@ -484,15 +500,24 @@ class UrlHelperControllerTest < ActionController::TestCase
|
||||
assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body
|
||||
end
|
||||
|
||||
def test_recall_params_should_be_normalized_when_using_block_route
|
||||
def test_recall_params_should_be_normalized
|
||||
get :normalize_recall_params
|
||||
assert_equal '/url_helper_controller_test/url_helper/normalize_recall_params', @response.body
|
||||
end
|
||||
|
||||
def test_recall_params_should_not_be_changed_when_using_normal_route
|
||||
def test_recall_params_should_not_be_changed
|
||||
get :recall_params_not_changed
|
||||
assert_equal '/url_helper_controller_test/url_helper/show_url_for', @response.body
|
||||
end
|
||||
|
||||
def test_recall_params_should_normalize_id
|
||||
get :show, :id => '123'
|
||||
assert_equal 302, @response.status
|
||||
assert_equal 'http://test.host/url_helper_controller_test/url_helper/profile/123', @response.location
|
||||
|
||||
get :show, :name => '123'
|
||||
assert_equal 'ok', @response.body
|
||||
end
|
||||
end
|
||||
|
||||
class TasksController < ActionController::Base
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/array/conversions'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/hash/reverse_merge'
|
||||
|
||||
@@ -770,15 +770,20 @@ module ActiveRecord
|
||||
# Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
|
||||
# and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
|
||||
# [collection=objects]
|
||||
# Replaces the collections content by deleting and adding objects as appropriate.
|
||||
# Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
|
||||
# option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
|
||||
# direct.
|
||||
# [collection_singular_ids]
|
||||
# Returns an array of the associated objects' ids
|
||||
# [collection_singular_ids=ids]
|
||||
# Replace the collection with the objects identified by the primary keys in +ids+
|
||||
# Replace the collection with the objects identified by the primary keys in +ids+. This
|
||||
# method loads the models and calls <tt>collection=</tt>. See above.
|
||||
# [collection.clear]
|
||||
# Removes every object from the collection. This destroys the associated objects if they
|
||||
# are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
|
||||
# database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
|
||||
# If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
|
||||
# Join models are directly deleted.
|
||||
# [collection.empty?]
|
||||
# Returns +true+ if there are no associated objects.
|
||||
# [collection.size]
|
||||
@@ -871,9 +876,11 @@ module ActiveRecord
|
||||
# [:as]
|
||||
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
|
||||
# [:through]
|
||||
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
|
||||
# Specifies a join model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
|
||||
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
|
||||
# <tt>has_one</tt> or <tt>has_many</tt> association on the join model.
|
||||
# <tt>has_one</tt> or <tt>has_many</tt> association on the join model. The collection of join models can be managed via the collection
|
||||
# API. For example, new join models are created for newly associated objects, and if some are gone their rows are deleted (directly,
|
||||
# no destroy callbacks are triggered).
|
||||
# [:source]
|
||||
# Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
|
||||
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
|
||||
|
||||
@@ -393,7 +393,12 @@ module ActiveRecord
|
||||
@target = find_target.map do |f|
|
||||
i = @target.index(f)
|
||||
t = @target.delete_at(i) if i
|
||||
(t && t.changed?) ? t : f
|
||||
if t && t.changed?
|
||||
t
|
||||
else
|
||||
f.mark_for_destruction if t && t.marked_for_destruction?
|
||||
f
|
||||
end
|
||||
end + @target
|
||||
else
|
||||
@target = find_target
|
||||
|
||||
@@ -898,11 +898,6 @@ module ActiveRecord #:nodoc:
|
||||
store_full_sti_class ? name : name.demodulize
|
||||
end
|
||||
|
||||
def relation
|
||||
@relation ||= Relation.new(self, arel_table)
|
||||
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
|
||||
end
|
||||
|
||||
def arel_table
|
||||
@arel_table ||= Arel::Table.new(table_name, :engine => arel_engine)
|
||||
end
|
||||
@@ -943,6 +938,12 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relation #:nodoc:
|
||||
@relation ||= Relation.new(self, arel_table)
|
||||
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
|
||||
end
|
||||
|
||||
# Finder methods must instantiate through this method to work with the
|
||||
# single-table inheritance model that makes it possible to create
|
||||
# objects of different types from the same table.
|
||||
|
||||
@@ -29,7 +29,7 @@ module ActiveRecord
|
||||
if options.present?
|
||||
scoped.apply_finder_options(options)
|
||||
else
|
||||
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
|
||||
current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ module ActiveRecord
|
||||
|
||||
def initialize
|
||||
super
|
||||
observed_subclasses.each { |klass| add_observer!(klass) }
|
||||
observed_descendants.each { |klass| add_observer!(klass) }
|
||||
end
|
||||
|
||||
def self.method_added(method)
|
||||
@@ -108,8 +108,8 @@ module ActiveRecord
|
||||
|
||||
protected
|
||||
|
||||
def observed_subclasses
|
||||
observed_classes.sum([]) { |klass| klass.send(:descendants) }
|
||||
def observed_descendants
|
||||
observed_classes.sum([]) { |klass| klass.descendants }
|
||||
end
|
||||
|
||||
def observe_callbacks?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace :db do
|
||||
task :load_config => :rails_env do
|
||||
require 'active_record'
|
||||
ActiveRecord::Base.configurations = Rails::Application.config.database_configuration
|
||||
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
||||
end
|
||||
|
||||
namespace :create do
|
||||
|
||||
@@ -489,6 +489,12 @@ module NestedAttributesOnACollectionAssociationTests
|
||||
assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
|
||||
end
|
||||
|
||||
def test_should_not_remove_scheduled_destroys_when_loading_association
|
||||
@pirate.reload
|
||||
@pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
|
||||
assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
|
||||
@child_1.stubs(:id).returns('ABC1X')
|
||||
@child_2.stubs(:id).returns('ABC2X')
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
require 'active_support'
|
||||
require 'active_support/time'
|
||||
require 'active_support/core_ext'
|
||||
require 'active_support/json'
|
||||
|
||||
@@ -432,7 +432,7 @@ module ActiveSupport
|
||||
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
||||
filters.unshift(block) if block
|
||||
|
||||
([self] + self.descendants).each do |target|
|
||||
([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
|
||||
chain = target.send("_#{name}_callbacks")
|
||||
yield chain, type, filters, options
|
||||
target.__define_runner(name)
|
||||
@@ -506,7 +506,7 @@ module ActiveSupport
|
||||
def reset_callbacks(symbol)
|
||||
callbacks = send("_#{symbol}_callbacks")
|
||||
|
||||
self.descendants.each do |target|
|
||||
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
||||
chain = target.send("_#{symbol}_callbacks")
|
||||
callbacks.each { |c| chain.delete(c) }
|
||||
target.__define_runner(symbol)
|
||||
|
||||
@@ -2,54 +2,49 @@ require 'active_support/core_ext/module/anonymous'
|
||||
require 'active_support/core_ext/module/reachable'
|
||||
|
||||
class Class #:nodoc:
|
||||
# Returns an array with the names of the subclasses of +self+ as strings.
|
||||
#
|
||||
# Integer.subclasses # => ["Bignum", "Fixnum"]
|
||||
def subclasses
|
||||
Class.subclasses_of(self).map { |o| o.to_s }
|
||||
end
|
||||
|
||||
# Rubinius
|
||||
if defined?(Class.__subclasses__)
|
||||
alias :subclasses :__subclasses__
|
||||
|
||||
def descendants
|
||||
subclasses = []
|
||||
__subclasses__.each {|k| subclasses << k; subclasses.concat k.descendants }
|
||||
subclasses
|
||||
descendants = []
|
||||
__subclasses__.each do |k|
|
||||
descendants << k
|
||||
descendants.concat k.descendants
|
||||
end
|
||||
descendants
|
||||
end
|
||||
else
|
||||
# MRI
|
||||
else # MRI
|
||||
begin
|
||||
ObjectSpace.each_object(Class.new) {}
|
||||
|
||||
def descendants
|
||||
subclasses = []
|
||||
descendants = []
|
||||
ObjectSpace.each_object(class << self; self; end) do |k|
|
||||
subclasses << k unless k == self
|
||||
descendants.unshift k unless k == self
|
||||
end
|
||||
subclasses
|
||||
descendants
|
||||
end
|
||||
# JRuby
|
||||
rescue StandardError
|
||||
rescue StandardError # JRuby
|
||||
def descendants
|
||||
subclasses = []
|
||||
descendants = []
|
||||
ObjectSpace.each_object(Class) do |k|
|
||||
subclasses << k if k < self
|
||||
descendants.unshift k if k < self
|
||||
end
|
||||
subclasses.uniq!
|
||||
subclasses
|
||||
descendants.uniq!
|
||||
descendants
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Exclude this class unless it's a subclass of our supers and is defined.
|
||||
# We check defined? in case we find a removed class that has yet to be
|
||||
# garbage collected. This also fails for anonymous classes -- please
|
||||
# submit a patch if you have a workaround.
|
||||
def self.subclasses_of(*superclasses) #:nodoc:
|
||||
subclasses = []
|
||||
superclasses.each do |klass|
|
||||
subclasses.concat klass.descendants.select {|k| k.anonymous? || k.reachable?}
|
||||
# Returns an array with the direct children of +self+.
|
||||
#
|
||||
# Integer.subclasses # => [Bignum, Fixnum]
|
||||
def subclasses
|
||||
subclasses, chain = [], descendants
|
||||
chain.each do |k|
|
||||
subclasses << k unless chain.any? { |c| c > k }
|
||||
end
|
||||
subclasses
|
||||
end
|
||||
subclasses
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
require 'date'
|
||||
require 'active_support/duration'
|
||||
require 'active_support/core_ext/time/zones'
|
||||
require 'active_support/core_ext/object/acts_like'
|
||||
require 'active_support/core_ext/date/zones'
|
||||
require 'active_support/core_ext/time/zones'
|
||||
|
||||
class Date
|
||||
if RUBY_VERSION < '1.9'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'date'
|
||||
require 'active_support/inflector'
|
||||
require 'active_support/core_ext/time/calculations'
|
||||
require 'active_support/core_ext/date/zones'
|
||||
|
||||
class Date
|
||||
DATE_FORMATS = {
|
||||
@@ -13,10 +13,10 @@ class Date
|
||||
}
|
||||
|
||||
# Ruby 1.9 has Date#to_time which converts to localtime only.
|
||||
remove_method :to_time if instance_methods.include?(:to_time)
|
||||
remove_method :to_time if method_defined?(:to_time)
|
||||
|
||||
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
|
||||
remove_method :xmlschema if instance_methods.include?(:xmlschema)
|
||||
remove_method :xmlschema if method_defined?(:xmlschema)
|
||||
|
||||
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
|
||||
#
|
||||
@@ -81,16 +81,6 @@ class Date
|
||||
def to_time(form = :local)
|
||||
::Time.send("#{form}_time", year, month, day)
|
||||
end
|
||||
|
||||
# Converts Date to a TimeWithZone in the current zone if Time.zone_default is set,
|
||||
# otherwise converts Date to a Time via Date#to_time
|
||||
def to_time_in_current_zone
|
||||
if ::Time.zone_default
|
||||
::Time.zone.local(year, month, day)
|
||||
else
|
||||
to_time
|
||||
end
|
||||
end
|
||||
|
||||
# Converts a Date instance to a DateTime, where the time is set to the beginning of the day
|
||||
# and UTC offset is set to 0.
|
||||
|
||||
14
activesupport/lib/active_support/core_ext/date/zones.rb
Normal file
14
activesupport/lib/active_support/core_ext/date/zones.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
require 'date'
|
||||
require 'active_support/core_ext/time/zones'
|
||||
|
||||
class Date
|
||||
# Converts Date to a TimeWithZone in the current zone if Time.zone_default is set,
|
||||
# otherwise converts Date to a Time via Date#to_time
|
||||
def to_time_in_current_zone
|
||||
if ::Time.zone_default
|
||||
::Time.zone.local(year, month, day)
|
||||
else
|
||||
to_time
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'rational' unless RUBY_VERSION >= '1.9.2'
|
||||
require 'active_support/core_ext/object/acts_like'
|
||||
require 'active_support/core_ext/time/zones'
|
||||
|
||||
class DateTime
|
||||
class << self
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require 'active_support/inflector'
|
||||
require 'active_support/core_ext/time/conversions'
|
||||
require 'active_support/core_ext/date_time/calculations'
|
||||
require 'active_support/values/time_zone'
|
||||
|
||||
class DateTime
|
||||
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'active_support/core_ext/time/zones'
|
||||
|
||||
class DateTime
|
||||
# Returns the simultaneous time in <tt>Time.zone</tt>.
|
||||
#
|
||||
|
||||
@@ -6,9 +6,9 @@ require 'active_support/core_ext/object/try'
|
||||
require 'active_support/core_ext/object/conversions'
|
||||
require 'active_support/core_ext/object/instance_variables'
|
||||
require 'active_support/core_ext/object/misc'
|
||||
require 'active_support/core_ext/object/extending'
|
||||
|
||||
require 'active_support/core_ext/object/returning'
|
||||
require 'active_support/core_ext/object/to_json'
|
||||
require 'active_support/core_ext/object/to_param'
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'active_support/core_ext/object/with_options'
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
require 'active_support/core_ext/class/subclasses'
|
||||
|
||||
class Object
|
||||
# Exclude this class unless it's a subclass of our supers and is defined.
|
||||
# We check defined? in case we find a removed class that has yet to be
|
||||
# garbage collected. This also fails for anonymous classes -- please
|
||||
# submit a patch if you have a workaround.
|
||||
def subclasses_of(*superclasses) #:nodoc:
|
||||
Class.subclasses_of(*superclasses)
|
||||
end
|
||||
end
|
||||
19
activesupport/lib/active_support/core_ext/object/to_json.rb
Normal file
19
activesupport/lib/active_support/core_ext/object/to_json.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# Hack to load json gem first so we can overwrite its to_json.
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
|
||||
# their default behavior. That said, we need to define the basic to_json method in all of them,
|
||||
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
||||
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
||||
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
|
||||
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
|
||||
klass.class_eval <<-RUBY, __FILE__, __LINE__
|
||||
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
|
||||
def to_json(options = nil)
|
||||
ActiveSupport::JSON.encode(self, options)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
@@ -40,6 +40,11 @@ class Time
|
||||
def local_time(*args)
|
||||
time_with_datetime_fallback(:local, *args)
|
||||
end
|
||||
|
||||
# Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
|
||||
def current
|
||||
::Time.zone_default ? ::Time.zone.now : ::Time.now
|
||||
end
|
||||
end
|
||||
|
||||
# Tells whether the Time object's time lies in the past
|
||||
|
||||
@@ -41,11 +41,6 @@ class Time
|
||||
::Time.zone = old_zone
|
||||
end
|
||||
|
||||
# Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
|
||||
def current
|
||||
::Time.zone_default ? ::Time.zone.now : ::Time.now
|
||||
end
|
||||
|
||||
private
|
||||
def get_zone(time_zone)
|
||||
return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require "active_support/notifications"
|
||||
require "active_support/core_ext/array/wrap"
|
||||
|
||||
module ActiveSupport
|
||||
module Deprecation
|
||||
@@ -7,11 +8,11 @@ module ActiveSupport
|
||||
attr_accessor :debug
|
||||
|
||||
def behavior
|
||||
@behavior ||= DEFAULT_BEHAVIORS[:stderr]
|
||||
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
|
||||
end
|
||||
|
||||
def behavior=(behavior)
|
||||
@behavior = DEFAULT_BEHAVIORS[behavior] || behavior
|
||||
@behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ module ActiveSupport
|
||||
attr_accessor :silenced
|
||||
|
||||
def warn(message = nil, callstack = caller)
|
||||
if behavior && !silenced
|
||||
behavior.call(deprecation_message(callstack, message), callstack)
|
||||
return if silenced
|
||||
deprecation_message(callstack, message).tap do |m|
|
||||
behavior.each { |b| b.call(m, callstack) }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,16 +4,23 @@ module ActiveSupport
|
||||
# This module provides an internal implementation to track descendants
|
||||
# which is faster than iterating through ObjectSpace.
|
||||
module DescendantsTracker
|
||||
@@descendants = Hash.new { |h, k| h[k] = [] }
|
||||
@@direct_descendants = Hash.new { |h, k| h[k] = [] }
|
||||
|
||||
def self.descendants
|
||||
@@descendants
|
||||
def self.direct_descendants(klass)
|
||||
@@direct_descendants[klass]
|
||||
end
|
||||
|
||||
def self.descendants(klass)
|
||||
@@direct_descendants[klass].inject([]) do |descendants, klass|
|
||||
descendants << klass
|
||||
descendants.concat klass.descendants
|
||||
end
|
||||
end
|
||||
|
||||
def self.clear
|
||||
@@descendants.each do |klass, descendants|
|
||||
@@direct_descendants.each do |klass, descendants|
|
||||
if ActiveSupport::Dependencies.autoloaded?(klass)
|
||||
@@descendants.delete(klass)
|
||||
@@direct_descendants.delete(klass)
|
||||
else
|
||||
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
|
||||
end
|
||||
@@ -26,14 +33,11 @@ module ActiveSupport
|
||||
end
|
||||
|
||||
def direct_descendants
|
||||
@@descendants[self]
|
||||
DescendantsTracker.direct_descendants(self)
|
||||
end
|
||||
|
||||
def descendants
|
||||
@@descendants[self].inject([]) do |descendants, klass|
|
||||
descendants << klass
|
||||
descendants.concat klass.descendants
|
||||
end
|
||||
DescendantsTracker.descendants(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,15 @@
|
||||
module ActiveSupport
|
||||
# This class is responsible to track files and invoke the given block
|
||||
# whenever one of these files are changed. For example, this class
|
||||
# is used by Rails to reload routes whenever they are changed upon
|
||||
# a new request.
|
||||
# is used by Rails to reload the I18n framework whenever they are
|
||||
# changed upon a new request.
|
||||
#
|
||||
# routes_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
|
||||
# paths.each { |p| load(p) }
|
||||
# Rails::Application.routes.reload!
|
||||
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
|
||||
# I18n.reload!
|
||||
# end
|
||||
#
|
||||
# ActionDispatch::Callbacks.to_prepare do
|
||||
# routes_reloader.execute_if_updated
|
||||
# i18n_reloader.execute_if_updated
|
||||
# end
|
||||
#
|
||||
class FileUpdateChecker
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require "active_support"
|
||||
require "rails"
|
||||
require "active_support/file_update_checker"
|
||||
require "active_support/core_ext/array/wrap"
|
||||
|
||||
module I18n
|
||||
class Railtie < Rails::Railtie
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
# encoding: utf-8
|
||||
require 'active_support/core_ext/object/to_json'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/deprecation'
|
||||
require 'active_support/json/variable'
|
||||
|
||||
require 'bigdecimal'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/object/instance_variables'
|
||||
require 'active_support/deprecation'
|
||||
|
||||
require 'active_support/time'
|
||||
|
||||
# Hack to load json gem first so we can overwrite its to_json.
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
end
|
||||
require 'time'
|
||||
require 'active_support/core_ext/time/conversions'
|
||||
require 'active_support/core_ext/date_time/conversions'
|
||||
require 'active_support/core_ext/date/conversions'
|
||||
|
||||
module ActiveSupport
|
||||
class << self
|
||||
@@ -128,20 +126,6 @@ module ActiveSupport
|
||||
end
|
||||
end
|
||||
|
||||
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
|
||||
# their default behavior. That said, we need to define the basic to_json method in all of them,
|
||||
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
||||
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
||||
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
|
||||
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
|
||||
klass.class_eval <<-RUBY, __FILE__, __LINE__
|
||||
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
|
||||
def to_json(options = nil)
|
||||
ActiveSupport::JSON.encode(self, options)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
class Object
|
||||
def as_json(options = nil) #:nodoc:
|
||||
if respond_to?(:to_hash)
|
||||
@@ -152,12 +136,6 @@ class Object
|
||||
end
|
||||
end
|
||||
|
||||
# A string that returns itself as its JSON-encoded form.
|
||||
class ActiveSupport::JSON::Variable < String
|
||||
def as_json(options = nil) self end #:nodoc:
|
||||
def encode_json(encoder) self end #:nodoc:
|
||||
end
|
||||
|
||||
class TrueClass
|
||||
AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
|
||||
def as_json(options = nil) AS_JSON end #:nodoc:
|
||||
|
||||
@@ -2,10 +2,8 @@ module ActiveSupport
|
||||
module JSON
|
||||
# A string that returns itself as its JSON-encoded form.
|
||||
class Variable < String
|
||||
private
|
||||
def rails_to_json(*)
|
||||
self
|
||||
end
|
||||
def as_json(options = nil) self end #:nodoc:
|
||||
def encode_json(encoder) self end #:nodoc:
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,6 +24,7 @@ require 'active_support/core_ext/date/acts_like'
|
||||
require 'active_support/core_ext/date/freeze'
|
||||
require 'active_support/core_ext/date/calculations'
|
||||
require 'active_support/core_ext/date/conversions'
|
||||
require 'active_support/core_ext/date/zones'
|
||||
|
||||
require 'active_support/core_ext/date_time/acts_like'
|
||||
require 'active_support/core_ext/date_time/calculations'
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/object/try'
|
||||
begin
|
||||
require 'tzinfo'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
|
||||
raise e
|
||||
end
|
||||
|
||||
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
|
||||
#
|
||||
@@ -201,6 +195,12 @@ module ActiveSupport
|
||||
# (GMT). Seconds were chosen as the offset unit because that is the unit that
|
||||
# Ruby uses to represent time zone offsets (see Time#utc_offset).
|
||||
def initialize(name, utc_offset = nil, tzinfo = nil)
|
||||
begin
|
||||
require 'tzinfo'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
|
||||
raise e
|
||||
end
|
||||
@name = name
|
||||
@utc_offset = utc_offset
|
||||
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
require 'abstract_unit'
|
||||
require 'active_support/core_ext/class'
|
||||
|
||||
class A
|
||||
end
|
||||
|
||||
module X
|
||||
class B
|
||||
end
|
||||
end
|
||||
|
||||
module Y
|
||||
module Z
|
||||
class C
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ClassTest < Test::Unit::TestCase
|
||||
def test_retrieving_subclasses
|
||||
@parent = eval("class D; end; D")
|
||||
@sub = eval("class E < D; end; E")
|
||||
@subofsub = eval("class F < E; end; F")
|
||||
assert_equal 2, @parent.subclasses.size
|
||||
assert_equal [@subofsub.to_s], @sub.subclasses
|
||||
assert_equal [], @subofsub.subclasses
|
||||
assert_equal [@sub.to_s, @subofsub.to_s].sort, @parent.subclasses.sort
|
||||
class Parent; end
|
||||
class Foo < Parent; end
|
||||
class Bar < Foo; end
|
||||
class Baz < Bar; end
|
||||
|
||||
class A < Parent; end
|
||||
class B < A; end
|
||||
class C < B; end
|
||||
|
||||
def test_descendants
|
||||
assert_equal [Foo, Bar, Baz, A, B, C], Parent.descendants
|
||||
assert_equal [Bar, Baz], Foo.descendants
|
||||
assert_equal [Baz], Bar.descendants
|
||||
assert_equal [], Baz.descendants
|
||||
end
|
||||
end
|
||||
|
||||
def test_subclasses
|
||||
assert_equal [Foo, A], Parent.subclasses
|
||||
assert_equal [Bar], Foo.subclasses
|
||||
assert_equal [Baz], Bar.subclasses
|
||||
assert_equal [], Baz.subclasses
|
||||
end
|
||||
end
|
||||
@@ -40,55 +40,6 @@ class Foo
|
||||
include Bar
|
||||
end
|
||||
|
||||
class ClassExtTest < Test::Unit::TestCase
|
||||
def test_subclasses_of_should_find_nested_classes
|
||||
assert Class.subclasses_of(ClassK).include?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_should_not_return_removed_classes
|
||||
# First create the removed class
|
||||
old_class = Nested.class_eval { remove_const :ClassL }
|
||||
new_class = Class.new(ClassK)
|
||||
Nested.const_set :ClassL, new_class
|
||||
assert_equal "Nested::ClassL", new_class.name # Sanity check
|
||||
|
||||
subclasses = Class.subclasses_of(ClassK)
|
||||
assert subclasses.include?(new_class)
|
||||
assert ! subclasses.include?(old_class)
|
||||
ensure
|
||||
Nested.const_set :ClassL, old_class unless defined?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_should_not_trigger_const_missing
|
||||
const_missing = false
|
||||
Nested.on_const_missing { const_missing = true }
|
||||
|
||||
subclasses = Class.subclasses_of ClassK
|
||||
assert !const_missing
|
||||
assert_equal [ Nested::ClassL ], subclasses
|
||||
|
||||
removed = Nested.class_eval { remove_const :ClassL } # keep it in memory
|
||||
subclasses = Class.subclasses_of ClassK
|
||||
assert !const_missing
|
||||
assert subclasses.empty?
|
||||
ensure
|
||||
Nested.const_set :ClassL, removed unless defined?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_with_multiple_roots
|
||||
classes = Class.subclasses_of(ClassI, ClassK)
|
||||
assert_equal %w(ClassJ Nested::ClassL), classes.collect(&:to_s).sort
|
||||
end
|
||||
|
||||
def test_subclasses_of_doesnt_find_anonymous_classes
|
||||
assert_equal [], Class.subclasses_of(Foo)
|
||||
bar = Class.new(Foo)
|
||||
assert_nothing_raised do
|
||||
assert_equal [bar], Class.subclasses_of(Foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ObjectTests < ActiveSupport::TestCase
|
||||
class DuckTime
|
||||
def acts_like_time?
|
||||
|
||||
@@ -80,6 +80,19 @@ class DeprecationTest < ActiveSupport::TestCase
|
||||
assert_deprecated(/foo=nil/) { @dtc.partially }
|
||||
end
|
||||
|
||||
def test_several_behaviors
|
||||
@a, @b = nil, nil
|
||||
|
||||
ActiveSupport::Deprecation.behavior = [
|
||||
Proc.new { |msg, callstack| @a = msg },
|
||||
Proc.new { |msg, callstack| @b = msg }
|
||||
]
|
||||
|
||||
@dtc.partially
|
||||
assert_match(/foo=nil/, @a)
|
||||
assert_match(/foo=nil/, @b)
|
||||
end
|
||||
|
||||
def test_deprecated_instance_variable_proxy
|
||||
assert_not_deprecated { @dtc.request.size }
|
||||
|
||||
@@ -123,6 +136,13 @@ class DeprecationTest < ActiveSupport::TestCase
|
||||
assert_equal 123, result
|
||||
end
|
||||
|
||||
def test_assert_deprecated_warn_work_with_default_behavior
|
||||
ActiveSupport::Deprecation.instance_variable_set('@behavior' , nil)
|
||||
assert_deprecated('abc') do
|
||||
ActiveSupport::Deprecation.warn 'abc'
|
||||
end
|
||||
end
|
||||
|
||||
def test_silence
|
||||
ActiveSupport::Deprecation.silence do
|
||||
assert_not_deprecated { @dtc.partially }
|
||||
|
||||
@@ -37,7 +37,9 @@ class DescendantsTrackerTest < Test::Unit::TestCase
|
||||
def test_clear_with_autoloaded_parent_children_and_granchildren
|
||||
mark_as_autoloaded(*ALL) do
|
||||
ActiveSupport::DescendantsTracker.clear
|
||||
assert ActiveSupport::DescendantsTracker.descendants.slice(*ALL).empty?
|
||||
ALL.each do |k|
|
||||
assert ActiveSupport::DescendantsTracker.descendants(k).empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -64,12 +66,12 @@ class DescendantsTrackerTest < Test::Unit::TestCase
|
||||
old_autoloaded = ActiveSupport::Dependencies.autoloaded_constants.dup
|
||||
ActiveSupport::Dependencies.autoloaded_constants = klasses.map(&:name)
|
||||
|
||||
old_descendants = ActiveSupport::DescendantsTracker.descendants.dup
|
||||
old_descendants = ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").dup
|
||||
old_descendants.each { |k, v| old_descendants[k] = v.dup }
|
||||
|
||||
yield
|
||||
ensure
|
||||
ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded
|
||||
ActiveSupport::DescendantsTracker.descendants.replace(old_descendants)
|
||||
ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").replace(old_descendants)
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,5 @@
|
||||
# encoding: utf-8
|
||||
require 'abstract_unit'
|
||||
require 'bigdecimal'
|
||||
require 'active_support/core_ext/big_decimal/conversions'
|
||||
require 'active_support/json'
|
||||
|
||||
class TestJSONEncoding < Test::Unit::TestCase
|
||||
@@ -138,6 +136,10 @@ class TestJSONEncoding < Test::Unit::TestCase
|
||||
ActiveSupport.use_standard_json_time_format = false
|
||||
end
|
||||
|
||||
def test_hash_with_time_to_json
|
||||
assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json
|
||||
end
|
||||
|
||||
def test_nested_hash_with_float
|
||||
assert_nothing_raised do
|
||||
hash = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'abstract_unit'
|
||||
require 'active_support/json'
|
||||
require 'active_support/core_ext/object/to_json'
|
||||
|
||||
class OrderedHashTest < Test::Unit::TestCase
|
||||
def setup
|
||||
|
||||
@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
|
||||
s.add_dependency('activeresource', version)
|
||||
s.add_dependency('actionmailer', version)
|
||||
s.add_dependency('railties', version)
|
||||
s.add_dependency('bundler', '>= 0.9.26')
|
||||
s.add_dependency('bundler', '>= 1.0.0.beta.2')
|
||||
end
|
||||
|
||||
@@ -387,40 +387,6 @@ TIP: Since +with_options+ forwards calls to its receiver they can be nested. Eac
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/object/with_options.rb+.
|
||||
|
||||
h5. +subclasses_of+
|
||||
|
||||
The method +subclasses_of+ receives an arbitrary number of class objects and returns all their anonymous or reachable descendants as a single array:
|
||||
|
||||
<ruby>
|
||||
class C; end
|
||||
subclasses_of(C) # => []
|
||||
|
||||
subclasses_of(Integer) # => [Bignum, Fixnum]
|
||||
|
||||
module M
|
||||
class A; end
|
||||
class B1 < A; end
|
||||
class B2 < A; end
|
||||
end
|
||||
|
||||
module N
|
||||
class C < M::B1; end
|
||||
end
|
||||
|
||||
subclasses_of(M::A) # => [N::C, M::B2, M::B1]
|
||||
</ruby>
|
||||
|
||||
The order in which these classes are returned is unspecified. The returned collection may have duplicates:
|
||||
|
||||
<ruby>
|
||||
subclasses_of(Numeric, Integer)
|
||||
# => [Bignum, Float, Fixnum, Integer, Date::Infinity, Rational, BigDecimal, Bignum, Fixnum]
|
||||
</ruby>
|
||||
|
||||
See also +Class#subclasses+ in "Extensions to +Class+ FIX THIS LINK":FIXME.
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/object/extending.rb+.
|
||||
|
||||
h4. Instance Variables
|
||||
|
||||
Active Support provides several methods to ease access to instance variables.
|
||||
@@ -1141,36 +1107,47 @@ If for whatever reason an application loads the definition of a mailer class and
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/class/delegating_attributes.rb+.
|
||||
|
||||
h4. Descendants
|
||||
h4. Descendants & Subclasses
|
||||
|
||||
h5. +subclasses+
|
||||
h5. +descendants+
|
||||
|
||||
The +subclasses+ method returns the names of all the anonymous or reachable descendants of its receiver as an array of strings:
|
||||
The +descendants+ method returns all classes, including its children, that inherits from self.
|
||||
|
||||
<ruby>
|
||||
class C; end
|
||||
C.subclasses # => []
|
||||
C.descendants #=> []
|
||||
|
||||
Integer.subclasses # => ["Bignum", "Fixnum"]
|
||||
class B < C; end
|
||||
C.descendants #=> [B]
|
||||
|
||||
module M
|
||||
class A; end
|
||||
class B1 < A; end
|
||||
class B2 < A; end
|
||||
end
|
||||
class A < B; end
|
||||
C.descendants #=> [B, A]
|
||||
|
||||
module N
|
||||
class C < M::B1; end
|
||||
end
|
||||
|
||||
M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
|
||||
class D < C; end
|
||||
C.descendants #=> [B, A, D]
|
||||
</ruby>
|
||||
|
||||
The order in which these class names are returned is unspecified.
|
||||
h5. +subclasses+
|
||||
|
||||
See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
|
||||
The +subclasses+ method returns all direct classes that inherits from self.
|
||||
|
||||
WARNING: This method is redefined in some Rails core classes.
|
||||
<ruby>
|
||||
class C; end
|
||||
C.subclasses #=> []
|
||||
|
||||
class B < C; end
|
||||
C.subclasses #=> [B]
|
||||
|
||||
class A < B; end
|
||||
C.subclasses #=> [B]
|
||||
|
||||
class D < C; end
|
||||
C.subclasses #=> [B, D]
|
||||
</ruby>
|
||||
|
||||
The order in which these class are returned is unspecified.
|
||||
|
||||
WARNING: This method is redefined in some Rails core classes but should be all compatible in Rails 3.1.
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/class/subclasses.rb+.
|
||||
|
||||
|
||||
@@ -137,6 +137,16 @@ end
|
||||
|
||||
!images/has_many_through.png(has_many :through Association Diagram)!
|
||||
|
||||
The collection of join models can be managed via the API. For example, if you assign
|
||||
|
||||
<ruby>
|
||||
physician.patients = patients
|
||||
</ruby>
|
||||
|
||||
new join models are created for newly associated objects, and if some are gone their rows are deleted.
|
||||
|
||||
WARNING: Automatic deletion of join models is direct, no destroy callbacks are triggered.
|
||||
|
||||
The +has_many :through+ association is also useful for setting up "shortcuts" through nested +has_many+ associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way:
|
||||
|
||||
<ruby>
|
||||
|
||||
@@ -11,7 +11,7 @@ This guide first describes the process of +rails server+ then explains the Passe
|
||||
|
||||
h3. Launch!
|
||||
|
||||
As of Rails 3, +script/server+ has become +rails server+. This was done to centralise all rails related commands to one common file.
|
||||
As of Rails 3, +script/server+ has become +rails server+. This was done to centralize all rails related commands to one common file.
|
||||
|
||||
The actual +rails+ command is kept in _railties/bin/rails_ and goes like this:
|
||||
|
||||
@@ -58,11 +58,8 @@ In +script/rails+ we see the following:
|
||||
#!/usr/bin/env ruby
|
||||
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
||||
|
||||
ENV_PATH = File.expand_path('../../config/environment', __FILE__)
|
||||
BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
|
||||
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
||||
|
||||
require BOOT_PATH
|
||||
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'rails/commands'
|
||||
</ruby>
|
||||
|
||||
@@ -79,15 +76,19 @@ h3. _config/boot.rb_
|
||||
_config/boot.rb_ is the first stop for everything for initializing your application. This boot process does quite a bit of work for you and so this section attempts to go in-depth enough to explain what each of the pieces does.
|
||||
|
||||
<ruby>
|
||||
# Use Bundler (preferred)
|
||||
require 'rubygems'
|
||||
|
||||
# Set up gems listed in the Gemfile.
|
||||
gemfile = File.expand_path('../../Gemfile', __FILE__)
|
||||
begin
|
||||
require File.expand_path('../../.bundle/environment', __FILE__)
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
ENV['BUNDLE_GEMFILE'] = gemfile
|
||||
require 'bundler'
|
||||
Bundler.setup
|
||||
end
|
||||
|
||||
rescue Bundler::GemNotFound => e
|
||||
STDERR.puts e.message
|
||||
STDERR.puts "Try running `bundle install`."
|
||||
exit!
|
||||
end if File.exist?(gemfile)
|
||||
</ruby>
|
||||
|
||||
h3. Bundled Rails (3.x)
|
||||
@@ -140,8 +141,8 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
|
||||
* activesupport-3.0.0.beta4.gem
|
||||
* arel-0.4.0.gem
|
||||
* builder-2.1.2.gem
|
||||
* bundler-0.9.26.gem
|
||||
* erubis-2.6.5.gem
|
||||
* bundler-1.0.0.beta.2.gem
|
||||
* erubis-2.6.6.gem
|
||||
* i18n-0.4.1.gem
|
||||
* mail-2.2.4.gem
|
||||
* memcache-client-1.8.3.gem
|
||||
@@ -156,7 +157,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
|
||||
* sqlite3-ruby-1.3.0.gem
|
||||
* text-format-1.0.0.gem
|
||||
* text-hyphen-1.0.0.gem
|
||||
* thor-0.13.6.gem
|
||||
* thor-0.13.7.gem
|
||||
* treetop-1.4.8.gem
|
||||
* tzinfo-0.3.22.gem
|
||||
|
||||
@@ -164,33 +165,7 @@ TODO: Prettify when it becomes more stable.
|
||||
|
||||
I won't go into what each of these gems are, as that is really something that needs covering on a case-by-case basis. We will however just dig a little under the surface of Bundler.
|
||||
|
||||
Back in _config/boot.rb_, the first line will try to include _.bundle/environment.rb_, which doesn't exist in a bare-bones Rails application and because this file does not exist Ruby will raise a +LoadError+ which will be rescued and run the following code:
|
||||
|
||||
<ruby>
|
||||
require 'rubygems'
|
||||
require 'bundler'
|
||||
Bundler.setup
|
||||
</ruby>
|
||||
|
||||
+Bundler.setup+ here will load and parse the +Gemfile+ and add the _lib_ directory of the gems mentioned **and** their dependencies (**and** their dependencies' dependencies, and so on) to the +$LOAD_PATH+.
|
||||
|
||||
Now we will go down the alternate timeline where we generate a _.bundle/environment.rb_ file using the +bundle lock+ command. This command also creates a _Gemfile.lock_ file which is actually a YAML file loaded by this method in Bundler before it moves on to check for _Gemfile_:
|
||||
|
||||
<ruby>
|
||||
def definition(gemfile = default_gemfile)
|
||||
configure
|
||||
root = Pathname.new(gemfile).dirname
|
||||
lockfile = root.join("Gemfile.lock")
|
||||
if lockfile.exist?
|
||||
Definition.from_lock(lockfile)
|
||||
else
|
||||
Definition.from_gemfile(gemfile)
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
|
||||
The _.bundle/environment.rb_ file adds the _lib_ directory of all the gems specified in +Gemfile.lock+ to +$LOAD_PATH+.
|
||||
Back in _config/boot.rb_, we call +Bundler.setup+ which will load and parse the +Gemfile+ and add the _lib_ directory of the gems mentioned **and** their dependencies (**and** their dependencies' dependencies, and so on) to the +$LOAD_PATH+.
|
||||
|
||||
h3. Requiring Rails
|
||||
|
||||
@@ -326,6 +301,11 @@ As you can see for the duration of the +eager_autoload+ block the class variable
|
||||
module ActiveSupport
|
||||
extend ActiveSupport::Autoload
|
||||
|
||||
autoload :DescendantsTracker
|
||||
autoload :FileUpdateChecker
|
||||
autoload :LogSubscriber
|
||||
autoload :Notifications
|
||||
|
||||
# TODO: Narrow this list down
|
||||
eager_autoload do
|
||||
autoload :BacktraceCleaner
|
||||
@@ -348,7 +328,6 @@ As you can see for the duration of the +eager_autoload+ block the class variable
|
||||
autoload :OptionMerger
|
||||
autoload :OrderedHash
|
||||
autoload :OrderedOptions
|
||||
autoload :Notifications
|
||||
autoload :Rescuable
|
||||
autoload :SecureRandom
|
||||
autoload :StringInquirer
|
||||
@@ -589,19 +568,20 @@ This file (_railties/lib/rails.rb_) requires the very, very basics that Rails ne
|
||||
require 'action_dispatch/railtie'
|
||||
</ruby>
|
||||
|
||||
+require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+ so that instead of doing:
|
||||
|
||||
<ruby>
|
||||
File.join(Rails.root, "app/controllers")
|
||||
</ruby>
|
||||
|
||||
You may do:
|
||||
+require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+. Although is coming to use this path name to generate paths as below:
|
||||
|
||||
<ruby>
|
||||
Rails.root.join("app/controllers")
|
||||
</ruby>
|
||||
|
||||
Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out.
|
||||
Pathname can also be converted to string, so the following syntax is preferred:
|
||||
|
||||
<ruby>
|
||||
"#{Rails.root}/app/controllers"
|
||||
</ruby>
|
||||
|
||||
|
||||
This works because Ruby automatically handles file path conversions. Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out.
|
||||
|
||||
Inside this file there are other helpful helper methods defined, such as +Rails.root+, +Rails.env+, +Rails.logger+ and +Rails.application+.
|
||||
|
||||
@@ -1833,7 +1813,7 @@ We do not already have a +Rails.application+, so instead this resorts to calling
|
||||
end
|
||||
</ruby>
|
||||
|
||||
This +called_from+ setting looks a little overwhelming to begin with, but the short end of it is that it returns the route to your application's config directory, something like: _/home/you/yourapp/config_. After +called_from+ has been set, +super+ is again called and this means the +Rails::Railtie#inherited+ method (in _railties/lib/rails/railtie.rb_):
|
||||
This +called_from+ setting looks a little overwhelming to begin with, but the short end of it is that it returns your application's root, something like: _/home/you/yourapp_. After +called_from+ has been set, +super+ is again called and this means the +Rails::Railtie#inherited+ method (in _railties/lib/rails/railtie.rb_):
|
||||
|
||||
<ruby>
|
||||
def inherited(base)
|
||||
|
||||
@@ -382,6 +382,12 @@ match ':controller/:action/:id/:user_id'
|
||||
|
||||
An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+.
|
||||
|
||||
NOTE: You can't use +namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g:
|
||||
|
||||
<ruby>
|
||||
match ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/
|
||||
</ruby>
|
||||
|
||||
h4. Static Segments
|
||||
|
||||
You can specify static segments when creating a route:
|
||||
|
||||
@@ -8,14 +8,6 @@ module Rails
|
||||
# In Rails 3.0, a Rails::Application object was introduced which is nothing more than
|
||||
# an Engine but with the responsibility of coordinating the whole boot process.
|
||||
#
|
||||
# Opposite to Rails::Engine, you can only have one Rails::Application instance
|
||||
# in your process and both Rails::Application and YourApplication::Application
|
||||
# points to it.
|
||||
#
|
||||
# In other words, Rails::Application is Singleton and whenever you are accessing
|
||||
# Rails::Application.config or YourApplication::Application.config, you are actually
|
||||
# accessing YourApplication::Application.instance.config.
|
||||
#
|
||||
# == Initialization
|
||||
#
|
||||
# Rails::Application is responsible for executing all railties, engines and plugin
|
||||
@@ -57,6 +49,10 @@ module Rails
|
||||
|
||||
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
|
||||
@@ -125,14 +121,13 @@ module Rails
|
||||
end
|
||||
|
||||
def reload_routes!
|
||||
routes = Rails::Application.routes
|
||||
routes.disable_clear_and_finalize = true
|
||||
|
||||
routes.clear!
|
||||
_routes = self.routes
|
||||
_routes.disable_clear_and_finalize = true
|
||||
_routes.clear!
|
||||
routes_reloader.paths.each { |path| load(path) }
|
||||
ActiveSupport.on_load(:action_controller) { routes.finalize! }
|
||||
ActiveSupport.on_load(:action_controller) { _routes.finalize! }
|
||||
ensure
|
||||
routes.disable_clear_and_finalize = false
|
||||
_routes.disable_clear_and_finalize = false
|
||||
end
|
||||
|
||||
def initialize!
|
||||
|
||||
@@ -13,27 +13,27 @@ command = aliases[command] || command
|
||||
case command
|
||||
when 'generate', 'destroy', 'plugin', 'benchmarker', 'profiler'
|
||||
require APP_PATH
|
||||
Rails::Application.require_environment!
|
||||
Rails.application.require_environment!
|
||||
require "rails/commands/#{command}"
|
||||
|
||||
when 'console'
|
||||
require 'rails/commands/console'
|
||||
require APP_PATH
|
||||
Rails::Application.require_environment!
|
||||
Rails::Console.start(Rails::Application)
|
||||
Rails.application.require_environment!
|
||||
Rails::Console.start(Rails.application)
|
||||
|
||||
when 'server'
|
||||
require 'rails/commands/server'
|
||||
Rails::Server.new.tap { |server|
|
||||
require APP_PATH
|
||||
Dir.chdir(Rails::Application.root)
|
||||
Dir.chdir(Rails.application.root)
|
||||
server.start
|
||||
}
|
||||
|
||||
when 'dbconsole'
|
||||
require 'rails/commands/dbconsole'
|
||||
require APP_PATH
|
||||
Rails::DBConsole.start(Rails::Application)
|
||||
Rails::DBConsole.start(Rails.application)
|
||||
|
||||
when 'application', 'runner'
|
||||
require "rails/commands/#{command}"
|
||||
|
||||
@@ -4,7 +4,7 @@ if %w(--version -v).include? ARGV.first
|
||||
exit(0)
|
||||
end
|
||||
|
||||
if ARGV.first != "new" || ARGV.empty?
|
||||
if ARGV.first != "new"
|
||||
ARGV[0] = "--help"
|
||||
else
|
||||
ARGV.shift
|
||||
|
||||
@@ -37,7 +37,7 @@ ARGV.delete(code_or_file)
|
||||
ENV["RAILS_ENV"] = options[:environment]
|
||||
|
||||
require APP_PATH
|
||||
Rails::Application.require_environment!
|
||||
Rails.application.require_environment!
|
||||
|
||||
begin
|
||||
if code_or_file.nil?
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
require 'rake'
|
||||
|
||||
Rails::Application.load_tasks
|
||||
<%= app_const %>.load_tasks
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
# If you change this key, all old signed cookies will become invalid!
|
||||
# Make sure the secret is at least 30 characters and all random,
|
||||
# no regular words or you'll be exposed to dictionary attacks.
|
||||
Rails.application.config.secret_token = '<%= app_secret %>'
|
||||
<%= app_const %>.config.secret_token = '<%= app_secret %>'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
Rails.application.config.session_store :cookie_store, :key => '_<%= app_name %>_session'
|
||||
<%= app_const %>.config.session_store :cookie_store, :key => '_<%= app_name %>_session'
|
||||
|
||||
# Use the database for sessions instead of the cookie-based default,
|
||||
# which shouldn't be used to store highly confidential information
|
||||
# (create the session table with "rake db:sessions:create")
|
||||
# Rails.application.config.session_store :active_record_store
|
||||
# <%= app_const %>.config.session_store :active_record_store
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Rails.application.routes.draw do |map|
|
||||
Rails.application.routes.draw do
|
||||
match '/rails/info/properties' => "rails/info#properties"
|
||||
end
|
||||
|
||||
@@ -3,5 +3,5 @@ task :middleware => :environment do
|
||||
Rails.configuration.middleware.each do |middleware|
|
||||
puts "use #{middleware.inspect}"
|
||||
end
|
||||
puts "run #{Rails::Application.instance.class.name}.routes"
|
||||
puts "run #{Rails.application.class.name}.routes"
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
|
||||
task :routes => :environment do
|
||||
Rails::Application.reload_routes!
|
||||
Rails.application.reload_routes!
|
||||
all_routes = ENV['CONTROLLER'] ? Rails.application.routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : Rails.application.routes.routes
|
||||
routes = all_routes.collect do |route|
|
||||
# TODO: The :index method is deprecated in 1.9 in favor of :key
|
||||
|
||||
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
||||
s.has_rdoc = false
|
||||
|
||||
s.add_dependency('rake', '>= 0.8.3')
|
||||
s.add_dependency('thor', '~> 0.13.6')
|
||||
s.add_dependency('thor', '~> 0.13.7')
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('actionpack', version)
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ module ApplicationTests
|
||||
|
||||
test "allows me to configure default url options for ActionMailer" do
|
||||
app_file "config/environments/development.rb", <<-RUBY
|
||||
Rails::Application.configure do
|
||||
AppTemplate::Application.configure do
|
||||
config.action_mailer.default_url_options = { :host => "test.rails" }
|
||||
end
|
||||
RUBY
|
||||
@@ -45,6 +45,25 @@ module ApplicationTests
|
||||
assert_equal "test.rails", ActionMailer::Base.default_url_options[:host]
|
||||
end
|
||||
|
||||
test "does not include url helpers as action methods" do
|
||||
app_file "config/routes.rb", <<-RUBY
|
||||
AppTemplate::Application.routes.draw do
|
||||
get "/foo", :to => lambda { |env| [200, {}, []] }, :as => :foo
|
||||
end
|
||||
RUBY
|
||||
|
||||
app_file "app/mailers/foo.rb", <<-RUBY
|
||||
class Foo < ActionMailer::Base
|
||||
def notify
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
assert Foo.method_defined?(:foo_path)
|
||||
assert_equal ["notify"], Foo.action_methods
|
||||
end
|
||||
|
||||
# AS
|
||||
test "if there's no config.active_support.bare, all of ActiveSupport is required" do
|
||||
use_frameworks []
|
||||
|
||||
@@ -16,7 +16,7 @@ module ApplicationTests
|
||||
end
|
||||
|
||||
def app
|
||||
@app ||= Rails::Application
|
||||
@app ||= Rails.application
|
||||
end
|
||||
|
||||
def assert_fallbacks(fallbacks)
|
||||
|
||||
@@ -24,11 +24,11 @@ module ApplicationTests
|
||||
app_file "config/environment.rb", <<-RUBY
|
||||
SuperMiddleware = Struct.new(:app)
|
||||
|
||||
Rails::Application.configure do
|
||||
AppTemplate::Application.configure do
|
||||
config.middleware.use SuperMiddleware
|
||||
end
|
||||
|
||||
Rails::Application.initialize!
|
||||
AppTemplate::Application.initialize!
|
||||
RUBY
|
||||
|
||||
assert_match "SuperMiddleware", Dir.chdir(app_path){ `rake middleware` }
|
||||
|
||||
@@ -19,7 +19,7 @@ module ApplicationTests
|
||||
end
|
||||
|
||||
def test_should_run_ruby_statement
|
||||
assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "puts User.count"` }
|
||||
assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "puts User.count"` }
|
||||
end
|
||||
|
||||
def test_should_run_file
|
||||
|
||||
@@ -11,7 +11,7 @@ class InfoControllerTest < ActionController::TestCase
|
||||
tests Rails::InfoController
|
||||
|
||||
def setup
|
||||
Rails.application.routes.draw do |map|
|
||||
Rails.application.routes.draw do
|
||||
match '/rails/info/properties' => "rails/info#properties"
|
||||
end
|
||||
@controller.stubs(:consider_all_requests_local? => false, :local_request? => true)
|
||||
|
||||
Reference in New Issue
Block a user