aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--actionmailer/lib/action_mailer/base.rb4
-rw-r--r--actionmailer/test/base_test.rb1
-rw-r--r--actionmailer/test/old_base/url_test.rb12
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb4
-rw-r--r--actionpack/lib/action_controller/base.rb6
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb10
-rw-r--r--actionpack/lib/action_controller/railtie.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing/deprecated_mapper.rb8
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb168
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb76
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb10
-rw-r--r--actionpack/test/abstract_unit.rb25
-rw-r--r--actionpack/test/controller/mime_responds_test.rb20
-rw-r--r--actionpack/test/controller/url_for_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb277
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb22
-rw-r--r--actionpack/test/dispatch/url_generation_test.rb16
-rw-r--r--actionpack/test/template/url_helper_test.rb29
-rw-r--r--activemodel/lib/active_model/errors.rb1
-rw-r--r--activerecord/lib/active_record/associations.rb15
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb7
-rw-r--r--activerecord/lib/active_record/base.rb11
-rw-r--r--activerecord/lib/active_record/named_scope.rb2
-rw-r--r--activerecord/lib/active_record/observer.rb6
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb6
-rw-r--r--activesupport/lib/active_support/all.rb1
-rw-r--r--activesupport/lib/active_support/callbacks.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb57
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/date/zones.rb14
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/zones.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/extending.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb5
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb5
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb5
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb24
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb11
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb1
-rw-r--r--activesupport/lib/active_support/json/encoding.rb42
-rw-r--r--activesupport/lib/active_support/json/variable.rb6
-rw-r--r--activesupport/lib/active_support/time.rb1
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb12
-rw-r--r--activesupport/test/core_ext/class_test.rb40
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb49
-rw-r--r--activesupport/test/deprecation_test.rb20
-rw-r--r--activesupport/test/descendants_tracker_test.rb8
-rw-r--r--activesupport/test/json/encoding_test.rb6
-rw-r--r--activesupport/test/ordered_hash_test.rb1
-rw-r--r--rails.gemspec2
-rw-r--r--railties/guides/source/active_support_core_extensions.textile81
-rw-r--r--railties/guides/source/association_basics.textile10
-rw-r--r--railties/guides/source/initialization.textile78
-rw-r--r--railties/guides/source/routing.textile6
-rw-r--r--railties/lib/rails/application.rb23
-rw-r--r--railties/lib/rails/commands.rb10
-rw-r--r--railties/lib/rails/commands/application.rb2
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Rakefile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt4
-rw-r--r--railties/lib/rails/info_routes.rb2
-rw-r--r--railties/lib/rails/tasks/middleware.rake2
-rw-r--r--railties/lib/rails/tasks/routes.rake2
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/application/initializers/frameworks_test.rb21
-rw-r--r--railties/test/application/initializers/i18n_test.rb2
-rw-r--r--railties/test/application/rake_test.rb4
-rw-r--r--railties/test/application/runner_test.rb2
-rw-r--r--railties/test/rails_info_controller_test.rb2
83 files changed, 855 insertions, 545 deletions
diff --git a/.gitignore b/.gitignore
index 16bffc157b..2dfdf96e7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.gem
pkg
.bundle
+Gemfile.lock
debug.log
doc/rdoc
activemodel/doc
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 11fa978b9c..ed4bea0c77 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -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)
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 88e38a583b..5bc0491cff 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -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
diff --git a/actionmailer/test/old_base/url_test.rb b/actionmailer/test/old_base/url_test.rb
index 6895fb230e..b6496bfe1b 100644
--- a/actionmailer/test/old_base/url_test.rb
+++ b/actionmailer/test/old_base/url_test.rb
@@ -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
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index b4311599a9..df0b5ac9a2 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -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
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 6e3998aa21..b81d5954eb 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -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
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index a70ba0d2e3..1a2cbaab65 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -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
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index c465035ca1..a51fc5b8e4 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -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 \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 86395c4d93..9261422f0b 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -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"
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 08bc80dbc2..64f4d1d532 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -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)
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 401d98b663..c664fb0bc2 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -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>.
diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
index bc3f62fb48..b3146a1c60 100644
--- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 0b4ba8c9f5..430f6fc5a3 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -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
- 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]
+ path
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
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 5ecad6bc04..1b1a221c60 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index db17615d92..980abd44df 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -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
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 956c88a553..20a2e7c1f0 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -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'
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index b8d6dc22f2..0e3cc54fd1 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -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.
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index e5614c9e3b..56aebca957 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -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
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index c8477fb83f..5be47f7c96 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -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
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index d654338dba..b5ce391b61 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -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"
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index 4b30b0af36..71a4a43477 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -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',
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 26bd641cd6..2a014bf976 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -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
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index fd63f5ad5e..f0e01bfff0 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -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
diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb
index 326b336a1a..f83651d583 100644
--- a/actionpack/test/dispatch/url_generation_test.rb
+++ b/actionpack/test/dispatch/url_generation_test.rb
@@ -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
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 765beebfa3..048f96c9a9 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -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
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d42fc5291d..482b3dac47 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -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'
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 49da8595b2..65daa8ffbe 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -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
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index ddf4ce4058..a4e08c7d41 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -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
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 9860c3c725..3f1015dd4b 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -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.
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index c010dac64e..849ec9c884 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -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
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index 5f80bd86df..d2ed643f35 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -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?
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 7882f05d07..5024787c3c 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -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
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 3c797076e0..62237f955b 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -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')
diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb
index def8eca89f..f537818300 100644
--- a/activesupport/lib/active_support/all.rb
+++ b/activesupport/lib/active_support/all.rb
@@ -1,4 +1,3 @@
require 'active_support'
require 'active_support/time'
require 'active_support/core_ext'
-require 'active_support/json'
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index c4e1eb2c04..1c7802f7de 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -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)
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 7d58a8b56a..3e5d1a2a42 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 28fec5394f..e6a213625c 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -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'
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index ba17b0a06b..092f936961 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -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.
diff --git a/activesupport/lib/active_support/core_ext/date/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb
new file mode 100644
index 0000000000..3a83af6be2
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date/zones.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 26979aa906..1dc3933e12 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
index c3f0acce25..47b8aa59fb 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb
index 6002d4ad2a..a7411d54ae 100644
--- a/activesupport/lib/active_support/core_ext/date_time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/time/zones'
+
class DateTime
# Returns the simultaneous time in <tt>Time.zone</tt>.
#
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index 3a6100f776..27618b55c6 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -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'
diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb
deleted file mode 100644
index c4c37b6a2a..0000000000
--- a/activesupport/lib/active_support/core_ext/object/extending.rb
+++ /dev/null
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb
new file mode 100644
index 0000000000..14ef27340e
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/to_json.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 3c218d88e5..90b6cd3685 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index a02402aa3f..6b9ee84d5c 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -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)
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index feb1508586..f54f65dcf0 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -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
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index 03c445ffbf..49d58cd3a1 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -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
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index a587d7770c..6cba84d79e 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -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 \ No newline at end of file
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 5f5b264eb9..cd658fe173 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -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
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index d82e54f1d4..db09919fd3 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -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
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index dd94315111..2f9588e0f4 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -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:
diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb
index daa7449b71..5685ed18b7 100644
--- a/activesupport/lib/active_support/json/variable.rb
+++ b/activesupport/lib/active_support/json/variable.rb
@@ -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
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index 784c7173a9..86f057d676 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -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'
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 05b40298d3..49dd8a1b99 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -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)
diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb
index b7f3dd9930..08bb13dd35 100644
--- a/activesupport/test/core_ext/class_test.rb
+++ b/activesupport/test/core_ext/class_test.rb
@@ -1,29 +1,27 @@
require 'abstract_unit'
require 'active_support/core_ext/class'
-class A
-end
+class ClassTest < Test::Unit::TestCase
+ class Parent; end
+ class Foo < Parent; end
+ class Bar < Foo; end
+ class Baz < Bar; end
-module X
- class B
- end
-end
+ class A < Parent; end
+ class B < A; end
+ class C < B; end
-module Y
- module Z
- class C
- 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
-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
+ 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
+end \ No newline at end of file
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index 5e03389dc2..6588f2e345 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -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?
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index cf27357b32..cad0810241 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -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 }
diff --git a/activesupport/test/descendants_tracker_test.rb b/activesupport/test/descendants_tracker_test.rb
index ff24e310de..79fb893592 100644
--- a/activesupport/test/descendants_tracker_test.rb
+++ b/activesupport/test/descendants_tracker_test.rb
@@ -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 \ No newline at end of file
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index a8ecf4e4cf..a679efb41e 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -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 = {
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 3d1bae163f..d340bed444 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -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
diff --git a/rails.gemspec b/rails.gemspec
index 236203c626..819513c4b5 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -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
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index 844b9428bd..de0c00ac68 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -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 # => []
-
-Integer.subclasses # => ["Bignum", "Fixnum"]
+C.descendants #=> []
-module M
- class A; end
- class B1 < A; end
- class B2 < A; end
-end
+class B < C; end
+C.descendants #=> [B]
-module N
- class C < M::B1; end
-end
+class A < B; end
+C.descendants #=> [B, A]
-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+
+
+The +subclasses+ method returns all direct classes that inherits from self.
+
+<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>
-See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
+The order in which these class are returned is unspecified.
-WARNING: This method is redefined in some Rails core classes.
+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+.
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 335d17579d..c69f2ae8c9 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -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>
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index e458413b35..9a0e23385d 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -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:
++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>
- File.join(Rails.root, "app/controllers")
+ Rails.root.join("app/controllers")
</ruby>
-You may do:
+Pathname can also be converted to string, so the following syntax is preferred:
<ruby>
- Rails.root.join("app/controllers")
+ "#{Rails.root}/app/controllers"
</ruby>
-Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out.
+
+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)
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 00755071c5..72a76e25bb 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -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:
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 4a7ed2d028..458177b954 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -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!
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index aad1170b56..b9353ba336 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -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}"
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb
index a3a5aed399..47c6752ca3 100644
--- a/railties/lib/rails/commands/application.rb
+++ b/railties/lib/rails/commands/application.rb
@@ -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
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index b97ff086b6..c43a233bc3 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -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?
diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile
index 13f1f9fa41..d83cafc3be 100644
--- a/railties/lib/rails/generators/rails/app/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/app/templates/Rakefile
@@ -4,4 +4,4 @@
require File.expand_path('../config/application', __FILE__)
require 'rake'
-Rails::Application.load_tasks
+<%= app_const %>.load_tasks
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
index 22aa576f5d..a3143f1346 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
@@ -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 %>'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
index a869a21e2c..0aed79bb70 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
@@ -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
diff --git a/railties/lib/rails/info_routes.rb b/railties/lib/rails/info_routes.rb
index bd58034d8f..b5c4e4c1e0 100644
--- a/railties/lib/rails/info_routes.rb
+++ b/railties/lib/rails/info_routes.rb
@@ -1,3 +1,3 @@
-Rails.application.routes.draw do |map|
+Rails.application.routes.draw do
match '/rails/info/properties' => "rails/info#properties"
end
diff --git a/railties/lib/rails/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake
index e670989345..cd35ffb19a 100644
--- a/railties/lib/rails/tasks/middleware.rake
+++ b/railties/lib/rails/tasks/middleware.rake
@@ -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
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 41619bc1f8..a99232c4be 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -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
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 99537d6d24..247b926af9 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -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
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 7269a7c5a8..4ff10091b1 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -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 []
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
index a1fcba3310..4baa8a8170 100644
--- a/railties/test/application/initializers/i18n_test.rb
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -16,7 +16,7 @@ module ApplicationTests
end
def app
- @app ||= Rails::Application
+ @app ||= Rails.application
end
def assert_fallbacks(fallbacks)
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 40fb446b16..d6e100ffe3 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -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` }
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index d37b7649e2..07a3d94120 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -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
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
index aa76979c27..687c2d1568 100644
--- a/railties/test/rails_info_controller_test.rb
+++ b/railties/test/rails_info_controller_test.rb
@@ -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)