From 57d750edf7c71e001ac314fa188aa1fc6292f8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 30 Jun 2010 19:38:20 +0200 Subject: [PATCH 01/36] Make relation a private method. --- activerecord/lib/active_record/base.rb | 11 ++++++----- activerecord/lib/active_record/named_scope.rb | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c8795e4496..c868ff3ae8 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -896,11 +896,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 @@ -941,6 +936,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 From f3fedd7f84c25d1d99a70af1e21e20abb48f100f Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Wed, 30 Jun 2010 23:22:13 +0100 Subject: [PATCH 02/36] Don't remove scheduled destroys when loading an association. [#4642 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_record/associations/association_collection.rb | 7 ++++++- activerecord/test/cases/nested_attributes_test.rb | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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/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') From e596a8e14cfb1f2ca19cd20ae7e40047d4819b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Jul 2010 10:26:45 +0200 Subject: [PATCH 03/36] Add the possibility to have several behaviors in AS::Deprecation. --- .../lib/active_support/deprecation/behaviors.rb | 3 ++- .../lib/active_support/deprecation/reporting.rb | 5 +++-- activesupport/lib/active_support/i18n_railtie.rb | 1 + activesupport/test/deprecation_test.rb | 13 +++++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index feb1508586..f0842f201d 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 @@ -11,7 +12,7 @@ module ActiveSupport 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/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/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index cf27357b32..44f1aebb37 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 } From 9024545a6b019e3a2596a4194e84e77963e31b05 Mon Sep 17 00:00:00 2001 From: Cyril Mougel Date: Thu, 1 Jul 2010 11:59:10 +0200 Subject: [PATCH 04/36] fix failure if behavior is not define and try use the default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/deprecation/behaviors.rb | 2 +- activesupport/test/deprecation_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index f0842f201d..f54f65dcf0 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -8,7 +8,7 @@ module ActiveSupport attr_accessor :debug def behavior - @behavior ||= DEFAULT_BEHAVIORS[:stderr] + @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] end def behavior=(behavior) diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 44f1aebb37..cad0810241 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -136,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 } From 53b34e84762b7f2d6b641f99dadbb1eab42907ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 1 Jul 2010 17:07:48 +0200 Subject: [PATCH 05/36] Avoid calls to Rails::Application since this is not the official API. Your application should *always* reference your application const (as Blog::Application) and Rails.application should be used just internally. --- actionpack/lib/action_dispatch/routing.rb | 2 +- .../lib/active_record/railties/databases.rake | 2 +- .../lib/active_support/file_update_checker.rb | 11 ++- railties/guides/source/initialization.textile | 78 +++++++------------ railties/lib/rails/application.rb | 12 +-- railties/lib/rails/commands.rb | 10 +-- railties/lib/rails/commands/runner.rb | 2 +- .../generators/rails/app/templates/Rakefile | 2 +- .../config/initializers/secret_token.rb.tt | 2 +- .../config/initializers/session_store.rb.tt | 4 +- railties/lib/rails/info_routes.rb | 2 +- railties/lib/rails/tasks/middleware.rake | 2 +- railties/lib/rails/tasks/routes.rake | 2 +- .../initializers/frameworks_test.rb | 2 +- .../application/initializers/i18n_test.rb | 2 +- railties/test/application/rake_test.rb | 4 +- railties/test/rails_info_controller_test.rb | 2 +- 17 files changed, 58 insertions(+), 83 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 401d98b663..89007fab74 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -167,7 +167,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 reload!. 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/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/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index e458413b35..cedf823bdc 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' @@ -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. - # 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) h3. Bundled Rails (3.x) @@ -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: - - - require 'rubygems' - require 'bundler' - Bundler.setup - - -+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_: - - - 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 - - - -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' -+require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+ so that instead of doing: - - - File.join(Rails.root, "app/controllers") - - -You may do: ++require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+. Although is coming to use this path name to generate paths as below: Rails.root.join("app/controllers") -Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out. +Pathname can also be converted to string, so the following syntax is preferred: + + + "#{Rails.root}/app/controllers" + + + +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 -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_): def inherited(base) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 4a7ed2d028..f61103c764 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 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/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/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 7269a7c5a8..b1deb7a645 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 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/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) From d7c1057652cfc971bb35ef09b0b1560fcd28ed70 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 1 Jul 2010 02:25:35 -0700 Subject: [PATCH 06/36] Bump bundler dependency to 1.0.0.beta.2 or later --- .gitignore | 1 + rails.gemspec | 2 +- railties/guides/source/initialization.textile | 2 +- 3 files changed, 3 insertions(+), 2 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/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/initialization.textile b/railties/guides/source/initialization.textile index cedf823bdc..8498f79f4e 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -141,7 +141,7 @@ 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 +* bundler-1.0.0.beta.2.gem * erubis-2.6.5.gem * i18n-0.4.1.gem * mail-2.2.4.gem From 4a0c514eb48b8e5d4ceffb4817661c182c2368a3 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 30 Jun 2010 20:04:35 -0300 Subject: [PATCH 07/36] AS json refactor, move to_json implementation to core_ext and a cleanup a bit the code --- activesupport/lib/active_support/all.rb | 1 - .../lib/active_support/core_ext/object.rb | 1 + .../active_support/core_ext/object/to_json.rb | 19 ++++++++++ .../lib/active_support/json/encoding.rb | 37 +++---------------- .../lib/active_support/json/variable.rb | 6 +-- activesupport/test/ordered_hash_test.rb | 1 + 6 files changed, 29 insertions(+), 36 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/object/to_json.rb 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/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 3a6100f776..8922016cd7 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -9,6 +9,7 @@ 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/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/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index dd94315111..3f266d1e96 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,21 +1,16 @@ -# 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 - module ActiveSupport class << self delegate :use_standard_json_time_format, :use_standard_json_time_format=, @@ -128,20 +123,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 +133,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/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 From f8720a04d129668c7554c1a7270fba5418510b47 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 1 Jul 2010 15:04:33 -0700 Subject: [PATCH 08/36] porting session.clear fix to master branch. [#5030 state:resolved] Signed-off-by: Jeremy Kemper --- .../middleware/session/abstract_store.rb | 5 +++++ .../dispatch/session/cookie_store_test.rb | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+) 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/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 From f7ba614c2db31933cbc12eda87518de3eca0228c Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 2 Jul 2010 00:29:20 +0200 Subject: [PATCH 09/36] Unify routes naming by renaming router to routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionmailer/lib/action_mailer/base.rb | 4 ++-- actionpack/lib/abstract_controller/rendering.rb | 4 ++-- .../lib/action_controller/deprecated/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 2 +- .../lib/action_controller/metal/url_for.rb | 8 ++++---- .../action_dispatch/routing/deprecated_mapper.rb | 4 ++-- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- .../lib/action_dispatch/routing/route_set.rb | 6 +++--- .../lib/action_dispatch/routing/url_for.rb | 2 +- actionpack/lib/action_view/base.rb | 2 +- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- actionpack/lib/action_view/test_case.rb | 10 +++++----- actionpack/test/abstract_unit.rb | 8 ++++---- actionpack/test/controller/url_for_test.rb | 2 +- actionpack/test/dispatch/url_generation_test.rb | 16 ++++++++-------- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 11fa978b9c..8b86d83301 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." + "url_helpers of a routes 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 url_helpers of the routes, which will override this module extend DeprecatedUrlOptions ActiveSupport.run_load_hooks(:action_mailer, self) 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/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb index 3975afcaf0..16b67b8ee7 100644 --- a/actionpack/lib/action_controller/deprecated/base.rb +++ b/actionpack/lib/action_controller/deprecated/base.rb @@ -96,7 +96,7 @@ module ActionController def resource_action_separator=(val) ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \ - "works with the deprecated router DSL." + "works with the deprecated routes DSL." @resource_action_separator = val end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 2281c500c5..8166d31719 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -47,7 +47,7 @@ module ActionController # # In AbstractController, dispatching is triggered directly by calling #process on a new controller. # ActionController::Metal provides an #action method that returns a valid Rack application for a - # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, + # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails routes, # can dispatch directly to the action returned by FooController.action(:index). class Metal < AbstractController::Base abstract! diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index c465035ca1..0c1e2fbe80 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 + def _routes raise "In order to use #url_for, you must include the helpers of a particular " \ - "router. For instance, `include Rails.application.routes.url_helpers" + "routes. 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_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index bc3f62fb48..9684b86ed4 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -30,8 +30,8 @@ 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/" + ActiveSupport::Deprecation.warn "You are using the old routes DSL which will be removed in Rails 3.1. " << + "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..0d760ed23a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -308,7 +308,7 @@ module ActionDispatch if name_prefix = options.delete(:name_prefix) options[:as] ||= name_prefix - ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller + ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new routes syntax. Use :as instead.", caller end case args.first diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 5ecad6bc04..7248ed03f6 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -268,10 +268,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 @@ -474,7 +474,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 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] 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| "
#{html_tag}
".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..89c4cb46a1 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) } @@ -275,9 +275,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/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/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 From cb321546b7aef33fcf5466b61f79bd9198cd12b5 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 1 Jul 2010 19:03:18 -0300 Subject: [PATCH 10/36] Time has it own implementation of xmlschema, now AMo doesn't depend on TZInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4979 state:committed] Signed-off-by: José Valim --- actionmailer/test/base_test.rb | 1 + activesupport/lib/active_support/json/encoding.rb | 4 ++-- activesupport/test/json/encoding_test.rb | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) 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/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 3f266d1e96..dbce7e710a 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -9,7 +9,7 @@ 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/object/instance_variables' -require 'active_support/time' +require 'time' module ActiveSupport class << self @@ -212,7 +212,7 @@ class Time if ActiveSupport.use_standard_json_time_format xmlschema else - %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) + strftime("%Y/%m/%d %H:%M:%S %z") end end end 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 = { From 2ef8a2b403adcee21fe1a5bfadc125b24779cd53 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 1 Jul 2010 16:59:31 -0300 Subject: [PATCH 11/36] bump erubis version to 2.6.6 and thor version to 0.13.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/actionpack.gemspec | 2 +- railties/guides/source/initialization.textile | 4 ++-- railties/railties.gemspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 8498f79f4e..9a0e23385d 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -142,7 +142,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This * arel-0.4.0.gem * builder-2.1.2.gem * bundler-1.0.0.beta.2.gem -* erubis-2.6.5.gem +* erubis-2.6.6.gem * i18n-0.4.1.gem * mail-2.2.4.gem * memcache-client-1.8.3.gem @@ -157,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 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 From 0189fb76e3c38ef9a0d292135683b0d135c4a369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 2 Jul 2010 08:13:52 +0200 Subject: [PATCH 12/36] reload_routes! was still referencing old Rails::Application. --- railties/lib/rails/application.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f61103c764..458177b954 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -121,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! From 9e6e64873243ee707ecad3d523ceb0dbae0617cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 2 Jul 2010 19:13:00 +0200 Subject: [PATCH 13/36] Fix routes with :controller segment when namespaced [#5034 state:resolved] --- .../lib/action_dispatch/routing/mapper.rb | 2 +- .../lib/action_dispatch/routing/route_set.rb | 50 ++++++++++++------- actionpack/test/abstract_unit.rb | 17 ++++--- actionpack/test/dispatch/routing_test.rb | 24 +++++++-- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0d760ed23a..d71005a852 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -91,7 +91,7 @@ module ActionDispatch def app Constraints.new( - to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults), + to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults, :module => @scope[:module]), blocks, @set.request_class ) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 7248ed03f6..afa312889f 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -14,6 +14,7 @@ module ActionDispatch def initialize(options={}) @defaults = options[:defaults] @glob_param = options.delete(:glob) + @module = options.delete(:module) @controllers = {} end @@ -26,7 +27,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 +35,44 @@ 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_param = @module && !default_controller ? + "#{@module}/#{params[:controller]}" : params[:controller] + 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 diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 89c4cb46a1..5be47f7c96 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -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 diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 26bd641cd6..1bfc92aa3d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -310,11 +310,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" @@ -348,6 +343,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + namespace :private do + root :to => redirect('/private/index') + match "index", :to => 'private#index' + match ":controller(/:action(/:id))" + end + match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ end end @@ -470,6 +471,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_namespace_with_controller_segment + with_test_routes do + get '/private/foo' + assert_equal 'private/foo#index', @response.body + + get '/private/foo/bar' + assert_equal 'private/foo#bar', @response.body + + get '/private/foo/bar/1' + assert_equal 'private/foo#bar', @response.body + end + end + def test_session_singleton_resource with_test_routes do get '/session' From aeaa4687ea5802089a99e0fd96b10ed5c463cd21 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 2 Jul 2010 11:32:05 -0700 Subject: [PATCH 14/36] Fix indent --- railties/test/application/runner_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 227e1caea51279fe4687f17e5704d637a21ea15f Mon Sep 17 00:00:00 2001 From: Geoff Buesing Date: Fri, 2 Jul 2010 12:58:32 -0500 Subject: [PATCH 15/36] Time#as_json: use Time#formatted_offset instead of strftime %z directive, which is non-standard and inaccurate on some platforms (e.g., Mac OS X). [#4979] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/json/encoding.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index dbce7e710a..8ec3af3f50 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -212,7 +212,7 @@ class Time if ActiveSupport.use_standard_json_time_format xmlschema else - strftime("%Y/%m/%d %H:%M:%S %z") + %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) end end end From 8cc746331c32a6951e5c73c8a21fd32f00680471 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Jul 2010 23:46:19 -0300 Subject: [PATCH 16/36] Time#formatted_offset is defined in core_ext/time/conversions [#4979] --- activesupport/lib/active_support/json/encoding.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 8ec3af3f50..315897b423 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -10,6 +10,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/object/instance_variables' require 'time' +require 'active_support/core_ext/time/conversions' module ActiveSupport class << self From 201f373e7a183f33264ae31e7ca66768dddd3ebe Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 3 Jul 2010 01:39:54 -0300 Subject: [PATCH 17/36] Refactor move some date, time and date_time methods to */zones and fixed some requires --- .../core_ext/date/calculations.rb | 3 ++- .../core_ext/date/conversions.rb | 18 ---------------- .../lib/active_support/core_ext/date/zones.rb | 21 +++++++++++++++++++ .../core_ext/date_time/calculations.rb | 1 + .../core_ext/date_time/conversions.rb | 1 + .../core_ext/date_time/zones.rb | 2 ++ .../core_ext/time/calculations.rb | 5 +++++ .../lib/active_support/core_ext/time/zones.rb | 5 ----- activesupport/lib/active_support/time.rb | 1 + 9 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/date/zones.rb 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..48d474dd9a 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,6 +1,5 @@ require 'date' require 'active_support/inflector' -require 'active_support/core_ext/time/calculations' class Date DATE_FORMATS = { @@ -15,9 +14,6 @@ class Date # Ruby 1.9 has Date#to_time which converts to localtime only. remove_method :to_time if instance_methods.include?(: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) - # Convert to a formatted string. See DATE_FORMATS for predefined formats. # # This method is aliased to to_s. @@ -82,16 +78,6 @@ class Date ::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. # @@ -102,8 +88,4 @@ class Date def to_datetime ::DateTime.civil(year, month, day, 0, 0, 0, 0) end if RUBY_VERSION < '1.9' - - def xmlschema - to_time_in_current_zone.xmlschema - end end 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..722f086951 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/date/zones.rb @@ -0,0 +1,21 @@ +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 + + # Ruby 1.9 has Date#xmlschema which converts to a string without the time component. + remove_method :xmlschema if instance_methods.include?(:xmlschema) + + def xmlschema + to_time_in_current_zone.xmlschema + 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 Time.zone. # 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 Time.zone.now when config.time_zone is set, otherwise just returns Time.now. + 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 Time.zone.now when config.time_zone is set, otherwise just returns Time.now. - 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/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' From f6d7a4d251cae3e425cb62898c50d3f070c69d4b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 3 Jul 2010 02:26:12 -0300 Subject: [PATCH 18/36] Removes the dependency that AMo has on tzinfo [#4979 state:committed] --- activesupport/lib/active_support/values/time_zone.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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) From 54250a5bfe6992afaaca6357d3b414e6c49651ba Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 3 Jul 2010 08:14:17 +0100 Subject: [PATCH 19/36] Refactor recall parameter normalization [#5021 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_dispatch/routing/route_set.rb | 22 ++++---------- actionpack/test/template/url_helper_test.rb | 29 +++++++++++++++++-- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index afa312889f..177a6db04e 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -318,7 +318,6 @@ module ActionDispatch @extras = extras normalize_options! - normalize_recall! normalize_controller_action_id! use_relative_controller! controller.sub!(%r{^/}, '') if controller @@ -335,7 +334,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 @@ -359,15 +362,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 @@ -440,12 +434,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 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 From 075f8b72418f2d2ce9c2038bd57139955f6e46ec Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 3 Jul 2010 09:59:51 -0300 Subject: [PATCH 20/36] Add a missing require to allow the usage of Array#to_xml --- activemodel/lib/active_model/errors.rb | 1 + 1 file changed, 1 insertion(+) 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' From 75b32a69a185e2e802934bc4000f6c0efbc5c2e7 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sat, 3 Jul 2010 12:12:50 +0200 Subject: [PATCH 21/36] Fixes for "router" and "routes" terminology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit f7ba614c2db improved the internal consistency of the different means of accessing routes, but it introduced some problems at the level of code comments and user-visible strings. This commit applies fixes on three levels: Firstly, we remove or replace grammatically invalid constructs such as "a routes" or "a particular routes". Secondly, we make sure that we always use "the router DSL" or "the router syntax", because this has always been the official terminology. Finally, we make sure that we only use "routes" when referring to the application-specific set of routes that are defined in the "config/routes.rb" file, we use "router" when referring on a more abstract level to "the code in Rails used to handle routing", and we use "routing" when we need an adjective to apply to nouns such as "url_helpers. Again this is consistent with historical practice and other places in the documentation. Note that this is not a sweep over the entire codebase to ensure consistent usage of language; it is just a revision of the changes introduced in commit f7ba614c2db. Signed-off-by: Wincent Colaiuta Signed-off-by: José Valim --- actionmailer/lib/action_mailer/base.rb | 4 ++-- actionpack/lib/action_controller/deprecated/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 2 +- actionpack/lib/action_controller/metal/url_for.rb | 4 ++-- actionpack/lib/action_dispatch/routing/deprecated_mapper.rb | 2 +- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/lib/action_dispatch/routing/route_set.rb | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 8b86d83301..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 routes 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 routes, 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/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb index 16b67b8ee7..3975afcaf0 100644 --- a/actionpack/lib/action_controller/deprecated/base.rb +++ b/actionpack/lib/action_controller/deprecated/base.rb @@ -96,7 +96,7 @@ module ActionController def resource_action_separator=(val) ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \ - "works with the deprecated routes DSL." + "works with the deprecated router DSL." @resource_action_separator = val end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 8166d31719..2281c500c5 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -47,7 +47,7 @@ module ActionController # # In AbstractController, dispatching is triggered directly by calling #process on a new controller. # ActionController::Metal provides an #action method that returns a valid Rack application for a - # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails routes, + # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router, # can dispatch directly to the action returned by FooController.action(:index). class Metal < AbstractController::Base abstract! diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 0c1e2fbe80..a51fc5b8e4 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -13,8 +13,8 @@ module ActionController end def _routes - raise "In order to use #url_for, you must include the helpers of a particular " \ - "routes. For instance, `include Rails.application.routes.url_helpers" + raise "In order to use #url_for, you must include routing helpers explicitly. " \ + "For instance, `include Rails.application.routes.url_helpers" end module ClassMethods diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index 9684b86ed4..e122bdf232 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -30,7 +30,7 @@ module ActionDispatch class DeprecatedMapper #:nodoc: def initialize(set) #:nodoc: - ActiveSupport::Deprecation.warn "You are using the old routes DSL which will be removed in Rails 3.1. " << + ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " << "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 d71005a852..64e12f960d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -308,7 +308,7 @@ module ActionDispatch if name_prefix = options.delete(:name_prefix) options[:as] ||= name_prefix - ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new routes syntax. Use :as instead.", caller + ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller end case args.first diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 177a6db04e..24f9981f4b 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -480,7 +480,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 routes + # 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] From c6843e23373c626ae49ad9fa253d4f7538d3434f Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sun, 4 Jul 2010 06:52:52 +0100 Subject: [PATCH 22/36] Refactor resource options and scoping. Resource classes are now only responsible for controlling how they are named. All other options passed to resources are pushed out to the scope. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_dispatch/routing/mapper.rb | 163 +++++++----------- actionpack/test/dispatch/routing_test.rb | 83 ++++++++- 2 files changed, 145 insertions(+), 101 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 64e12f960d..f44c10f533 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -444,7 +444,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] class Resource #:nodoc: DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit] @@ -463,16 +463,6 @@ module ActionDispatch self.class::DEFAULT_ACTIONS end - def actions - if only = options[:only] - Array(only).map(&:to_sym) - elsif except = options[:except] - default_actions - Array(except).map(&:to_sym) - else - default_actions - end - end - def name @as || @name end @@ -489,68 +479,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 +512,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 +522,10 @@ module ActionDispatch end alias :collection_name :member_name - def nested_path + def member_scope path end - - def nested_options - {}.tap do |opts| - opts[:as] = member_name - opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil? - end - end - - def shallow? - false - end - - def member_scope - [path, options] - end + alias :nested_scope :member_scope end def initialize(*args) #:nodoc: @@ -610,17 +549,17 @@ module ActionDispatch collection_scope do post :create - end if parent_resource.actions.include?(:create) + end if resource_actions.include?(:create) new_scope do get :new - end if parent_resource.actions.include?(:new) + end if resource_actions.include?(:new) member_scope do - get :show if parent_resource.actions.include?(:show) - put :update if parent_resource.actions.include?(:update) - delete :destroy if parent_resource.actions.include?(:destroy) - get :edit if parent_resource.actions.include?(:edit) + get :show if resource_actions.include?(:show) + put :update if resource_actions.include?(:update) + delete :destroy if resource_actions.include?(:destroy) + get :edit if resource_actions.include?(:edit) end end @@ -638,19 +577,19 @@ module ActionDispatch yield if block_given? collection_scope do - get :index if parent_resource.actions.include?(:index) - post :create if parent_resource.actions.include?(:create) + get :index if resource_actions.include?(:index) + post :create if resource_actions.include?(:create) end new_scope do get :new - end if parent_resource.actions.include?(:new) + end if resource_actions.include?(:new) member_scope do - get :show if parent_resource.actions.include?(:show) - put :update if parent_resource.actions.include?(:update) - delete :destroy if parent_resource.actions.include?(:destroy) - get :edit if parent_resource.actions.include?(:edit) + get :show if resource_actions.include?(:show) + put :update if resource_actions.include?(:update) + delete :destroy if resource_actions.include?(:destroy) + get :edit if resource_actions.include?(:edit) end end @@ -693,18 +632,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 +662,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) @@ -794,23 +737,30 @@ module ActionDispatch @scope[:scope_level_resource] end + def resource_actions + if only = @scope[:options][:only] + Array(only).map(&:to_sym) + elsif except = @scope[:options][:except] + parent_resource.default_actions - Array(except).map(&:to_sym) + else + parent_resource.default_actions + end + end + def apply_common_behavior_for(method, resources, options, &block) if resources.length > 1 resources.each { |r| send(method, r, options, &block) } 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? - if resource_scope? nested do send(method, resources.pop, options, &block) @@ -853,7 +803,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 +811,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 +819,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 +827,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) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 1bfc92aa3d..d56612d6ec 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -298,8 +298,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 @@ -349,6 +349,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match ":controller(/:action(/:id))" end + scope :only => :index do + resources :clubs do + resources :players, :only => [:show] + resource :chairman, :only => [:show] + end + end + + scope :except => [:new, :create, :edit, :update] do + resources :sectors do + resources :companies, :except => :destroy do + resources :divisions, :only => :index + end + resource :leader, :except => :destroy + end + end + match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ end end @@ -1254,7 +1270,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'} @@ -1640,6 +1656,69 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'Not Found', @response.body assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') } end + + def test_only_option_should_be_overwritten + get '/clubs' + assert_equal 'clubs#index', @response.body + assert_equal '/clubs', clubs_path + + get '/clubs/1' + assert_equal 'Not Found', @response.body + assert_raise(NameError) { club_path(:id => '1') } + + get '/clubs/1/players' + assert_equal 'Not Found', @response.body + assert_raise(NameError) { club_players_path(:club_id => '1') } + + get '/clubs/1/players/2' + assert_equal 'players#show', @response.body + assert_equal '/clubs/1/players/2', club_player_path(:club_id => '1', :id => '2') + + get '/clubs/1/chairman/new' + assert_equal 'Not Found', @response.body + assert_raise(NameError) { new_club_chairman_path(:club_id => '1') } + + get '/clubs/1/chairman' + assert_equal 'chairmen#show', @response.body + assert_equal '/clubs/1/chairman', club_chairman_path(:club_id => '1') + end + + def test_except_option_should_be_overwritten + get '/sectors' + assert_equal 'sectors#index', @response.body + assert_equal '/sectors', sectors_path + + get '/sectors/new' + assert_equal 'Not Found', @response.body + assert_raise(NameError) { new_sector_path } + + delete '/sectors/1' + assert_equal 'sectors#destroy', @response.body + + get '/sectors/1/companies/new' + assert_equal 'companies#new', @response.body + assert_equal '/sectors/1/companies/new', new_sector_company_path + + delete '/sectors/1/companies/1' + assert_equal 'Not Found', @response.body + + get '/sectors/1/leader/new' + assert_equal 'leaders#new', @response.body + assert_equal '/sectors/1/leader/new', new_sector_leader_path + + delete '/sectors/1/leader' + assert_equal 'Not Found', @response.body + end + + def test_only_option_should_overwrite_except_option + get '/sectors/1/companies/2/divisions' + assert_equal 'divisions#index', @response.body + assert_equal '/sectors/1/companies/2/divisions', sector_company_divisions_path + + get '/sectors/1/companies/2/divisions/3' + assert_equal 'Not Found', @response.body + assert_raise(NameError) { sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') } + end end private From aa31a255c831808b42c28978a36ffa42ff03e68e Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sun, 4 Jul 2010 17:35:34 +0100 Subject: [PATCH 23/36] Fix syntax of routing tests so they actually run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/test/dispatch/routing_test.rb | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index d56612d6ec..1eb5d32ec3 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1656,19 +1656,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'Not Found', @response.body assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') } end + end - def test_only_option_should_be_overwritten + def test_only_option_should_be_overwritten + with_test_routes do get '/clubs' assert_equal 'clubs#index', @response.body assert_equal '/clubs', clubs_path get '/clubs/1' assert_equal 'Not Found', @response.body - assert_raise(NameError) { club_path(:id => '1') } + assert_raise(NoMethodError) { club_path(:id => '1') } get '/clubs/1/players' assert_equal 'Not Found', @response.body - assert_raise(NameError) { club_players_path(:club_id => '1') } + assert_raise(NoMethodError) { club_players_path(:club_id => '1') } get '/clubs/1/players/2' assert_equal 'players#show', @response.body @@ -1676,48 +1678,52 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/clubs/1/chairman/new' assert_equal 'Not Found', @response.body - assert_raise(NameError) { new_club_chairman_path(:club_id => '1') } + assert_raise(NoMethodError) { new_club_chairman_path(:club_id => '1') } get '/clubs/1/chairman' assert_equal 'chairmen#show', @response.body assert_equal '/clubs/1/chairman', club_chairman_path(:club_id => '1') end + end - def test_except_option_should_be_overwritten + def test_except_option_should_be_overwritten + with_test_routes do get '/sectors' assert_equal 'sectors#index', @response.body assert_equal '/sectors', sectors_path - get '/sectors/new' + get '/sectors/1/edit' assert_equal 'Not Found', @response.body - assert_raise(NameError) { new_sector_path } + assert_raise(NoMethodError) { edit_sector_path(:id => '1') } delete '/sectors/1' assert_equal 'sectors#destroy', @response.body get '/sectors/1/companies/new' assert_equal 'companies#new', @response.body - assert_equal '/sectors/1/companies/new', new_sector_company_path + assert_equal '/sectors/1/companies/new', new_sector_company_path(:sector_id => '1') delete '/sectors/1/companies/1' assert_equal 'Not Found', @response.body get '/sectors/1/leader/new' assert_equal 'leaders#new', @response.body - assert_equal '/sectors/1/leader/new', new_sector_leader_path + assert_equal '/sectors/1/leader/new', new_sector_leader_path(:sector_id => '1') delete '/sectors/1/leader' assert_equal 'Not Found', @response.body end + end - def test_only_option_should_overwrite_except_option + def test_only_option_should_overwrite_except_option + with_test_routes do get '/sectors/1/companies/2/divisions' assert_equal 'divisions#index', @response.body - assert_equal '/sectors/1/companies/2/divisions', sector_company_divisions_path + assert_equal '/sectors/1/companies/2/divisions', sector_company_divisions_path(:sector_id => '1', :company_id => '2') get '/sectors/1/companies/2/divisions/3' assert_equal 'Not Found', @response.body - assert_raise(NameError) { sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') } + assert_raise(NoMethodError) { sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') } end end From 3cb53758324214ec8bc65979f47dd43d0e85a248 Mon Sep 17 00:00:00 2001 From: Madjo DIAPENA Date: Sun, 4 Jul 2010 17:41:08 +0200 Subject: [PATCH 24/36] ARGV.empty? is useless. If ARGV is empty, ARGV.first != "new" will always be true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails/commands/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 7f7480f6fc1e88ce19bee8ac7f6fb2243343495e Mon Sep 17 00:00:00 2001 From: Patrik Stenmark Date: Sat, 15 May 2010 14:10:40 +0200 Subject: [PATCH 25/36] Adds tests for content negotiation change introduced in dc5300adb6d46252c26e Signed-off-by: wycats --- .../test/controller/mime_responds_test.rb | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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" From 8a09ea6d6dbbd6a1325b3be429d96d9c45c046df Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 3 Jul 2010 21:12:49 -0300 Subject: [PATCH 26/36] Avoids deprecation warning running tests --- actionmailer/test/old_base/url_test.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 From 5bf3294c8b3aeb3afd426e8c182456c675829c1e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 3 Jul 2010 21:30:33 -0300 Subject: [PATCH 27/36] Move Date#xmlschema to conversions and add a missing require --- .../lib/active_support/core_ext/date/conversions.rb | 12 ++++++++++-- .../lib/active_support/core_ext/date/zones.rb | 7 ------- activesupport/lib/active_support/json/encoding.rb | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 48d474dd9a..092f936961 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,5 +1,6 @@ require 'date' require 'active_support/inflector' +require 'active_support/core_ext/date/zones' class Date DATE_FORMATS = { @@ -12,7 +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 method_defined?(:xmlschema) # Convert to a formatted string. See DATE_FORMATS for predefined formats. # @@ -77,7 +81,7 @@ class Date def to_time(form = :local) ::Time.send("#{form}_time", year, month, day) 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. # @@ -88,4 +92,8 @@ class Date def to_datetime ::DateTime.civil(year, month, day, 0, 0, 0, 0) end if RUBY_VERSION < '1.9' + + def xmlschema + to_time_in_current_zone.xmlschema + end end diff --git a/activesupport/lib/active_support/core_ext/date/zones.rb b/activesupport/lib/active_support/core_ext/date/zones.rb index 722f086951..3a83af6be2 100644 --- a/activesupport/lib/active_support/core_ext/date/zones.rb +++ b/activesupport/lib/active_support/core_ext/date/zones.rb @@ -11,11 +11,4 @@ class Date to_time end end - - # Ruby 1.9 has Date#xmlschema which converts to a string without the time component. - remove_method :xmlschema if instance_methods.include?(:xmlschema) - - def xmlschema - to_time_in_current_zone.xmlschema - end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 315897b423..2f9588e0f4 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -11,6 +11,8 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/object/instance_variables' 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 From a5dda97602f2188a13cbcab5c7e9a5ba84ba876b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 5 Jul 2010 12:50:08 +0200 Subject: [PATCH 28/36] Define a convention for descendants and subclasses. The former should be symmetric with ancestors and include all children. However, it should not include self since ancestors + descendants should not have duplicated. The latter is symmetric to superclass in the sense it only includes direct children. By adopting a convention, we expect to have less conflict with other frameworks, as Datamapper. For this moment, to ensure ActiveModel::Validations can be used with Datamapper, we should always call ActiveSupport::DescendantsTracker.descendants(self) internally instead of self.descendants avoiding conflicts. --- actionpack/lib/action_controller/base.rb | 6 -- actionpack/lib/action_controller/railtie.rb | 1 - .../routing/deprecated_mapper.rb | 4 +- activerecord/lib/active_record/observer.rb | 6 +- activesupport/lib/active_support/callbacks.rb | 4 +- .../core_ext/class/subclasses.rb | 57 ++++++------- .../lib/active_support/core_ext/object.rb | 1 - .../core_ext/object/extending.rb | 11 --- .../lib/active_support/descendants_tracker.rb | 24 +++--- activesupport/test/core_ext/class_test.rb | 46 +++++------ .../core_ext/object_and_class_ext_test.rb | 49 ----------- .../test/descendants_tracker_test.rb | 8 +- .../active_support_core_extensions.textile | 81 +++++++------------ 13 files changed, 103 insertions(+), 195 deletions(-) delete mode 100644 activesupport/lib/active_support/core_ext/object/extending.rb 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/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/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index e122bdf232..05e8dfded6 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -19,8 +19,8 @@ module ActionDispatch def in_memory_controller_namespaces namespaces = Set.new - ActionController::Base.subclasses.each do |klass| - controller_name = klass.underscore + ActionController::Base.descendants.each do |klass| + controller_name = klass.name.underscore namespaces << controller_name.split('/')[0...-1].join('/') end namespaces.delete('') 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/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/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 8922016cd7..27618b55c6 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -6,7 +6,6 @@ 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' 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/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/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 - -module X - class B - end -end - -module Y - module Z - class C - end - end -end - class ClassTest < Test::Unit::TestCase - def test_retrieving_subclasses - @parent = eval("class D; end; D") - @sub = eval("class E < D; end; E") - @subofsub = eval("class F < E; end; F") - assert_equal 2, @parent.subclasses.size - assert_equal [@subofsub.to_s], @sub.subclasses - assert_equal [], @subofsub.subclasses - assert_equal [@sub.to_s, @subofsub.to_s].sort, @parent.subclasses.sort + class Parent; end + class Foo < Parent; end + class Bar < Foo; end + class Baz < Bar; end + + class A < Parent; end + class B < A; end + class C < B; end + + def test_descendants + assert_equal [Foo, Bar, Baz, A, B, C], Parent.descendants + assert_equal [Bar, Baz], Foo.descendants + assert_equal [Baz], Bar.descendants + assert_equal [], Baz.descendants end -end + + def test_subclasses + assert_equal [Foo, A], Parent.subclasses + assert_equal [Bar], Foo.subclasses + assert_equal [Baz], Bar.subclasses + assert_equal [], Baz.subclasses + end +end \ 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/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/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: - - -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] - - -The order in which these classes are returned is unspecified. The returned collection may have duplicates: - - -subclasses_of(Numeric, Integer) -# => [Bignum, Float, Fixnum, Integer, Date::Infinity, Rational, BigDecimal, Bignum, Fixnum] - - -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. class C; end -C.subclasses # => [] +C.descendants #=> [] -Integer.subclasses # => ["Bignum", "Fixnum"] +class B < C; end +C.descendants #=> [B] -module M - class A; end - class B1 < A; end - class B2 < A; end -end +class A < B; end +C.descendants #=> [B, A] -module N - class C < M::B1; end -end - -M::A.subclasses # => ["N::C", "M::B2", "M::B1"] +class D < C; end +C.descendants #=> [B, A, D] -The order in which these class names are returned is unspecified. +h5. +subclasses+ -See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME. +The +subclasses+ method returns all direct classes that inherits from self. -WARNING: This method is redefined in some Rails core classes. + +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] + + +The order in which these class are returned is unspecified. + +WARNING: This method is redefined in some Rails core classes but should be all compatible in Rails 3.1. NOTE: Defined in +active_support/core_ext/class/subclasses.rb+. From 6671d9cdc1cc40a6cdd365902f76d4aca78a410c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 5 Jul 2010 21:44:49 +0200 Subject: [PATCH 29/36] RouteSet should also handle anonymous classes. --- actionpack/lib/action_dispatch/routing/deprecated_mapper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index 05e8dfded6..b3146a1c60 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -20,8 +20,8 @@ module ActionDispatch def in_memory_controller_namespaces namespaces = Set.new ActionController::Base.descendants.each do |klass| - controller_name = klass.name.underscore - namespaces << controller_name.split('/')[0...-1].join('/') + next if klass.anonymous? + namespaces << klass.name.underscore.split('/')[0...-1].join('/') end namespaces.delete('') namespaces From 8079484b118e6dc8ffe3575b50c3857acd5b1a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 6 Jul 2010 00:39:13 +0200 Subject: [PATCH 30/36] Recognize should also work with route is wrapped in a constraint. --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 ++ actionpack/lib/action_dispatch/routing/route_set.rb | 2 ++ actionpack/test/dispatch/routing_test.rb | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index f44c10f533..6529cf6f31 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 diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 24f9981f4b..01a068a9f2 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -512,6 +512,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/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 1eb5d32ec3..463a62cd53 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" From 92ff71bb14b1b589a3d5c04d120e4b9210b243b1 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 6 Jul 2010 17:29:19 +0200 Subject: [PATCH 31/36] documents automatic management of join models in hmt associations, in particular the gotcha that deletion is direct --- activerecord/lib/active_record/associations.rb | 15 +++++++++++---- railties/guides/source/association_basics.textile | 10 ++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4caa434fc0..eebbd17f43 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -768,15 +768,20 @@ module ActiveRecord # Objects will be in addition destroyed if they're associated with :dependent => :destroy, # and deleted if they're associated with :dependent => :delete_all. # [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 :through + # 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 collection=. See above. # [collection.clear] # Removes every object from the collection. This destroys the associated objects if they # are associated with :dependent => :destroy, deletes them directly from the # database if :dependent => :delete_all, otherwise sets their foreign keys to +NULL+. + # If the :through 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] @@ -869,9 +874,11 @@ module ActiveRecord # [:as] # Specifies a polymorphic interface (See belongs_to). # [:through] - # Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key + # Specifies a join model through which to perform the query. Options for :class_name and :foreign_key # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to - # has_one or has_many association on the join model. + # has_one or has_many 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 has_many :through queries. Only use it if the name cannot be # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either :subscribers or 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 + + +physician.patients = patients + + +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: From f4be0041c605daad2406ec41fa7dfa4388af5f31 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Tue, 6 Jul 2010 19:06:02 +0100 Subject: [PATCH 32/36] Refactor handling of :only and :except options. The rules are: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Don't inherit when specified as an option on a resource 2. Don't push into scope when specified as an option on a resource 2. Resources pull in :only or :except options from scope 3. Either :only or :except in nested scope overwrites parent scope [#5048 state:resolved] Signed-off-by: José Valim --- .../lib/action_dispatch/routing/mapper.rb | 72 ++++-- actionpack/test/dispatch/routing_test.rb | 241 ++++++++++++++---- 2 files changed, 238 insertions(+), 75 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 6529cf6f31..aab272f565 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -433,12 +433,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 @@ -446,7 +450,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] - RESOURCE_OPTIONS = [:as, :controller, :path] + RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except] class Resource #:nodoc: DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit] @@ -465,6 +469,16 @@ module ActionDispatch self.class::DEFAULT_ACTIONS end + def actions + if only = @options[:only] + Array(only).map(&:to_sym) + elsif except = @options[:except] + default_actions - Array(except).map(&:to_sym) + else + default_actions + end + end + def name @as || @name end @@ -551,17 +565,17 @@ module ActionDispatch collection_scope do post :create - end if resource_actions.include?(:create) + end if parent_resource.actions.include?(:create) new_scope do get :new - end if resource_actions.include?(:new) + end if parent_resource.actions.include?(:new) member_scope do - get :show if resource_actions.include?(:show) - put :update if resource_actions.include?(:update) - delete :destroy if resource_actions.include?(:destroy) - get :edit if resource_actions.include?(:edit) + get :show if parent_resource.actions.include?(:show) + put :update if parent_resource.actions.include?(:update) + delete :destroy if parent_resource.actions.include?(:destroy) + get :edit if parent_resource.actions.include?(:edit) end end @@ -579,19 +593,19 @@ module ActionDispatch yield if block_given? collection_scope do - get :index if resource_actions.include?(:index) - post :create if resource_actions.include?(:create) + get :index if parent_resource.actions.include?(:index) + post :create if parent_resource.actions.include?(:create) end new_scope do get :new - end if resource_actions.include?(:new) + end if parent_resource.actions.include?(:new) member_scope do - get :show if resource_actions.include?(:show) - put :update if resource_actions.include?(:update) - delete :destroy if resource_actions.include?(:destroy) - get :edit if resource_actions.include?(:edit) + get :show if parent_resource.actions.include?(:show) + put :update if parent_resource.actions.include?(:update) + delete :destroy if parent_resource.actions.include?(:destroy) + get :edit if parent_resource.actions.include?(:edit) end end @@ -739,16 +753,6 @@ module ActionDispatch @scope[:scope_level_resource] end - def resource_actions - if only = @scope[:options][:only] - Array(only).map(&:to_sym) - elsif except = @scope[:options][:except] - parent_resource.default_actions - Array(except).map(&:to_sym) - else - parent_resource.default_actions - end - end - def apply_common_behavior_for(method, resources, options, &block) if resources.length > 1 resources.each { |r| send(method, r, options, &block) } @@ -763,6 +767,10 @@ module ActionDispatch return true end + unless action_options?(options) + options.merge!(scope_action_options) if scope_action_options? + end + if resource_scope? nested do send(method, resources.pop, options, &block) @@ -773,6 +781,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 @@ -899,7 +919,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/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 463a62cd53..90d05d4a67 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -352,19 +352,55 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match ":controller(/:action(/:id))" end - scope :only => :index do - resources :clubs do - resources :players, :only => [:show] - resource :chairman, :only => [:show] + scope :only => [:index, :show] do + namespace :only do + resources :clubs do + resources :players + resource :chairman + end end end - scope :except => [:new, :create, :edit, :update] do - resources :sectors do - resources :companies, :except => :destroy do - resources :divisions, :only => :index + 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 - resource :leader, :except => :destroy end end @@ -1661,72 +1697,179 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - def test_only_option_should_be_overwritten + def test_only_should_be_read_from_scope with_test_routes do - get '/clubs' - assert_equal 'clubs#index', @response.body - assert_equal '/clubs', clubs_path + get '/only/clubs' + assert_equal 'only/clubs#index', @response.body + assert_equal '/only/clubs', only_clubs_path - get '/clubs/1' + get '/only/clubs/1/edit' assert_equal 'Not Found', @response.body - assert_raise(NoMethodError) { club_path(:id => '1') } + assert_raise(NoMethodError) { edit_only_club_path(:id => '1') } - get '/clubs/1/players' + 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) { club_players_path(:club_id => '1') } + assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') } - get '/clubs/1/players/2' - assert_equal 'players#show', @response.body - assert_equal '/clubs/1/players/2', 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 '/clubs/1/chairman/new' + get '/only/clubs/1/chairman/edit' assert_equal 'Not Found', @response.body - assert_raise(NoMethodError) { new_club_chairman_path(:club_id => '1') } - - get '/clubs/1/chairman' - assert_equal 'chairmen#show', @response.body - assert_equal '/clubs/1/chairman', club_chairman_path(:club_id => '1') + assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') } end end - def test_except_option_should_be_overwritten + def test_except_should_be_read_from_scope with_test_routes do - get '/sectors' - assert_equal 'sectors#index', @response.body - assert_equal '/sectors', sectors_path + get '/except/clubs' + assert_equal 'except/clubs#index', @response.body + assert_equal '/except/clubs', except_clubs_path - get '/sectors/1/edit' + get '/except/clubs/1/edit' assert_equal 'Not Found', @response.body - assert_raise(NoMethodError) { edit_sector_path(:id => '1') } + assert_raise(NoMethodError) { edit_except_club_path(:id => '1') } - delete '/sectors/1' - assert_equal 'sectors#destroy', @response.body + 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 '/sectors/1/companies/new' - assert_equal 'companies#new', @response.body - assert_equal '/sectors/1/companies/new', new_sector_company_path(:sector_id => '1') - - delete '/sectors/1/companies/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 '/sectors/1/leader/new' - assert_equal 'leaders#new', @response.body - assert_equal '/sectors/1/leader/new', new_sector_leader_path(:sector_id => '1') + 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') - delete '/sectors/1/leader' + 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_overwrite_except_option + def test_only_option_should_override_scope with_test_routes do - get '/sectors/1/companies/2/divisions' - assert_equal 'divisions#index', @response.body - assert_equal '/sectors/1/companies/2/divisions', sector_company_divisions_path(:sector_id => '1', :company_id => '2') + get '/only/sectors' + assert_equal 'only/sectors#index', @response.body + assert_equal '/only/sectors', only_sectors_path - get '/sectors/1/companies/2/divisions/3' + get '/only/sectors/1' assert_equal 'Not Found', @response.body - assert_raise(NoMethodError) { sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') } + 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 From b802a0d4c72fbd605b92e9f403c9b8765a6e480c Mon Sep 17 00:00:00 2001 From: Andrew White Date: Tue, 6 Jul 2010 22:20:06 +0100 Subject: [PATCH 33/36] When a dynamic :controller segment is present in the path add a Regexp constraint that allow matching on multiple path segments. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a namespace block isn't compatible with dynamic routes so we raise an ArgumentError if we detect a :module present in the scope. [#5052 state:resolved] Signed-off-by: José Valim --- .../lib/action_dispatch/routing/mapper.rb | 25 +++++++++---- .../lib/action_dispatch/routing/route_set.rb | 6 ++-- actionpack/test/dispatch/routing_test.rb | 36 +++++++++++++------ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index aab272f565..430f6fc5a3 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -65,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\)$/, '') @@ -93,7 +103,7 @@ module ActionDispatch def app Constraints.new( - to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults, :module => @scope[:module]), + to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults), blocks, @set.request_class ) @@ -135,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" @@ -185,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 diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 01a068a9f2..1b1a221c60 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -14,7 +14,6 @@ module ActionDispatch def initialize(options={}) @defaults = options[:defaults] @glob_param = options.delete(:glob) - @module = options.delete(:module) @controllers = {} end @@ -39,12 +38,11 @@ module ActionDispatch # 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 + # 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 = @module && !default_controller ? - "#{@module}/#{params[:controller]}" : params[:controller] + controller_param = params[:controller] controller_reference(controller_param) end rescue NameError => e diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 90d05d4a67..2a014bf976 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -323,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 @@ -349,7 +349,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest namespace :private do root :to => redirect('/private/index') match "index", :to => 'private#index' - match ":controller(/:action(/:id))" end scope :only => [:index, :show] do @@ -527,15 +526,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end def test_namespace_with_controller_segment - with_test_routes do - get '/private/foo' - assert_equal 'private/foo#index', @response.body - - get '/private/foo/bar' - assert_equal 'private/foo#bar', @response.body - - get '/private/foo/bar/1' - assert_equal 'private/foo#bar', @response.body + 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 @@ -1354,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") From 0f96cea322294f84df34c43000443b249a880126 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Wed, 7 Jul 2010 05:31:54 +0100 Subject: [PATCH 34/36] Add note about incompatibility of namespace and :controller --- railties/guides/source/routing.textile | 6 ++++++ 1 file changed, 6 insertions(+) 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: + + +match ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/ + + h4. Static Segments You can specify static segments when creating a route: From 8735d15e61797d0e6879efbde4f1ca18b4403eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 7 Jul 2010 12:50:38 +0200 Subject: [PATCH 35/36] Add a test to ensure url helpers are not action methods in ActionMailer. --- .../initializers/frameworks_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index b1deb7a645..4ff10091b1 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -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 [] From dc364fdc595405aa3d5735e60d46ad3f9544a65b Mon Sep 17 00:00:00 2001 From: Rohit Arondekar Date: Wed, 7 Jul 2010 22:15:15 -0700 Subject: [PATCH 36/36] API Docs: Fixes to the Routing docs --- actionpack/lib/action_dispatch/routing.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 89007fab74..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 root as a shorthand to name a route for the root path "". + # Use root 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}/