diff options
author | Mikel Lindsaar <raasdnil@gmail.com> | 2009-12-17 11:24:02 +1100 |
---|---|---|
committer | Mikel Lindsaar <raasdnil@gmail.com> | 2009-12-17 11:24:02 +1100 |
commit | 186cd7bc530f705b889c27f3680ab48c7c10a6f3 (patch) | |
tree | 76955e442615d77ee05ef2a8260c5373e1cac680 | |
parent | 5f2395041d1578433fa825ed5c6f26a201f2203d (diff) | |
parent | b9d4ceb43c9497fb1c47d8b1e1e6a24a9e157384 (diff) | |
download | rails-186cd7bc530f705b889c27f3680ab48c7c10a6f3.tar.gz rails-186cd7bc530f705b889c27f3680ab48c7c10a6f3.tar.bz2 rails-186cd7bc530f705b889c27f3680ab48c7c10a6f3.zip |
Merge branch 'rails'
Conflicts:
actionmailer/lib/action_mailer.rb
actionmailer/lib/action_mailer/delivery_method/smtp.rb
217 files changed, 3351 insertions, 2750 deletions
diff --git a/.gitignore b/.gitignore index 70b7c0057a..9a65e4996f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,9 @@ activerecord/doc actionpack/doc actionmailer/doc activesupport/doc -activemodel/pkg -activeresource/pkg -activerecord/pkg -actionpack/pkg activemodel/test/fixtures/fixture_database.sqlite3 -actionmailer/pkg -activesupport/pkg actionpack/test/tmp activesupport/test/fixtures/isolation_test -railties/pkg railties/test/500.html railties/test/fixtures/tmp railties/test/initializer/root/log @@ -31,3 +24,4 @@ railties/guides/output bin vendor/gems/ railties/tmp +pkg diff --git a/.gitmodules b/.gitmodules index 20ed3ed9c9..d0be7ff194 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "arel"] path = arel url = git://github.com/rails/arel.git -[submodule "rack-mount"] - path = rack-mount - url = git://github.com/rails/rack-mount.git +[submodule "rack"] + path = rack + url = git://github.com/rails/rack.git @@ -1,9 +1,10 @@ require 'rake' require 'rake/rdoctask' +require 'rake/gempackagetask' env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD'] -PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord activemodel railties) +PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties) Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path| require version_path @@ -23,11 +24,21 @@ task :default => %w(test test:isolated) end end +spec = eval(File.read('rails.gemspec')) + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + task :install => :gem do + system %(cd arel && gem build arel.gemspec && gem install arel-0.2.pre.gem --no-ri --no-rdoc --ignore-dependencies) + system %(cd rack && rake gem VERSION=1.0.2.pre && gem install rack-1.0.2.pre.gem --no-ri --no-rdoc --ignore-dependencies) (PROJECTS - ["railties"]).each do |project| + puts "INSTALLING #{project}" system("gem install #{project}/pkg/#{project}-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") end - system("gem install railties/pkg/rails-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") + system("gem install railties/pkg/railties-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") + system("gem install pkg/rails-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") end desc "Generate documentation for the Rails framework" diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 1a7ece5068..6c19371514 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -23,7 +23,6 @@ task :default => [ :test ] Rake::TestTask.new { |t| t.libs << "test" t.pattern = 'test/*_test.rb' - t.verbose = true t.warning = true } diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 41ed399308..e7ad68e212 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -21,37 +21,38 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib" -$:.unshift(actionpack_path) if File.directory?(actionpack_path) +actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__) +$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path) + require 'action_controller' require 'action_view' module ActionMailer - def self.load_all! - [Base, Part, ::Text::Format, ::Net::SMTP] - end - - autoload :AdvAttrAccessor, 'action_mailer/adv_attr_accessor' - autoload :DeprecatedBody, 'action_mailer/deprecated_body' - autoload :Base, 'action_mailer/base' - autoload :DeliveryMethod, 'action_mailer/delivery_method' - autoload :Part, 'action_mailer/part' - autoload :PartContainer, 'action_mailer/part_container' - autoload :Quoting, 'action_mailer/quoting' - autoload :TestCase, 'action_mailer/test_case' - autoload :TestHelper, 'action_mailer/test_helper' - + extend ::ActiveSupport::Autoload + + autoload :AdvAttrAccessor + autoload :DeprecatedBody + autoload :Base + autoload :DeliveryMethod + autoload :Part + autoload :PartContainer + autoload :Quoting + autoload :TestCase + autoload :TestHelper end module Text + extend ActiveSupport::Autoload + autoload :Format, 'action_mailer/vendor/text_format' end module Net - autoload :SMTP, 'net/smtp' + extend ActiveSupport::Autoload + + autoload :SMTP end -autoload :MailHelper, 'action_mailer/mail_helper' gem 'mail', '>= 1.3.0' require 'mail' diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 3639992ce9..24064b3a74 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -258,7 +258,7 @@ module ActionMailer #:nodoc: include AbstractController::Layouts include AbstractController::Helpers - helper MailHelper + helper ActionMailer::MailHelper if Object.const_defined?(:ActionController) include ActionController::UrlWriter @@ -543,11 +543,15 @@ module ActionMailer #:nodoc: @headers ||= {} @sent_on ||= Time.now - super # Run deprecation hooks + ActiveSupport::Deprecation.silence do + super # Run deprecation hooks + end end def create_parts - super # Run deprecation hooks + ActiveSupport::Deprecation.silence do + super # Run deprecation hooks + end if String === response_body @parts.unshift Mail::Part.new( @@ -620,7 +624,7 @@ module ActionMailer #:nodoc: headers.each { |k, v| m[k] = v } real_content_type, ctype_attrs = parse_content_type - + if @parts.empty? main_type, sub_type = split_content_type(real_content_type) m.content_type([main_type, sub_type, ctype_attrs]) diff --git a/actionmailer/lib/action_mailer/delivery_method/file.rb b/actionmailer/lib/action_mailer/delivery_method/file.rb index 99ef5bc76d..571e32df49 100644 --- a/actionmailer/lib/action_mailer/delivery_method/file.rb +++ b/actionmailer/lib/action_mailer/delivery_method/file.rb @@ -6,7 +6,7 @@ module ActionMailer # A delivery method implementation which writes all mails to a file. class File < Method self.settings = { - :location => defined?(Rails) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" } def perform_delivery(mail) diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 351b966abe..9aa178cdef 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -1,17 +1,19 @@ -module MailHelper - # Uses Text::Format to take the text and format it, indented two spaces for - # each line, and wrapped at 72 columns. - def block_format(text) - formatted = text.split(/\n\r\n/).collect { |paragraph| - Text::Format.new( - :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph - ).format - }.join("\n") +module ActionMailer + module MailHelper + # Uses Text::Format to take the text and format it, indented two spaces for + # each line, and wrapped at 72 columns. + def block_format(text) + formatted = text.split(/\n\r\n/).collect { |paragraph| + Text::Format.new( + :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph + ).format + }.join("\n") - # Make list points stand on their own line - formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" } - formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" } + # Make list points stand on their own line + formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" } + formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" } - formatted + formatted + end end -end +end
\ No newline at end of file diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index e84b3b0d23..af6f1bc92e 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -1,12 +1,9 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") - $:.unshift("#{root}/actionpack/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'rubygems' diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 7a47a654cd..dd8307a834 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -31,6 +31,18 @@ class TestMailer < ActionMailer::Base render :text => "Goodbye, Mr. #{recipient}" end + def from_with_name + from "System <system@loudthinking.com>" + recipients "root@loudthinking.com" + body "Nothing to see here." + end + + def from_without_name + from "system@loudthinking.com" + recipients "root@loudthinking.com" + body "Nothing to see here." + end + def cc_bcc(recipient) recipients recipient subject "testing bcc/cc" @@ -514,6 +526,28 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, delivered.encoded end + def test_from_without_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_without_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + + def test_from_with_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_with_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + def test_reply_to expected = new_mail diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 427833b36c..0e565b95e8 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -1,7 +1,9 @@ require 'abstract_unit' -class TestMailer < ActionMailer::Base +class WelcomeController < ActionController::Base +end +class TestMailer < ActionMailer::Base default_url_options[:host] = 'www.basecamphq.com' def signed_up_with_url(recipient) @@ -52,27 +54,25 @@ class ActionMailerUrlTest < Test::Unit::TestCase end def test_signed_up_with_url - ActionController::Routing.use_controllers! ['welcome'] do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - map.welcome 'welcome', :controller=>"foo", :action=>"bar" - end - - expected = new_mail - expected.to = @recipient - expected.subject = "[Signed up] Welcome #{@recipient}" - expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />" - expected.from = "system@loudthinking.com" - expected.date = Time.local(2004, 12, 12) - - created = nil - assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) } - assert_not_nil created - assert_equal expected.encoded, created.encoded - - assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) } - assert_not_nil ActionMailer::Base.deliveries.first - assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + map.welcome 'welcome', :controller=>"foo", :action=>"bar" end + + expected = new_mail + expected.to = @recipient + expected.subject = "[Signed up] Welcome #{@recipient}" + expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />" + expected.from = "system@loudthinking.com" + expected.date = Time.local(2004, 12, 12) + + created = nil + assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) } + assert_not_nil created + assert_equal expected.encoded, created.encoded + + assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) } + assert_not_nil ActionMailer::Base.deliveries.first + assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded end end diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 99bdcc95fa..863daa4b44 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -31,7 +31,6 @@ Rake::TestTask.new(:test_action_pack) do |t| # this will not happen automatically and the tests (as a whole) will error t.test_files = Dir.glob('test/{abstract,controller,dispatch,template}/**/*_test.rb').sort - t.verbose = true # t.warning = true end @@ -45,7 +44,6 @@ desc 'ActiveRecord Integration Tests' Rake::TestTask.new(:test_active_record_integration) do |t| t.libs << 'test' t.test_files = Dir.glob("test/activerecord/*_test.rb") - t.verbose = true end # Genereate the RDoc documentation diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index ccf6bf1e83..23d478fd44 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.0.1') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.2.2') + s.add_dependency('rack-mount', '~> 0.3.2') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 1a6c4278c9..109a3a3385 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -1,16 +1,20 @@ -require "active_support/core_ext/module/attr_internal" -require "active_support/core_ext/module/delegation" +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +require 'active_support' +require 'active_support/core_ext/module/attr_internal' +require 'active_support/core_ext/module/delegation' module AbstractController - autoload :Base, "abstract_controller/base" - autoload :Callbacks, "abstract_controller/callbacks" - autoload :Helpers, "abstract_controller/helpers" - autoload :Layouts, "abstract_controller/layouts" - autoload :LocalizedCache, "abstract_controller/localized_cache" - autoload :Logger, "abstract_controller/logger" - autoload :RenderingController, "abstract_controller/rendering_controller" - # === Exceptions - autoload :ActionNotFound, "abstract_controller/exceptions" - autoload :DoubleRenderError, "abstract_controller/exceptions" - autoload :Error, "abstract_controller/exceptions" + extend ActiveSupport::Autoload + + deferrable do + autoload :Base + autoload :Callbacks + autoload :Helpers + autoload :Layouts + autoload :LocalizedCache + autoload :Logger + autoload :RenderingController + end end diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index f5b1c9e4d1..905d04e20d 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,4 +1,6 @@ module AbstractController + class Error < StandardError; end + class ActionNotFound < StandardError; end class Base attr_internal :response_body @@ -74,7 +76,7 @@ module AbstractController abstract! # Calls the action going through the entire action dispatch stack. - # + # # The actual method that is called is determined by calling # #method_for_action. If no method can handle the action, then an # ActionNotFound error is raised. @@ -88,6 +90,8 @@ module AbstractController raise ActionNotFound, "The action '#{action}' could not be found" end + @_response_body = nil + process_action(action_name) end diff --git a/actionpack/lib/abstract_controller/exceptions.rb b/actionpack/lib/abstract_controller/exceptions.rb deleted file mode 100644 index b671516de1..0000000000 --- a/actionpack/lib/abstract_controller/exceptions.rb +++ /dev/null @@ -1,12 +0,0 @@ -module AbstractController - class Error < StandardError; end - class ActionNotFound < StandardError; end - - class DoubleRenderError < Error - DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end -end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index 7054b9cf26..7f2243d4ef 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -1,6 +1,15 @@ +require "abstract_controller/base" require "abstract_controller/logger" module AbstractController + class DoubleRenderError < Error + DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + module RenderingController extend ActiveSupport::Concern @@ -21,7 +30,7 @@ module AbstractController # An instance of a view class. The default view class is ActionView::Base # # The view class must have the following methods: - # View.for_controller[controller] Create a new ActionView instance for a + # View.for_controller[controller] Create a new ActionView instance for a # controller # View#render_partial[options] # - responsible for setting options[:_template] @@ -115,7 +124,7 @@ module AbstractController # _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial def _determine_template(options) if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(options[:text], format_for_text) + options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) elsif options.key?(:inline) handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) @@ -152,12 +161,12 @@ module AbstractController module ClassMethods def clear_template_caches! end - + # Append a path to the list of view paths for this controller. # # ==== Parameters - # path<String, ViewPath>:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path + # path<String, ViewPath>:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path # (see ActionView::ViewPathSet for more information) def append_view_path(path) self.view_paths << path @@ -166,8 +175,8 @@ module AbstractController # Prepend a path to the list of view paths for this controller. # # ==== Parameters - # path<String, ViewPath>:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path + # path<String, ViewPath>:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path # (see ActionView::ViewPathSet for more information) def prepend_view_path(path) clear_template_caches! diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 03a40e4fce..37ff10e852 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,66 +1,78 @@ +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' + module ActionController - autoload :Base, "action_controller/base" - autoload :Benchmarking, "action_controller/metal/benchmarking" - autoload :ConditionalGet, "action_controller/metal/conditional_get" - autoload :Configuration, "action_controller/metal/configuration" - autoload :Head, "action_controller/metal/head" - autoload :Helpers, "action_controller/metal/helpers" - autoload :HideActions, "action_controller/metal/hide_actions" - autoload :Layouts, "action_controller/metal/layouts" - autoload :Metal, "action_controller/metal" - autoload :Middleware, "action_controller/middleware" - autoload :RackConvenience, "action_controller/metal/rack_convenience" - autoload :Rails2Compatibility, "action_controller/metal/compatibility" - autoload :Redirector, "action_controller/metal/redirector" - autoload :RenderingController, "action_controller/metal/rendering_controller" - autoload :RenderOptions, "action_controller/metal/render_options" - autoload :Rescue, "action_controller/metal/rescuable" - autoload :Responder, "action_controller/metal/responder" - autoload :Session, "action_controller/metal/session" - autoload :Testing, "action_controller/metal/testing" - autoload :UrlFor, "action_controller/metal/url_for" + extend ActiveSupport::Autoload - autoload :Caching, 'action_controller/caching' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' - autoload :MimeResponds, 'action_controller/metal/mime_responds' - autoload :PerformanceTest, 'action_controller/deprecated/performance_test' - autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Routing, 'action_controller/deprecated' - autoload :SessionManagement, 'action_controller/metal/session_management' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :TestProcess, 'action_controller/testing/process' - autoload :UrlRewriter, 'action_controller/url_rewriter' - autoload :UrlWriter, 'action_controller/url_rewriter' + deferrable do + autoload :Base + autoload :Caching + autoload :PolymorphicRoutes + autoload :Translation + autoload :Metal + autoload :Middleware - autoload :Verification, 'action_controller/metal/verification' - autoload :Flash, 'action_controller/metal/flash' - autoload :RequestForgeryProtection, 'action_controller/metal/request_forgery_protection' - autoload :Streaming, 'action_controller/metal/streaming' - autoload :HttpAuthentication, 'action_controller/metal/http_authentication' - autoload :FilterParameterLogging, 'action_controller/metal/filter_parameter_logging' - autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/metal/cookies' + autoload_under "metal" do + autoload :Benchmarking + autoload :ConditionalGet + autoload :Configuration + autoload :Head + autoload :Helpers + autoload :HideActions + autoload :Layouts + autoload :MimeResponds + autoload :RackConvenience + autoload :Compatibility + autoload :Redirector + autoload :RenderingController + autoload :RenderOptions + autoload :Rescue + autoload :Responder + autoload :Session + autoload :SessionManagement + autoload :UrlFor + autoload :Verification + autoload :Flash + autoload :RequestForgeryProtection + autoload :Streaming + autoload :HttpAuthentication + autoload :FilterParameterLogging + autoload :Cookies + end - autoload :ActionControllerError, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :RoutingError, 'action_controller/metal/exceptions' - autoload :MethodNotAllowed, 'action_controller/metal/exceptions' - autoload :NotImplemented, 'action_controller/metal/exceptions' - autoload :UnknownController, 'action_controller/metal/exceptions' - autoload :MissingFile, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :SessionOverflowError, 'action_controller/metal/exceptions' - autoload :UnknownHttpMethod, 'action_controller/metal/exceptions' -end + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :Routing, 'action_controller/deprecated' + autoload :Integration, 'action_controller/deprecated/integration_test' + autoload :IntegrationTest, 'action_controller/deprecated/integration_test' + end -autoload :HTML, 'action_controller/vendor/html-scanner' -autoload :AbstractController, 'abstract_controller' + autoload :RecordIdentifier + autoload :UrlRewriter + autoload :UrlWriter, 'action_controller/url_rewriter' + + # TODO: Don't autoload exceptions, setup explicit + # requires for files that need them + autoload_at "action_controller/metal/exceptions" do + autoload :ActionControllerError + autoload :RenderError + autoload :RoutingError + autoload :MethodNotAllowed + autoload :NotImplemented + autoload :UnknownController + autoload :MissingFile + autoload :RenderError + autoload :SessionOverflowError + autoload :UnknownHttpMethod + end +end +# All of these simply register additional autoloads +require 'abstract_controller' require 'action_dispatch' require 'action_view' +require 'action_controller/vendor/html-scanner' # Common ActiveSupport usage in ActionController require "active_support/concern" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4c026fe5f7..48bfbab215 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -19,12 +19,11 @@ module ActionController # Legacy modules include SessionManagement - include ActionDispatch::StatusCodes include ActionController::Caching include ActionController::MimeResponds # Rails 2.x compatibility - include ActionController::Rails2Compatibility + include ActionController::Compatibility include ActionController::Cookies include ActionController::Session @@ -91,7 +90,7 @@ module ActionController end if options[:status] - options[:status] = interpret_status(options[:status]).to_i + options[:status] = _interpret_status(options[:status]) end options[:update] = blk if block_given? @@ -140,9 +139,9 @@ module ActionController raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? status = if options.is_a?(Hash) && options.key?(:status) - interpret_status(options.delete(:status)) + _interpret_status(options.delete(:status)) elsif response_status.key?(:status) - interpret_status(response_status[:status]) + _interpret_status(response_status[:status]) else 302 end @@ -164,5 +163,14 @@ module ActionController super(url, status) end + + private + def _interpret_status(status) + if status.is_a?(Symbol) + (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) + else + status.to_i + end + end end end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 3caf759032..ad357cceda 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -30,10 +30,11 @@ module ActionController #:nodoc: # config.action_controller.cache_store = MyOwnStore.new("parameter") module Caching extend ActiveSupport::Concern + extend ActiveSupport::Autoload - autoload :Actions, 'action_controller/caching/actions' - autoload :Fragments, 'action_controller/caching/fragments' - autoload :Pages, 'action_controller/caching/pages' + autoload :Actions + autoload :Fragments + autoload :Pages autoload :Sweeper, 'action_controller/caching/sweeping' autoload :Sweeping, 'action_controller/caching/sweeping' diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb index e04da42637..cf02757cf6 100644 --- a/actionpack/lib/action_controller/dispatch/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb @@ -13,11 +13,6 @@ module ActionController # Run prepare callbacks before every request in development mode self.prepare_each_request = true - # Development mode callbacks - ActionDispatch::Callbacks.before_dispatch do |app| - ActionController::Routing::Routes.reload - end - ActionDispatch::Callbacks.after_dispatch do # Cleanup the application before processing the current request. ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index c251d79f4e..a90f798cd5 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -1,5 +1,5 @@ module ActionController - module Rails2Compatibility + module Compatibility extend ActiveSupport::Concern class ::ActionController::ActionControllerError < StandardError #:nodoc: @@ -46,11 +46,8 @@ module ActionController cattr_accessor :use_accept_header self.use_accept_header = true - cattr_accessor :page_cache_directory self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" - cattr_reader :cache_store - cattr_accessor :consider_all_requests_local self.consider_all_requests_local = true @@ -116,7 +113,7 @@ module ActionController details[:prefix] = nil if name =~ /\blayouts/ super end - + # Move this into a "don't run in production" module def _default_layout(details, require_layout = false) super diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index b9d23da3e0..07024d0a9a 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -18,18 +18,9 @@ module ActionController def initialize(*allowed_methods) super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - - def handle_response!(response) - response.headers['Allow'] ||= allowed_methods_header end end - + class NotImplemented < MethodNotAllowed #:nodoc: end diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index f43900faa0..9d08ed6081 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -133,10 +133,23 @@ module ActionController #:nodoc: Array(key || keys).each { |k| used ? @used << k : @used.delete(k) } return key ? self[key] : self end + end + + # Access the contents of the flash. Use <tt>flash["notice"]</tt> to + # read a notice you put there or <tt>flash["notice"] = "hello"</tt> + # to put a new one. + def flash #:doc: + unless @_flash + @_flash = session["flash"] || FlashHash.new + @_flash.sweep end + @_flash + end + protected def process_action(method_name) + @_flash = nil super @_flash.store(session) if @_flash @_flash = nil @@ -146,17 +159,5 @@ module ActionController #:nodoc: super @_flash = nil end - - # Access the contents of the flash. Use <tt>flash["notice"]</tt> to - # read a notice you put there or <tt>flash["notice"] = "hello"</tt> - # to put a new one. - def flash #:doc: - unless @_flash - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end - - @_flash - end end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index b4325e24ad..d0402e5bad 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -52,7 +52,7 @@ module ActionController included do # Set the default directory for helpers extlib_inheritable_accessor(:helpers_dir) do - defined?(Rails) ? "#{Rails.root}/app/helpers" : "app/helpers" + defined?(Rails.root) ? "#{Rails.root}/app/helpers" : "app/helpers" end end diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb index 0d69ca10df..b6a7ca0eda 100644 --- a/actionpack/lib/action_controller/metal/render_options.rb +++ b/actionpack/lib/action_controller/metal/render_options.rb @@ -1,19 +1,24 @@ module ActionController + + def self.add_renderer(key, &block) + RenderOptions.add(key, &block) + end + module RenderOptions extend ActiveSupport::Concern included do extlib_inheritable_accessor :_renderers - self._renderers = [] + self._renderers = {} end module ClassMethods def _write_render_options - renderers = _renderers.map do |r| + renderers = _renderers.map do |name, value| <<-RUBY_EVAL - if options.key?(:#{r}) + if options.key?(:#{name}) _process_options(options) - return render_#{r}(options[:#{r}], options) + return _render_option_#{name}(options[:#{name}], options) end RUBY_EVAL end @@ -25,79 +30,63 @@ module ActionController RUBY_EVAL end - def _add_render_option(name) - _renderers << name + def use_renderers(*args) + args.each do |key| + _renderers[key] = RENDERERS[key] + end _write_render_options end + alias use_renderer use_renderers end def render_to_body(options) _handle_render_options(options) || super end - end - - module RenderOption #:nodoc: - def self.extended(base) - base.extend ActiveSupport::Concern - base.send :include, ::ActionController::RenderOptions - def base.register_renderer(name) - included { _add_render_option(name) } - end + RENDERERS = {} + def self.add(key, &block) + define_method("_render_option_#{key}", &block) + RENDERERS[key] = block + All._write_render_options end - end - module RenderOptions - module Json - extend RenderOption - register_renderer :json + module All + extend ActiveSupport::Concern + include RenderOptions - def render_json(json, options) - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - self.content_type ||= Mime::JSON - self.response_body = json + INCLUDED = [] + included do + self._renderers = RENDERERS + _write_render_options + INCLUDED << self end - end - - module Js - extend RenderOption - register_renderer :js - def render_js(js, options) - self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js : js + def self._write_render_options + INCLUDED.each(&:_write_render_options) end end - module Xml - extend RenderOption - register_renderer :xml - - def render_xml(xml, options) - self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml - end + add :json do |json, options| + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + self.content_type ||= Mime::JSON + self.response_body = json end - module RJS - extend RenderOption - register_renderer :update - - def render_update(proc, options) - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) - self.content_type = Mime::JS - self.response_body = generator.to_s - end + add :js do |js, options| + self.content_type ||= Mime::JS + self.response_body = js.respond_to?(:to_js) ? js.to_js : js end - module All - extend ActiveSupport::Concern + add :xml do |xml, options| + self.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end - include ActionController::RenderOptions::Json - include ActionController::RenderOptions::Js - include ActionController::RenderOptions::Xml - include ActionController::RenderOptions::RJS + add :update do |proc, options| + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) + self.content_type = Mime::JS + self.response_body = generator.to_s end end end diff --git a/actionpack/lib/action_controller/metal/rescuable.rb b/actionpack/lib/action_controller/metal/rescue.rb index bbca1b2179..bbca1b2179 100644 --- a/actionpack/lib/action_controller/metal/rescuable.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index e8e88e7479..cb0e600871 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -80,6 +80,11 @@ module ActionController #:nodoc: class Responder attr_reader :controller, :request, :format, :resource, :resources, :options + ACTIONS_FOR_VERBS = { + :post => :new, + :put => :edit + } + def initialize(controller, resources, options={}) @controller = controller @request = controller.request @@ -102,9 +107,14 @@ module ActionController #:nodoc: # not defined, call to_format. # def self.call(*args) - responder = new(*args) - method = :"to_#{responder.format}" - responder.respond_to?(method) ? responder.send(method) : responder.to_format + new(*args).respond + end + + # Main entry point for responder responsible to dispatch to the proper format. + # + def respond + method = :"to_#{format}" + respond_to?(method) ? send(method) : to_format end # HTML format does not render the resource, it always attempt to render a @@ -133,7 +143,7 @@ module ActionController #:nodoc: def navigation_behavior(error) if get? raise error - elsif has_errors? + elsif has_errors? && default_action render :action => default_action else redirect_to resource_location @@ -204,7 +214,7 @@ module ActionController #:nodoc: # the verb is post. # def default_action - @action || (request.post? ? :new : :edit) + @action ||= ACTIONS_FOR_VERBS[request.method] end end end diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 01a55fe930..398ea52495 100644 --- a/actionpack/lib/action_controller/testing/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,5 +1,6 @@ require 'active_support/test_case' require 'rack/session/abstract/id' +require 'action_controller/metal/testing' module ActionController class TestRequest < ActionDispatch::TestRequest #:nodoc: @@ -180,7 +181,7 @@ module ActionController # # assert_redirected_to page_url(:title => 'foo') class TestCase < ActiveSupport::TestCase - include TestProcess + include ActionDispatch::TestProcess # Executes a request simulating GET HTTP method and set/volley the response def get(action, parameters = nil, session = nil, flash = nil) diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb deleted file mode 100644 index 323cce6a2f..0000000000 --- a/actionpack/lib/action_controller/testing/process.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'active_support/core_ext/object/conversions' -require "rack/test" - -module ActionController #:nodoc: - # Essentially generates a modified Tempfile object similar to the object - # you'd get from the standard library CGI module in a multipart - # request. This means you can use an ActionController::TestUploadedFile - # object in the params of a test request in order to simulate - # a file upload. - # - # Usage example, within a functional test: - # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png') - # - # Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows): - # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary) - TestUploadedFile = Rack::Test::UploadedFile - - module TestProcess - def assigns(key = nil) - assigns = {} - @controller.instance_variable_names.each do |ivar| - next if ActionController::Base.protected_instance_variables.include?(ivar) - assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) - end - - key.nil? ? assigns : assigns[key.to_s] - end - - def session - @request.session - end - - def flash - @request.flash - end - - def cookies - @request.cookies.merge(@response.cookies) - end - - def redirect_to_url - @response.redirect_url - end - - def html_document - xml = @response.content_type =~ /xml$/ - @html_document ||= HTML::Document.new(@response.body, false, xml) - end - - def find_tag(conditions) - html_document.find(conditions) - end - - def find_all_tag(conditions) - html_document.find_all(conditions) - end - - def method_missing(selector, *args, &block) - if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector) - @controller.send(selector, *args, &block) - else - super - end - end - - # Shortcut for <tt>ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>: - # - # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') - # - # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter. - # This will not affect other platforms: - # - # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) - def fixture_file_upload(path, mime_type = nil, binary = false) - fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) - ActionController::TestUploadedFile.new("#{fixture_path}#{path}", mime_type, binary) - end - - # A helper to make it easier to test different route configurations. - # This method temporarily replaces ActionController::Routing::Routes - # with a new RouteSet instance. - # - # The new instance is yielded to the passed block. Typically the block - # will create some routes using <tt>map.draw { map.connect ... }</tt>: - # - # with_routing do |set| - # set.draw do |map| - # map.connect ':controller/:action/:id' - # assert_equal( - # ['/content/10/show', {}], - # map.generate(:controller => 'content', :id => 10, :action => 'show') - # end - # end - # end - # - def with_routing - real_routes = ActionController::Routing::Routes - ActionController::Routing.module_eval { remove_const :Routes } - - temporary_routes = ActionController::Routing::RouteSet.new - ActionController::Routing.module_eval { const_set :Routes, temporary_routes } - - yield temporary_routes - ensure - if ActionController::Routing.const_defined? :Routes - ActionController::Routing.module_eval { remove_const :Routes } - end - ActionController::Routing.const_set(:Routes, real_routes) if real_routes - end - end -end diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb index f622d195ee..2cb20ddd05 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner.rb @@ -1,6 +1,8 @@ $LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner" module HTML + extend ActiveSupport::Autoload + autoload :CDATA, 'html/node' autoload :Document, 'html/document' autoload :FullSanitizer, 'html/sanitizer' diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 259814a322..feed6a8e25 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -21,6 +21,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' + require 'rack' module Rack @@ -28,43 +32,49 @@ module Rack end module ActionDispatch - autoload :Request, 'action_dispatch/http/request' - autoload :Response, 'action_dispatch/http/response' - autoload :StatusCodes, 'action_dispatch/http/status_codes' - autoload :Utils, 'action_dispatch/http/utils' + extend ActiveSupport::Autoload - autoload :Callbacks, 'action_dispatch/middleware/callbacks' - autoload :MiddlewareStack, 'action_dispatch/middleware/stack' - autoload :ParamsParser, 'action_dispatch/middleware/params_parser' - autoload :Rescue, 'action_dispatch/middleware/rescue' - autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions' - autoload :Static, 'action_dispatch/middleware/static' - autoload :StringCoercion, 'action_dispatch/middleware/string_coercion' + autoload_under 'http' do + autoload :Request + autoload :Response + autoload :StatusCodes + autoload :Utils + end - autoload :Routing, 'action_dispatch/routing' + deferrable do + autoload_under 'middleware' do + autoload :Callbacks + autoload :ParamsParser + autoload :Rescue + autoload :ShowExceptions + autoload :Static + autoload :StringCoercion + end - autoload :Assertions, 'action_dispatch/testing/assertions' - autoload :Integration, 'action_dispatch/testing/integration' - autoload :IntegrationTest, 'action_dispatch/testing/integration' - autoload :PerformanceTest, 'action_dispatch/testing/performance_test' - autoload :TestRequest, 'action_dispatch/testing/test_request' - autoload :TestResponse, 'action_dispatch/testing/test_response' + autoload :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Routing - autoload :HTML, 'action_controller/vendor/html-scanner' + module Http + autoload :Headers, 'action_dispatch/http/headers' + end - module Http - autoload :Headers, 'action_dispatch/http/headers' - end + module Session + autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' + end - module Session - autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' - autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' + autoload_under 'testing' do + autoload :Assertions + autoload :Integration + autoload :PerformanceTest + autoload :TestProcess + autoload :TestRequest + autoload :TestResponse + end end + + autoload :HTML, 'action_controller/vendor/html-scanner' end autoload :Mime, 'action_dispatch/http/mime_type' - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 6a52854961..7d1f5a4504 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -6,6 +6,7 @@ require 'active_support/memoizable' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/string/access' +require 'action_dispatch/http/headers' module ActionDispatch class Request < Rack::Request @@ -117,7 +118,7 @@ module ActionDispatch end end end - + def if_modified_since if since = env['HTTP_IF_MODIFIED_SINCE'] Time.rfc2822(since) rescue nil diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index b3ed7c9d1a..378fd5e61d 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -33,7 +33,6 @@ module ActionDispatch # :nodoc: # end class Response < Rack::Response attr_accessor :request, :blank - attr_reader :cache_control attr_writer :header, :sending_file alias_method :headers=, :header= @@ -50,6 +49,9 @@ module ActionDispatch # :nodoc: @body, @cookie = [], [] @sending_file = false + @blank = false + @etag = nil + yield self if block_given? end @@ -57,12 +59,6 @@ module ActionDispatch # :nodoc: @cache_control ||= {} end - def write(str) - s = str.to_s - @writer.call s - str - end - def status=(status) @status = status.to_i end @@ -277,7 +273,7 @@ module ActionDispatch # :nodoc: max_age = control[:max_age] options = [] - options << "max-age=#{max_age}" if max_age + options << "max-age=#{max_age.to_i}" if max_age options << (control[:public] ? "public" : "private") options << "must-revalidate" if control[:must_revalidate] options.concat(extras) if extras diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb index 5bac842ec1..ea1475e827 100644 --- a/actionpack/lib/action_dispatch/http/status_codes.rb +++ b/actionpack/lib/action_dispatch/http/status_codes.rb @@ -21,22 +21,5 @@ module ActionDispatch hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code hash }.freeze - - private - # Given a status parameter, determine whether it needs to be converted - # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup - # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE - # hash to convert it. - def interpret_status(status) - case status - when Fixnum then - "#{status} #{STATUS_CODES[status]}".strip - when Symbol then - interpret_status(SYMBOL_TO_STATUS_CODE[status] || - "500 Unknown Status #{status.inspect}") - else - status.to_s - end - end end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 036deec6d2..bd87764f5b 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -2,8 +2,6 @@ require "active_support/core_ext/exception" module ActionDispatch class ShowExceptions - include StatusCodes - LOCALHOST = '127.0.0.1'.freeze RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') @@ -12,8 +10,7 @@ module ActionDispatch @@rescue_responses = Hash.new(:internal_server_error) @@rescue_responses.update({ 'ActionController::RoutingError' => :not_found, - # TODO: Clean this up after the switch - ActionController::UnknownAction.name => :not_found, + 'AbstractController::ActionNotFound' => :not_found, 'ActiveRecord::RecordNotFound' => :not_found, 'ActiveRecord::StaleObjectError' => :conflict, 'ActiveRecord::RecordInvalid' => :unprocessable_entity, @@ -28,8 +25,8 @@ module ActionDispatch @@rescue_templates.update({ 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', - ActionController::UnknownAction.name => 'unknown_action', - 'ActionView::TemplateError' => 'template_error' + 'AbstractController::ActionNotFound' => 'unknown_action', + 'ActionView::Template::Error' => 'template_error' }) FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, @@ -104,7 +101,7 @@ module ActionDispatch end def status_code(exception) - interpret_status(@@rescue_responses[exception.class.name]).to_i + ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[@@rescue_responses[exception.class.name]] end def render(status, body) @@ -119,7 +116,7 @@ module ActionDispatch return unless logger ActiveSupport::Deprecation.silence do - if ActionView::TemplateError === exception + if ActionView::Template::Error === exception logger.fatal(exception.to_s) else logger.fatal( diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb index f8f6b424ca..07b4919934 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb @@ -7,7 +7,7 @@ names = traces.collect {|name, trace| name} %> -<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p> +<p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p> <div id="traces"> <% names.each do |name| %> diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 9b977800b4..e99f979197 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -266,77 +266,9 @@ module ActionDispatch SEPARATORS = %w( / . ? ) HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] - # The root paths which may contain controller files - mattr_accessor :controller_paths - self.controller_paths = [] - # A helper module to hold URL related helpers. module Helpers include ActionController::PolymorphicRoutes end - - class << self - def controller_constraints - @controller_constraints ||= Regexp.union(*possible_controllers.collect { |n| Regexp.escape(n) }) - end - - def clear_controller_cache! - @controller_constraints = nil - end - - private - # Returns the array of controller names currently available to ActionController::Routing. - def possible_controllers - possible_controllers = [] - - # Find any controller classes already in memory - ActionController::Base.subclasses.each do |klass| - controller_name = klass.underscore - controller_name.gsub!(/_controller\Z/, '') - possible_controllers << controller_name - end - - # Find controllers in controllers/ directory - paths = controller_paths.select { |path| File.directory?(path) && path != "." } - seen_paths = Hash.new {|h, k| h[k] = true; false} - normalize_paths(paths).each do |load_path| - Dir["#{load_path}/**/*_controller.rb"].collect do |path| - next if seen_paths[path.gsub(%r{^\.[/\\]}, "")] - - controller_name = path[(load_path.length + 1)..-1] - - controller_name.gsub!(/_controller\.rb\Z/, '') - possible_controllers << controller_name - end - end - - # remove duplicates - possible_controllers.uniq! - - possible_controllers - end - - # Returns an array of paths, cleaned of double-slashes and relative path references. - # * "\\\" and "//" become "\\" or "/". - # * "/foo/bar/../config" becomes "/foo/config". - # The returned array is sorted by length, descending. - def normalize_paths(paths) - # do the hokey-pokey of path normalization... - paths = paths.collect do |path| - path = path. - gsub("//", "/"). # replace double / chars with a single - gsub("\\\\", "\\"). # replace double \ chars with a single - gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it - - # eliminate .. paths where possible - re = %r{[^/\\]+[/\\]\.\.[/\\]} - path.gsub!(re, "") while path.match(re) - path - end - - # start with longest path, first - paths = paths.uniq.sort_by { |path| - path.length } - end - end end end diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index dd76391870..8ce6b2f6d5 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -113,7 +113,7 @@ module ActionDispatch end end - requirements[:controller] ||= Routing.controller_constraints + requirements[:controller] ||= @set.controller_constraints if defaults[:controller] defaults[:action] ||= 'index' @@ -175,7 +175,7 @@ module ActionDispatch optional = false elsif segment =~ /^:(\w+)$/ if defaults.has_key?($1.to_sym) - defaults.delete($1.to_sym) + defaults.delete($1.to_sym) if defaults[$1.to_sym].nil? else optional = false end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index cfe7425a61..d480af876d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1,153 +1,192 @@ module ActionDispatch module Routing class Mapper - module Resources - def resource(*resources, &block) - options = resources.last.is_a?(Hash) ? resources.pop : {} - - if resources.length > 1 - raise ArgumentError if block_given? - resources.each { |r| resource(r, options) } - return self + class Constraints + def self.new(app, constraints = []) + if constraints.any? + super(app, constraints) + else + app end + end - resource = resources.pop + def initialize(app, constraints = []) + @app, @constraints = app, constraints + end - if @scope[:scope_level] == :resources - member do - resource(resource, options, &block) - end - return self - end + def call(env) + req = Rack::Request.new(env) - singular = resource.to_s - plural = singular.pluralize + @constraints.each { |constraint| + if constraint.respond_to?(:matches?) && !constraint.matches?(req) + return [417, {}, []] + elsif constraint.respond_to?(:call) && !constraint.call(req) + return [417, {}, []] + end + } - controller(plural) do - namespace(resource) do - with_scope_level(:resource) do - yield if block_given? + @app.call(env) + end + end - get "", :to => :show, :as => "#{singular}" - post "", :to => :create - put "", :to => :update - delete "", :to => :destroy - get "new", :to => :new, :as => "new_#{singular}" - get "edit", :to => :edit, :as => "edit_#{singular}" - end - end - end + module Base + def initialize(set) + @set = set + end - self + def root(options = {}) + match '/', options.merge(:as => :root) end - def resources(*resources, &block) - options = resources.last.is_a?(Hash) ? resources.pop : {} + def match(*args) + options = args.extract_options! - if resources.length > 1 - raise ArgumentError if block_given? - resources.each { |r| resources(r, options) } - return self - end + path = args.first - resource = resources.pop + conditions, defaults = {}, {} - plural = resource.to_s - singular = plural.singularize + path = nil if path == "" + path = "#{@scope[:path]}#{path}" if @scope[:path] + path = Rack::Mount::Utils.normalize_path(path) if path - if @scope[:scope_level] == :resources - parent_resource = @scope[:scope_level_options][:name] - with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_resource) do - resources(resource, options, &block) - end - end - return self - end + raise ArgumentError, "path is required" unless path - if @scope[:options] && (prefix = @scope[:options][:name_prefix]) - plural = "#{prefix}_#{plural}" - singular = "#{prefix}_#{singular}" + constraints = options[:constraints] || {} + unless constraints.is_a?(Hash) + block, constraints = constraints, {} end + blocks = ((@scope[:blocks] || []) + [block]).compact + constraints = (@scope[:constraints] || {}).merge(constraints) + options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - controller(resource) do - namespace(resource) do - with_scope_level(:resources, :name => singular) do - yield if block_given? + conditions[:path_info] = path + requirements = constraints.dup - member do - get "", :to => :show, :as => singular - put "", :to => :update - delete "", :to => :destroy - get "edit", :to => :edit, :as => "edit_#{singular}" - end + path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) + segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names + constraints.reject! { |k, v| segment_keys.include?(k.to_s) } + conditions.merge!(constraints) - collection do - get "", :to => :index, :as => plural - post "", :to => :create - get "new", :to => :new, :as => "new_#{singular}" - end - end - end + requirements[:controller] ||= @set.controller_constraints + + if via = options[:via] + via = Array(via).map { |m| m.to_s.upcase } + conditions[:request_method] = Regexp.union(*via) end + defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] + + app = initialize_app_endpoint(options, defaults) + validate_defaults!(app, defaults, segment_keys) + app = Constraints.new(app, blocks) + + @set.add_route(app, conditions, requirements, defaults, options[:as]) + self end - def collection - unless @scope[:scope_level] == :resources - raise ArgumentError, "can't use collection outside resources scope" - end + private + def initialize_app_endpoint(options, defaults) + app = nil + + if options[:to].respond_to?(:call) + app = options[:to] + defaults.delete(:controller) + defaults.delete(:action) + elsif options[:to].is_a?(String) + defaults[:controller], defaults[:action] = options[:to].split('#') + elsif options[:to].is_a?(Symbol) + defaults[:action] = options[:to].to_s + end - with_scope_level(:collection) do - yield + app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) end - end - def member - unless @scope[:scope_level] == :resources - raise ArgumentError, "can't use member outside resources scope" - end + def validate_defaults!(app, defaults, segment_keys) + return unless app.is_a?(Routing::RouteSet::Dispatcher) - with_scope_level(:member) do - scope(":id") do - yield + unless defaults.include?(:controller) || segment_keys.include?("controller") + raise ArgumentError, "missing :controller" + end + + unless defaults.include?(:action) || segment_keys.include?("action") + raise ArgumentError, "missing :action" end end + end + + module HttpHelpers + def get(*args, &block) + map_method(:get, *args, &block) end - def match(*args) - options = args.last.is_a?(Hash) ? args.pop : {} - args.push(options) + def post(*args, &block) + map_method(:post, *args, &block) + end - case options.delete(:on) - when :collection - return collection { match(*args) } - when :member - return member { match(*args) } - end + def put(*args, &block) + map_method(:put, *args, &block) + end - if @scope[:scope_level] == :resources - raise ArgumentError, "can't define route directly in resources scope" - end + def delete(*args, &block) + map_method(:delete, *args, &block) + end - super + def redirect(path, options = {}) + status = options[:status] || 301 + lambda { |env| + req = Rack::Request.new(env) + url = req.scheme + '://' + req.host + path + [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] + } end private - def with_scope_level(kind, options = {}) - old, @scope[:scope_level] = @scope[:scope_level], kind - old_options, @scope[:scope_level_options] = @scope[:scope_level_options], options - yield - ensure - @scope[:scope_level] = old - @scope[:scope_level_options] = old_options + def map_method(method, *args, &block) + options = args.extract_options! + options[:via] = method + args.push(options) + match(*args, &block) + self end end module Scoping + def initialize(*args) + @scope = {} + super + end + def scope(*args) - options = args.last.is_a?(Hash) ? args.pop : {} + options = args.extract_options! + + case args.first + when String + options[:path] = args.first + when Symbol + options[:controller] = args.first + end + + if path = options.delete(:path) + path_set = true + path, @scope[:path] = @scope[:path], Rack::Mount::Utils.normalize_path(@scope[:path].to_s + path.to_s) + else + path_set = false + end + + if name_prefix = options.delete(:name_prefix) + name_prefix_set = true + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix) + else + name_prefix_set = false + end + + if controller = options.delete(:controller) + controller_set = true + controller, @scope[:controller] = @scope[:controller], controller + else + controller_set = false + end constraints = options.delete(:constraints) || {} unless constraints.is_a?(Hash) @@ -158,24 +197,12 @@ module ActionDispatch options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options) - path_set = controller_set = false - - case args.first - when String - path_set = true - path = args.first - path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" - when Symbol - controller_set = true - controller = args.first - controller, @scope[:controller] = @scope[:controller], controller - end - yield self ensure @scope[:path] = path if path_set + @scope[:name_prefix] = name_prefix if name_prefix_set @scope[:controller] = controller if controller_set @scope[:options] = options @scope[:blocks] = blocks @@ -187,153 +214,253 @@ module ActionDispatch end def namespace(path) - scope(path.to_s) { yield } + scope("/#{path}") { yield } end def constraints(constraints = {}) scope(:constraints => constraints) { yield } end - end - class Constraints - def initialize(app, constraints = []) - @app, @constraints = app, constraints - end + def match(*args) + options = args.extract_options! - def call(env) - req = Rack::Request.new(env) + options = (@scope[:options] || {}).merge(options) - @constraints.each { |constraint| - if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [417, {}, []] - elsif constraint.respond_to?(:call) && !constraint.call(req) - return [417, {}, []] - end - } + if @scope[:name_prefix] && !options[:as].blank? + options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" + elsif @scope[:name_prefix] && options[:as] == "" + options[:as] = @scope[:name_prefix].to_s + end - @app.call(env) + args.push(options) + super(*args) end end - def initialize(set) - @set = set - @scope = {} - - extend Scoping - extend Resources - end + module Resources + class Resource #:nodoc: + attr_reader :plural, :singular - def get(*args, &block) - map_method(:get, *args, &block) - end + def initialize(entities, options = {}) + entities = entities.to_s - def post(*args, &block) - map_method(:post, *args, &block) - end + @plural = entities.pluralize + @singular = entities.singularize + end - def put(*args, &block) - map_method(:put, *args, &block) - end + def name + plural + end - def delete(*args, &block) - map_method(:delete, *args, &block) - end + def controller + plural + end - def root(options = {}) - match '/', options.merge(:as => :root) - end + def member_name + singular + end - def match(*args) - options = args.last.is_a?(Hash) ? args.pop : {} + def collection_name + plural + end - if args.length > 1 - args.each { |path| match(path, options) } - return self + def id_segment + ":#{singular}_id" + end end - if args.first.is_a?(Symbol) - return match(args.first.to_s, options.merge(:to => args.first.to_sym)) + class SingletonResource < Resource #:nodoc: + def initialize(entity, options = {}) + super + end + + def name + singular + end end - path = args.first + def resource(*resources, &block) + options = resources.extract_options! - options = (@scope[:options] || {}).merge(options) - conditions, defaults = {}, {} + if resources.length > 1 + raise ArgumentError if block_given? + resources.each { |r| resource(r, options) } + return self + end + + resource = SingletonResource.new(resources.pop) + + if @scope[:scope_level] == :resources + nested do + resource(resource.name, options, &block) + end + return self + end - path = nil if path == "" - path = Rack::Mount::Utils.normalize_path(path) if path - path = "#{@scope[:path]}#{path}" if @scope[:path] + scope(:path => "/#{resource.name}", :controller => resource.controller) do + with_scope_level(:resource, resource) do + yield if block_given? - raise ArgumentError, "path is required" unless path + get "(.:format)", :to => :show, :as => resource.member_name + post "(.:format)", :to => :create + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" + end + end - constraints = options[:constraints] || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} + self end - blocks = ((@scope[:blocks] || []) + [block]).compact - constraints = (@scope[:constraints] || {}).merge(constraints) - options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - conditions[:path_info] = path - requirements = constraints.dup + def resources(*resources, &block) + options = resources.extract_options! + + if resources.length > 1 + raise ArgumentError if block_given? + resources.each { |r| resources(r, options) } + return self + end + + resource = Resource.new(resources.pop) + + if @scope[:scope_level] == :resources + nested do + resources(resource.name, options, &block) + end + return self + end + + scope(:path => "/#{resource.name}", :controller => resource.controller) do + with_scope_level(:resources, resource) do + yield if block_given? + + with_scope_level(:collection) do + get "(.:format)", :to => :index, :as => resource.collection_name + post "(.:format)", :to => :create + with_exclusive_name_prefix :new do + get "/new(.:format)", :to => :new, :as => resource.singular + end + end + + with_scope_level(:member) do + scope("/:id") do + get "(.:format)", :to => :show, :as => resource.member_name + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + with_exclusive_name_prefix :edit do + get "/edit(.:format)", :to => :edit, :as => resource.singular + end + end + end + end + end - path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) - segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names - constraints.reject! { |k, v| segment_keys.include?(k.to_s) } - conditions.merge!(constraints) + self + end - requirements[:controller] ||= Routing.controller_constraints + def collection + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use collection outside resources scope" + end - if via = options[:via] - via = Array(via).map { |m| m.to_s.upcase } - conditions[:request_method] = Regexp.union(*via) + with_scope_level(:collection) do + scope(:name_prefix => parent_resource.collection_name, :as => "") do + yield + end + end end - defaults[:controller] = @scope[:controller].to_s if @scope[:controller] + def member + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use member outside resources scope" + end - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s + with_scope_level(:member) do + scope("/:id", :name_prefix => parent_resource.member_name, :as => "") do + yield + end + end end - app ||= Routing::RouteSet::Dispatcher.new(:defaults => defaults) - if app.is_a?(Routing::RouteSet::Dispatcher) - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" + def nested + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use nested outside resources scope" end - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" + + with_scope_level(:nested) do + scope("/#{parent_resource.id_segment}", :name_prefix => parent_resource.member_name) do + yield + end end end - app = Constraints.new(app, blocks) if blocks.any? - @set.add_route(app, conditions, requirements, defaults, options[:as]) + def match(*args) + options = args.extract_options! - self - end + if args.length > 1 + args.each { |path| match(path, options) } + return self + end - def redirect(path, options = {}) - status = options[:status] || 301 - lambda { |env| - req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } - end + if args.first.is_a?(Symbol) + with_exclusive_name_prefix(args.first) do + return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym)) + end + end - private - def map_method(method, *args, &block) - options = args.last.is_a?(Hash) ? args.pop : {} - options[:via] = method args.push(options) - match(*args, &block) - self + + case options.delete(:on) + when :collection + return collection { match(*args) } + when :member + return member { match(*args) } + end + + if @scope[:scope_level] == :resources + raise ArgumentError, "can't define route directly in resources scope" + end + + super end + + protected + def parent_resource + @scope[:scope_level_resource] + end + + private + def with_exclusive_name_prefix(prefix) + begin + old_name_prefix = @scope[:name_prefix] + + if !old_name_prefix.blank? + @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}" + else + @scope[:name_prefix] = prefix.to_s + end + + yield + ensure + @scope[:name_prefix] = old_name_prefix + end + end + + def with_scope_level(kind, resource = parent_resource) + old, @scope[:scope_level] = @scope[:scope_level], kind + old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource + yield + ensure + @scope[:scope_level] = old + @scope[:scope_level_resource] = old_resource + end + end + + include Base + include HttpHelpers + include Scoping + include Resources end end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 79e15edeaa..bf2443c1be 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -18,31 +18,37 @@ module ActionDispatch def call(env) params = env[PARAMETERS_KEY] + prepare_params!(params) + + unless controller = controller(params) + return [417, {}, []] + end + + controller.action(params[:action]).call(env) + end + + def prepare_params!(params) merge_default_action!(params) split_glob_param!(params) if @glob_param + params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding) params[key] = URI.unescape(value) end end + end - if env['action_controller.recognize'] - [200, {}, params] - else - controller = controller(params) - controller.action(params[:action]).call(env) + def controller(params) + if params && params.has_key?(:controller) + controller = "#{params[:controller].camelize}Controller" + ActiveSupport::Inflector.constantize(controller) end + rescue NameError + nil end private - def controller(params) - if params && params.has_key?(:controller) - controller = "#{params[:controller].camelize}Controller" - ActiveSupport::Inflector.constantize(controller) - end - end - def merge_default_action!(params) params[:action] ||= 'index' end @@ -197,26 +203,40 @@ module ActionDispatch end end - attr_accessor :routes, :named_routes, :configuration_files + attr_accessor :routes, :named_routes + attr_accessor :disable_clear_and_finalize def initialize - self.configuration_files = [] - self.routes = [] self.named_routes = NamedRouteCollection.new - clear! + @disable_clear_and_finalize = false end def draw(&block) - clear! - Mapper.new(self).instance_exec(DeprecatedMapper.new(self), &block) + clear! unless @disable_clear_and_finalize + + mapper = Mapper.new(self) + if block.arity == 1 + mapper.instance_exec(DeprecatedMapper.new(self), &block) + else + mapper.instance_exec(&block) + end + + finalize! unless @disable_clear_and_finalize + + nil + end + + def finalize! @set.add_route(NotFound) install_helpers @set.freeze end def clear! + # Clear the controller cache so we may discover new ones + @controller_constraints = nil routes.clear named_routes.clear @set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY) @@ -231,65 +251,38 @@ module ActionDispatch routes.empty? end - def add_configuration_file(path) - self.configuration_files << path - end - - # Deprecated accessor - def configuration_file=(path) - add_configuration_file(path) - end - - # Deprecated accessor - def configuration_file - configuration_files - end - - def load! - # Clear the controller cache so we may discover new ones - Routing.clear_controller_cache! - - load_routes! - end - - # reload! will always force a reload whereas load checks the timestamp first - alias reload! load! + CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/ - def reload - if configuration_files.any? && @routes_last_modified - if routes_changed_at == @routes_last_modified - return # routes didn't change, don't reload - else - @routes_last_modified = routes_changed_at - end - end - - load! - end - - def load_routes! - if configuration_files.any? - configuration_files.each { |config| load(config) } - @routes_last_modified = routes_changed_at - else - draw do |map| - map.connect ":controller/:action/:id" - end + def controller_constraints + @controller_constraints ||= begin + source = controller_namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" } + source << CONTROLLER_REGEXP.source + Regexp.compile(source.sort.reverse.join('|')) end end - def routes_changed_at - routes_changed_at = nil + def controller_namespaces + namespaces = Set.new - configuration_files.each do |config| - config_changed_at = File.stat(config).mtime + # Find any nested controllers already in memory + ActionController::Base.subclasses.each do |klass| + controller_name = klass.underscore + namespaces << controller_name.split('/')[0...-1].join('/') + end - if routes_changed_at.nil? || config_changed_at > routes_changed_at - routes_changed_at = config_changed_at + # TODO: Move this into Railties + if defined?(Rails.application) + # Find namespaces in controllers/ directory + Rails.application.configuration.controller_paths.each do |load_path| + load_path = File.expand_path(load_path) + Dir["#{load_path}/**/*_controller.rb"].collect do |path| + namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') + end end end - routes_changed_at + namespaces.delete('') + namespaces end def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil) @@ -374,7 +367,8 @@ module ActionDispatch end recall[:action] = options.delete(:action) if options[:action] == 'index' - parameterize = lambda { |name, value| + opts = {} + opts[:parameterize] = lambda { |name, value| if name == :controller value elsif value.is_a?(Array) @@ -384,14 +378,23 @@ module ActionDispatch end } - path = @set.url(named_route, options, recall, :parameterize => parameterize) + unless result = @set.generate(:path_info, named_route, options, recall, opts) + raise ActionController::RoutingError, "No route matches #{options.inspect}" + end + + path, params = result + params.each do |k, v| + if v + params[k] = v + else + params.delete(k) + end + end + if path && method == :generate_extras - uri = URI(path) - extras = uri.query ? - Rack::Utils.parse_nested_query(uri.query).keys.map { |k| k.to_sym } : - [] - [uri.path, extras] + [path, params.keys] elsif path + path << "?#{params.to_query}" if params.any? path else raise ActionController::RoutingError, "No route matches #{options.inspect}" @@ -402,37 +405,11 @@ module ActionDispatch def call(env) @set.call(env) - rescue ActionController::RoutingError => e - raise e if env['action_controller.rescue_error'] == false - - method, path = env['REQUEST_METHOD'].downcase.to_sym, env['PATH_INFO'] - - # Route was not recognized. Try to find out why (maybe wrong verb). - allows = HTTP_METHODS.select { |verb| - begin - recognize_path(path, {:method => verb}, false) - rescue ActionController::RoutingError - nil - end - } - - if !HTTP_METHODS.include?(method) - raise ActionController::NotImplemented.new(*allows) - elsif !allows.empty? - raise ActionController::MethodNotAllowed.new(*allows) - else - raise e - end - end - - def recognize(request) - params = recognize_path(request.path, extract_request_environment(request)) - request.path_parameters = params.with_indifferent_access - "#{params[:controller].to_s.camelize}Controller".constantize end - def recognize_path(path, environment = {}, rescue_error = true) + def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase + path = Rack::Mount::Utils.normalize_path(path) begin env = Rack::MockRequest.env_for(path, {:method => method}) @@ -440,16 +417,16 @@ module ActionDispatch raise ActionController::RoutingError, e.message end - env['action_controller.recognize'] = true - env['action_controller.rescue_error'] = rescue_error - status, headers, body = call(env) - body - end + req = Rack::Request.new(env) + @set.recognize(req) do |route, params| + dispatcher = route.app + if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params) + dispatcher.prepare_params!(params) + return params + end + end - # Subclasses and plugins may override this method to extract further attributes - # from the request, for use by route conditions and such. - def extract_request_environment(request) - { :method => request.method } + raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index e6d6b5a3ef..fc477afb17 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -46,7 +46,6 @@ module ActionDispatch request_method = nil end - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? request = recognized_request_for(path, request_method) expected_options = expected_options.clone @@ -80,7 +79,6 @@ module ActionDispatch def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil) expected_path = "/#{expected_path}" unless expected_path[0] == ?/ # Load routes.rb if it hasn't been loaded. - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults) found_extras = options.reject {|k, v| ! extra_keys.include? k} @@ -126,6 +124,46 @@ module ActionDispatch assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message) end + # A helper to make it easier to test different route configurations. + # This method temporarily replaces ActionController::Routing::Routes + # with a new RouteSet instance. + # + # The new instance is yielded to the passed block. Typically the block + # will create some routes using <tt>map.draw { map.connect ... }</tt>: + # + # with_routing do |set| + # set.draw do |map| + # map.connect ':controller/:action/:id' + # assert_equal( + # ['/content/10/show', {}], + # map.generate(:controller => 'content', :id => 10, :action => 'show') + # end + # end + # end + # + def with_routing + real_routes = ActionController::Routing::Routes + ActionController::Routing.module_eval { remove_const :Routes } + + temporary_routes = ActionController::Routing::RouteSet.new + ActionController::Routing.module_eval { const_set :Routes, temporary_routes } + + yield temporary_routes + ensure + if ActionController::Routing.const_defined? :Routes + ActionController::Routing.module_eval { remove_const :Routes } + end + ActionController::Routing.const_set(:Routes, real_routes) if real_routes + end + + def method_missing(selector, *args, &block) + if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector) + @controller.send(selector, *args, &block) + else + super + end + end + private # Recognizes the route for a given path. def recognized_request_for(path, request_method = nil) @@ -134,9 +172,11 @@ module ActionDispatch # Assume given controller request = ActionController::TestRequest.new request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method - request.path = path + request.path = path + + params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method }) + request.path_parameters = params.with_indifferent_access - ActionController::Routing::Routes.recognize(request) request end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb index ef6867576e..b74dcb1fe4 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb @@ -76,10 +76,10 @@ module ActionDispatch # # Assert that there is a "span" containing between 2 and 4 "em" tags # # as immediate children # assert_tag :tag => "span", - # :children => { :count => 2..4, :only => { :tag => "em" } } + # :children => { :count => 2..4, :only => { :tag => "em" } } # # # Get funky: assert that there is a "div", with an "ul" ancestor - # # and an "li" parent (with "class" = "enum"), and containing a + # # and an "li" parent (with "class" = "enum"), and containing a # # "span" descendant that contains text matching /hello world/ # assert_tag :tag => "div", # :ancestor => { :tag => "ul" }, @@ -98,7 +98,7 @@ module ActionDispatch tag = find_tag(opts) assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}" end - + # Identical to +assert_tag+, but asserts that a matching tag does _not_ # exist. (See +assert_tag+ for a full discussion of the syntax.) # @@ -118,6 +118,19 @@ module ActionDispatch tag = find_tag(opts) assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}" end + + def find_tag(conditions) + html_document.find(conditions) + end + + def find_all_tag(conditions) + html_document.find_all(conditions) + end + + def html_document + xml = @response.content_type =~ /xml$/ + @html_document ||= HTML::Document.new(@response.body, false, xml) + end end end end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 40d6f97b2a..5c127dfe37 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -2,9 +2,7 @@ require 'stringio' require 'uri' require 'active_support/test_case' require 'active_support/core_ext/object/metaclass' - -# TODO: Remove circular dependency on ActionController -require 'action_controller/testing/process' +require 'rack/test' module ActionDispatch module Integration #:nodoc: @@ -128,9 +126,7 @@ module ActionDispatch DEFAULT_HOST = "www.example.com" include Test::Unit::Assertions - include ActionDispatch::Assertions - include ActionController::TestProcess - include RequestHelpers + include TestProcess, RequestHelpers, Assertions %w( status status_message headers body redirect? ).each do |method| delegate method, :to => :response, :allow_nil => true diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index b1ed9d31f4..1b9a6c18b7 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,15 +1,17 @@ require 'active_support/testing/performance' require 'active_support/testing/default' -module ActionDispatch - # An integration test that runs a code profiler on your test methods. - # Profiling output for combinations of each test method, measurement, and - # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. - class PerformanceTest < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Performance - include ActiveSupport::Testing::Default +if defined?(ActiveSupport::Testing::Performance) + module ActionDispatch + # An integration test that runs a code profiler on your test methods. + # Profiling output for combinations of each test method, measurement, and + # output format are written to your tmp/performance directory. + # + # By default, process_time is measured and both flat and graph_html output + # formats are written, so you'll have two output files per test method. + class PerformanceTest < ActionDispatch::IntegrationTest + include ActiveSupport::Testing::Performance + include ActiveSupport::Testing::Default + end end -end +end
\ No newline at end of file diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb new file mode 100644 index 0000000000..eae703e1b6 --- /dev/null +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -0,0 +1,42 @@ +module ActionDispatch + module TestProcess + def assigns(key = nil) + assigns = {} + @controller.instance_variable_names.each do |ivar| + next if ActionController::Base.protected_instance_variables.include?(ivar) + assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) + end + + key.nil? ? assigns : assigns[key.to_s] + end + + def session + @request.session + end + + def flash + @request.flash + end + + def cookies + @request.cookies.merge(@response.cookies) + end + + def redirect_to_url + @response.redirect_url + end + + # Shortcut for <tt>ARack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>: + # + # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') + # + # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter. + # This will not affect other platforms: + # + # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) + def fixture_file_upload(path, mime_type = nil, binary = false) + fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) + Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary) + end + end +end diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index e95e84aeb5..c3e42ac0d5 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -21,38 +21,39 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -require File.join(File.dirname(__FILE__), "action_pack") +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' +require 'active_support/core_ext/class/attribute_accessors' + +require 'action_pack' module ActionView - def self.load_all! - [Context, Base, InlineTemplate, TemplateError] + extend ActiveSupport::Autoload + + autoload :Base + autoload :Context + autoload :Template + autoload :Helpers + autoload :SafeBuffer + + + autoload_under "render" do + autoload :Partials + autoload :Rendering end - autoload :Base, 'action_view/base' - autoload :Context, 'action_view/context' - autoload :Helpers, 'action_view/helpers' autoload :MissingTemplate, 'action_view/base' - autoload :Partials, 'action_view/render/partials' autoload :Resolver, 'action_view/template/resolver' autoload :PathResolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' - autoload :Rendering, 'action_view/render/rendering' - autoload :Template, 'action_view/template/template' - autoload :TemplateError, 'action_view/template/error' - autoload :TemplateHandler, 'action_view/template/handler' - autoload :TemplateHandlers, 'action_view/template/handlers' - autoload :TextTemplate, 'action_view/template/text' - autoload :Helpers, 'action_view/helpers' autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' - autoload :SafeBuffer, 'action_view/safe_buffer' + + autoload :TemplateError, 'action_view/template/error' + autoload :TemplateHandler, 'action_view/template' + autoload :TemplateHandlers, 'action_view/template' end require 'action_view/erb/util' - I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' -require 'active_support/core_ext/class/attribute_accessors' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c33695770f..d69e5109fa 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -196,7 +196,7 @@ module ActionView #:nodoc: end class << self - delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' + delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 23bde61f9c..0059b79e5f 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -4,7 +4,7 @@ module ActionView #:nodoc: # TODO: Clean this up if obj.is_a?(String) if cache.nil? - cache = !defined?(Rails) || Rails.application.config.cache_classes + cache = !defined?(Rails.application) || Rails.application.config.cache_classes end FileSystemResolverWithFallback.new(obj, :cache => cache) else diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 2eb88ae3e5..aeaf1ee4ff 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -181,20 +181,20 @@ module ActionView def initialize(view_context, options, block) @view = view_context @partial_names = PARTIAL_NAMES[@view.controller.class] - + key = Thread.current[:format_locale_key] @templates = TEMPLATES[key] if key - + setup(options, block) end - + def setup(options, block) partial = options[:partial] - + @options = options @locals = options[:locals] || {} @block = block - + if String === partial @object = options[:object] @path = partial @@ -240,7 +240,7 @@ module ActionView segments << template.render(@view, locals) end - + @template = template segments end @@ -294,7 +294,7 @@ module ActionView path && @templates[path] ||= _find_template(path) end end - + def _find_template(path) if controller = @view.controller prefix = controller.controller_path unless path.include?(?/) @@ -319,7 +319,7 @@ module ActionView _evaluate_assigns_and_ivars details = options[:_details] - + # Is this needed self.formats = details[:formats] if details renderer = PartialRenderer.new(self, options, nil) @@ -329,12 +329,12 @@ module ActionView end def _render_partial(options, &block) #:nodoc: - if @renderer + if defined? @renderer @renderer.setup(options, block) else @renderer = PartialRenderer.new(self, options, block) end - + @renderer.render end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template.rb index d1970ca3c7..210ad508f5 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -6,7 +6,14 @@ require "action_view/template/resolver" module ActionView class Template - extend TemplateHandlers + extend ActiveSupport::Autoload + + autoload :Error + autoload :Handler + autoload :Handlers + autoload :Text + + extend Template::Handlers attr_reader :source, :identifier, :handler, :mime_type, :formats, :details def initialize(source, identifier, handler, details) @@ -32,11 +39,11 @@ module ActionView view.send(method_name, locals, &block) end rescue Exception => e - if e.is_a?(TemplateError) + if e.is_a?(Template::Error) e.sub_template_of(self) raise e else - raise TemplateError.new(self, view.assigns, e) + raise Template::Error.new(self, view.assigns, e) end end @@ -103,7 +110,7 @@ module ActionView logger.debug "Backtrace: #{e.backtrace.join("\n")}" end - raise ActionView::TemplateError.new(self, {}, e) + raise ActionView::Template::Error.new(self, {}, e) end end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index aa21606f76..648f708d3d 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,101 +1,105 @@ require "active_support/core_ext/enumerable" module ActionView - # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a - # bunch of intimate details and uses it to report a very precise exception message. - class TemplateError < ActionViewError #:nodoc: - SOURCE_CODE_RADIUS = 3 + class Template + # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a + # bunch of intimate details and uses it to report a very precise exception message. + class Error < ActionViewError #:nodoc: + SOURCE_CODE_RADIUS = 3 - attr_reader :original_exception + attr_reader :original_exception - def initialize(template, assigns, original_exception) - @template, @assigns, @original_exception = template, assigns.dup, original_exception - @backtrace = compute_backtrace - end + def initialize(template, assigns, original_exception) + @template, @assigns, @original_exception = template, assigns.dup, original_exception + @backtrace = compute_backtrace + end - def file_name - @template.identifier - end + def file_name + @template.identifier + end - def message - ActiveSupport::Deprecation.silence { original_exception.message } - end + def message + ActiveSupport::Deprecation.silence { original_exception.message } + end - def clean_backtrace - if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) - Rails.backtrace_cleaner.clean(original_exception.backtrace) - else - original_exception.backtrace + def clean_backtrace + if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) + Rails.backtrace_cleaner.clean(original_exception.backtrace) + else + original_exception.backtrace + end end - end - def sub_template_message - if @sub_templates - "Trace of template inclusion: " + - @sub_templates.collect { |template| template.inspect }.join(", ") - else - "" + def sub_template_message + if @sub_templates + "Trace of template inclusion: " + + @sub_templates.collect { |template| template.inspect }.join(", ") + else + "" + end end - end - def source_extract(indentation = 0) - return unless num = line_number - num = num.to_i + def source_extract(indentation = 0) + return unless num = line_number + num = num.to_i - source_code = @template.source.split("\n") + source_code = @template.source.split("\n") - start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max - end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min + start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max + end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min - indent = ' ' * indentation - line_counter = start_on_line - return unless source_code = source_code[start_on_line..end_on_line] + indent = ' ' * indentation + line_counter = start_on_line + return unless source_code = source_code[start_on_line..end_on_line] - source_code.sum do |line| - line_counter += 1 - "#{indent}#{line_counter}: #{line}\n" + source_code.sum do |line| + line_counter += 1 + "#{indent}#{line_counter}: #{line}\n" + end end - end - - def sub_template_of(template_path) - @sub_templates ||= [] - @sub_templates << template_path - end - - def line_number - @line_number ||= - if file_name - regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } - end - end + def sub_template_of(template_path) + @sub_templates ||= [] + @sub_templates << template_path + end - def to_s - "\n#{self.class} (#{message}) #{source_location}:\n" + - "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" - end + def line_number + @line_number ||= + if file_name + regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - # don't do anything nontrivial here. Any raised exception from here becomes fatal - # (and can't be rescued). - def backtrace - @backtrace - end + $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } + end + end - private - def compute_backtrace - [ - "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + - clean_backtrace.join("\n ") - ] + def to_s + "\n#{self.class} (#{message}) #{source_location}:\n" + + "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" end - def source_location - if line_number - "on line ##{line_number} of " - else - 'in ' - end + file_name + # don't do anything nontrivial here. Any raised exception from here becomes fatal + # (and can't be rescued). + def backtrace + @backtrace end + + private + def compute_backtrace + [ + "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + + clean_backtrace.join("\n ") + ] + end + + def source_location + if line_number + "on line ##{line_number} of " + else + 'in ' + end + file_name + end + end end -end
\ No newline at end of file + + TemplateError = Template::Error +end diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 4bf58b9fa8..5a46a27893 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -3,34 +3,39 @@ require "action_dispatch/http/mime_type" # Legacy TemplateHandler stub module ActionView - module TemplateHandlers #:nodoc: - module Compilable - def self.included(base) - base.extend(ClassMethods) - end + class Template + module Handlers #:nodoc: + module Compilable + def self.included(base) + base.extend(ClassMethods) + end - module ClassMethods - def call(template) - new.compile(template) + module ClassMethods + def call(template) + new.compile(template) + end end - end - def compile(template) - raise "Need to implement #{self.class.name}#compile(template)" - end + def compile(template) + raise "Need to implement #{self.class.name}#compile(template)" + end + end end - end - class TemplateHandler - extlib_inheritable_accessor :default_format - self.default_format = Mime::HTML + class Template::Handler + extlib_inheritable_accessor :default_format + self.default_format = Mime::HTML - def self.call(template) - raise "Need to implement #{self.class.name}#call(template)" - end + def self.call(template) + raise "Need to implement #{self.class.name}#call(template)" + end - def render(template, local_assigns) - raise "Need to implement #{self.class.name}#render(template, local_assigns)" + def render(template, local_assigns) + raise "Need to implement #{self.class.name}#render(template, local_assigns)" + end end end + + TemplateHandlers = Template::Handlers + TemplateHandler = Template::Handler end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index faf54b9fe5..35488c0391 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -1,52 +1,54 @@ module ActionView #:nodoc: - module TemplateHandlers #:nodoc: - autoload :ERB, 'action_view/template/handlers/erb' - autoload :RJS, 'action_view/template/handlers/rjs' - autoload :Builder, 'action_view/template/handlers/builder' - - def self.extended(base) - base.register_default_template_handler :erb, TemplateHandlers::ERB - base.register_template_handler :rjs, TemplateHandlers::RJS - base.register_template_handler :builder, TemplateHandlers::Builder - - # TODO: Depreciate old template extensions - base.register_template_handler :rhtml, TemplateHandlers::ERB - base.register_template_handler :rxml, TemplateHandlers::Builder - end - - @@template_handlers = {} - @@default_template_handlers = nil + class Template + module Handlers #:nodoc: + autoload :ERB, 'action_view/template/handlers/erb' + autoload :RJS, 'action_view/template/handlers/rjs' + autoload :Builder, 'action_view/template/handlers/builder' + + def self.extended(base) + base.register_default_template_handler :erb, ERB + base.register_template_handler :rjs, RJS + base.register_template_handler :builder, Builder + + # TODO: Depreciate old template extensions + base.register_template_handler :rhtml, ERB + base.register_template_handler :rxml, Builder + end + + @@template_handlers = {} + @@default_template_handlers = nil - def self.extensions - @@template_handlers.keys - end - - # Register a class that knows how to handle template files with the given - # extension. This can be used to implement new template types. - # The constructor for the class must take the ActiveView::Base instance - # as a parameter, and the class must implement a +render+ method that - # takes the contents of the template to render as well as the Hash of - # local assigns available to the template. The +render+ method ought to - # return the rendered template as a string. - def register_template_handler(extension, klass) - @@template_handlers[extension.to_sym] = klass - end - - def template_handler_extensions - @@template_handlers.keys.map {|key| key.to_s }.sort - end - - def registered_template_handler(extension) - extension && @@template_handlers[extension.to_sym] - end - - def register_default_template_handler(extension, klass) - register_template_handler(extension, klass) - @@default_template_handlers = klass - end - - def handler_class_for_extension(extension) - (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + def self.extensions + @@template_handlers.keys + end + + # Register a class that knows how to handle template files with the given + # extension. This can be used to implement new template types. + # The constructor for the class must take the ActiveView::Base instance + # as a parameter, and the class must implement a +render+ method that + # takes the contents of the template to render as well as the Hash of + # local assigns available to the template. The +render+ method ought to + # return the rendered template as a string. + def register_template_handler(extension, klass) + @@template_handlers[extension.to_sym] = klass + end + + def template_handler_extensions + @@template_handlers.keys.map {|key| key.to_s }.sort + end + + def registered_template_handler(extension) + extension && @@template_handlers[extension.to_sym] + end + + def register_default_template_handler(extension, klass) + register_template_handler(extension, klass) + @@default_template_handlers = klass + end + + def handler_class_for_extension(extension) + (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + end end end end diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 5f381f7bf0..a93cfca8aa 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class Builder < TemplateHandler + module Template::Handlers + class Builder < Template::Handler include Compilable self.default_format = Mime::XML diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 88aeb4b053..f8e6376589 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/string/output_safety' require 'erubis' module ActionView - module TemplateHandlers + module Template::Handlers class Erubis < ::Erubis::Eruby def add_preamble(src) src << "@output_buffer = ActionView::SafeBuffer.new;" @@ -26,7 +26,7 @@ module ActionView end end - class ERB < TemplateHandler + class ERB < Template::Handler include Compilable ## diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index b1d15dc209..63e7dc0902 100644 --- a/actionpack/lib/action_view/template/handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class RJS < TemplateHandler + module Template::Handlers + class RJS < Template::Handler include Compilable self.default_format = Mime::JS diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 7336114e1b..a2f4ab2ef5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,6 +1,6 @@ require "pathname" require "active_support/core_ext/class" -require "action_view/template/template" +require "action_view/template" module ActionView # Abstract superclass @@ -20,7 +20,7 @@ module ActionView register_detail(:locale) { [I18n.locale] } register_detail(:formats) { Mime::SET.symbols } register_detail(:handlers, :allow_nil => false) do - TemplateHandlers.extensions + Template::Handlers.extensions end def initialize(options = {}) @@ -65,7 +65,7 @@ module ActionView # as well as incorrectly putting part of the path in the template # name instead of the prefix. def normalize_name(name, prefix) - handlers = TemplateHandlers.extensions.join('|') + handlers = Template::Handlers.extensions.join('|') name = name.to_s.gsub(/\.(?:#{handlers})$/, '') parts = name.split('/') diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index f6e011a5ab..67e086d8bd 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,38 +1,40 @@ module ActionView #:nodoc: - class TextTemplate < String #:nodoc: - HTML = Mime[:html] - - def initialize(string, content_type = HTML) - super(string.to_s) - @content_type = Mime[content_type] || content_type - end - - def details - {:formats => [@content_type.to_sym]} - end - - def identifier - self - end - - def inspect - 'text template' - end - - def render(*args) - to_s - end - - def mime_type - @content_type - end - - def formats - [mime_type] - end - - def partial? - false + class Template + class Text < String #:nodoc: + HTML = Mime[:html] + + def initialize(string, content_type = HTML) + super(string.to_s) + @content_type = Mime[content_type] || content_type + end + + def details + {:formats => [@content_type.to_sym]} + end + + def identifier + self + end + + def inspect + 'text template' + end + + def render(*args) + to_s + end + + def mime_type + @content_type + end + + def formats + [mime_type] + end + + def partial? + false + end end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 86bbad822d..be9a2ed50d 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,5 +1,5 @@ require 'active_support/test_case' -require 'action_controller/testing/test_case' +require 'action_controller/test_case' module ActionView class Base @@ -39,8 +39,7 @@ module ActionView end end - include ActionDispatch::Assertions - include ActionController::TestProcess + include ActionDispatch::Assertions, ActionDispatch::TestProcess include ActionView::Context include ActionController::PolymorphicRoutes diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb index ae2f1bf1f2..5028c19b80 100644 --- a/actionpack/test/abstract/layouts_test.rb +++ b/actionpack/test/abstract/layouts_test.rb @@ -23,7 +23,7 @@ module AbstractControllerTests self.view_paths = [] def index - render :_template => ActionView::TextTemplate.new("Hello blank!") + render :_template => ActionView::Template::Text.new("Hello blank!") end end @@ -31,19 +31,19 @@ module AbstractControllerTests layout "hello" def index - render :_template => ActionView::TextTemplate.new("Hello string!") + render :_template => ActionView::Template::Text.new("Hello string!") end def overwrite_default - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => :default + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => :default end def overwrite_false - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => false + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => false end def overwrite_string - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => "omg" + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => "omg" end def overwrite_skip @@ -72,7 +72,7 @@ module AbstractControllerTests layout :hello def index - render :_template => ActionView::TextTemplate.new("Hello symbol!") + render :_template => ActionView::Template::Text.new("Hello symbol!") end private def hello @@ -84,7 +84,7 @@ module AbstractControllerTests layout :no_hello def index - render :_template => ActionView::TextTemplate.new("Hello missing symbol!") + render :_template => ActionView::Template::Text.new("Hello missing symbol!") end private def no_hello @@ -96,7 +96,7 @@ module AbstractControllerTests layout :nilz def index - render :_template => ActionView::TextTemplate.new("Hello nilz!") + render :_template => ActionView::Template::Text.new("Hello nilz!") end def nilz() end @@ -106,7 +106,7 @@ module AbstractControllerTests layout :objekt def index - render :_template => ActionView::TextTemplate.new("Hello nilz!") + render :_template => ActionView::Template::Text.new("Hello nilz!") end def objekt @@ -118,7 +118,7 @@ module AbstractControllerTests layout :omg_no_method def index - render :_template => ActionView::TextTemplate.new("Hello boom!") + render :_template => ActionView::Template::Text.new("Hello boom!") end end @@ -126,7 +126,7 @@ module AbstractControllerTests layout "missing" def index - render :_template => ActionView::TextTemplate.new("Hello missing!") + render :_template => ActionView::Template::Text.new("Hello missing!") end end @@ -134,7 +134,7 @@ module AbstractControllerTests layout false def index - render :_template => ActionView::TextTemplate.new("Hello false!") + render :_template => ActionView::Template::Text.new("Hello false!") end end @@ -142,7 +142,7 @@ module AbstractControllerTests layout nil def index - render :_template => ActionView::TextTemplate.new("Hello nil!") + render :_template => ActionView::Template::Text.new("Hello nil!") end end diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb index 45a4763fe4..331cb6f769 100644 --- a/actionpack/test/abstract/render_test.rb +++ b/actionpack/test/abstract/render_test.rb @@ -38,7 +38,7 @@ module AbstractController end def object - render :_template => ActionView::TextTemplate.new("With Object") + render :_template => ActionView::Template::Text.new("With Object") end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 9d055da4b9..a9341b60df 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,12 +1,9 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift "#{root}/activesupport/lib" - $:.unshift "#{root}/activemodel/lib" end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) $:.unshift(File.dirname(__FILE__) + '/lib') @@ -16,18 +13,20 @@ $:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp') require 'test/unit' -require 'active_support' -require 'active_support/test_case' require 'abstract_controller' require 'action_controller' require 'action_view' require 'action_view/base' require 'action_dispatch' -require 'active_model' require 'fixture_template' +require 'active_support/test_case' require 'action_view/test_case' require 'active_support/dependencies' +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) +require 'active_model' + begin require 'ruby-debug' Debugger.settings[:autoeval] = true @@ -83,7 +82,7 @@ class ActiveSupport::TestCase # have been loaded. setup_once do ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' + match ':controller(/:action(/:id))' end end end @@ -198,7 +197,7 @@ module ActionController Base.view_paths = FIXTURE_LOAD_PATH class TestCase - include TestProcess + include ActionDispatch::TestProcess def assert_template(options = {}, message = nil) validate_request! diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb index 9e0c66055d..9a094cf66b 100644 --- a/actionpack/test/active_record_unit.rb +++ b/actionpack/test/active_record_unit.rb @@ -11,19 +11,15 @@ class ActiveRecordTestConnector end # Try to grab AR -if defined?(ActiveRecord) && defined?(Fixtures) - $stderr.puts 'Active Record is already loaded, running tests' -else - $stderr.print 'Attempting to load Active Record... ' +unless defined?(ActiveRecord) && defined?(Fixtures) begin PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib" raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR) $LOAD_PATH.unshift PATH_TO_AR require 'active_record' require 'active_record/fixtures' - $stderr.puts 'success' rescue LoadError => e - $stderr.print "failed. Skipping Active Record assertion tests: #{e}" + $stderr.print "Failed to load Active Record. Skipping Active Record assertion tests: #{e}" ActiveRecordTestConnector.able_to_connect = false end end diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index c6c079f88c..61bee1b66c 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -155,7 +155,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| - map.connect "/:action", :controller => "active_record_store_test/test" + match ':action', :to => 'active_record_store_test/test' end @app = ActiveRecord::SessionStore.new(set, options.reverse_merge(:key => '_session_id')) yield diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 901cb940ea..d54be9bdc0 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -221,8 +221,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_named_route with_routing do |set| set.draw do |map| - map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing' - map.connect ':controller/:action/:id' + match 'route_one', :to => 'action_pack_assertions#nothing', :as => :route_one + match ':controller/:action' end set.install_helpers @@ -235,9 +235,9 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_named_route_failure with_routing do |set| set.draw do |map| - map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'one' - map.route_two 'route_two', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two' - map.connect ':controller/:action/:id' + match 'route_one', :to => 'action_pack_assertions#nothing', :as => :route_one + match 'route_two', :to => 'action_pack_assertions#nothing', :id => 'two', :as => :route_two + match ':controller/:action' end process :redirect_to_named_route assert_raise(ActiveSupport::TestCase::Assertion) do @@ -255,8 +255,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_nested_named_route with_routing do |set| set.draw do |map| - map.admin_inner_module 'admin/inner_module', :controller => 'admin/inner_module', :action => 'index' - map.connect ':controller/:action/:id' + match 'admin/inner_module', :to => 'admin/inner_module#index', :as => :admin_inner_module + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_index @@ -268,8 +268,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirected_to_top_level_named_route_from_nested_controller with_routing do |set| set.draw do |map| - map.top_level '/action_pack_assertions/:id', :controller => 'action_pack_assertions', :action => 'index' - map.connect ':controller/:action/:id' + match '/action_pack_assertions/:id', :to => 'action_pack_assertions#index', :as => :top_level + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_top_level_named_route @@ -282,8 +282,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase with_routing do |set| set.draw do |map| # this controller exists in the admin namespace as well which is the only difference from previous test - map.top_level '/user/:id', :controller => 'user', :action => 'index' - map.connect ':controller/:action/:id' + match '/user/:id', :to => 'user#index', :as => :top_level + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_top_level_named_route diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index b57550a69a..8f8ada8d8c 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -179,8 +179,8 @@ class DefaultUrlOptionsTest < ActionController::TestCase def test_default_url_options_are_used_if_set with_routing do |set| set.draw do |map| - map.default_url_options 'default_url_options', :controller => 'default_url_options' - map.connect ':controller/:action/:id' + match 'default_url_options', :to => 'default_url_options#default_url_options_action', :as => :default_url_options + match ':controller/:action' end get :default_url_options_action # Make a dummy request so that the controller is initialized properly. @@ -210,7 +210,7 @@ class EnsureNamedRoutesWorksTicket22BugTest < ActionController::TestCase def test_named_routes_still_work with_routing do |set| set.draw do |map| - map.resources :things + resources :things end EmptyController.send :include, ActionController::UrlWriter diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 3ce90b6ccf..4ea2e57741 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -70,8 +70,8 @@ class PageCachingTest < ActionController::TestCase def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route with_routing do |set| set.draw do |map| - map.main '', :controller => 'posts', :format => nil - map.formatted_posts 'posts.:format', :controller => 'posts' + match 'posts.:format', :to => 'posts#index', :as => :formatted_posts + match '/', :to => 'posts#index', :as => :main end @params[:format] = 'rss' assert_equal '/posts.rss', @rewriter.rewrite(@params) @@ -422,8 +422,7 @@ class ActionCacheTest < ActionController::TestCase def test_xml_version_of_resource_is_treated_as_different_cache with_routing do |set| set.draw do |map| - map.connect ':controller/:action.:format' - map.connect ':controller/:action' + match ':controller(/:action(.:format))' end get :index, :format => 'xml' @@ -632,13 +631,16 @@ class FragmentCachingTest < ActionController::TestCase def test_fragment_for_logging fragment_computed = false - ActiveSupport::Notifications.queue.expects(:publish).times(2) + events = [] + ActiveSupport::Notifications.subscribe { |*args| events << args } buffer = 'generated till now -> ' @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } assert fragment_computed assert_equal 'generated till now -> ', buffer + ActiveSupport::Notifications.notifier.wait + assert_equal [:fragment_exist?, :write_fragment], events.map(&:first) end end diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 622d67287d..64f1ad7610 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -15,7 +15,6 @@ class DispatcherTest < Test::Unit::TestCase ActionDispatch::Callbacks.reset_callbacks(:call) ActionController::Routing::Routes.stubs(:call).returns([200, {}, 'response']) - ActionController::Routing::Routes.stubs(:reload) Dispatcher.stubs(:require_dependency) end @@ -28,18 +27,6 @@ class DispatcherTest < Test::Unit::TestCase dispatch(false) end - def test_reloads_routes_before_dispatch_if_in_loading_mode - ActionController::Routing::Routes.expects(:reload).once - dispatch(false) - end - - def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode - ActionController::Routing::Routes.expects(:reload).never - ActiveSupport::Dependencies.expects(:clear).never - - dispatch - end - def test_prepare_callbacks a = b = c = nil ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 } diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index b9be163904..9030e562bb 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -191,7 +191,7 @@ class IsolatedHelpersTest < Test::Unit::TestCase end def test_helper_in_a - assert_raise(ActionView::TemplateError) { call_controller(A, "index") } + assert_raise(ActionView::Template::Error) { call_controller(A, "index") } end def test_helper_in_b diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index feb2f81cc1..f635253156 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -115,7 +115,7 @@ class RendersNoLayoutController < LayoutTest end class LayoutSetInResponseTest < ActionController::TestCase - include ActionView::TemplateHandlers + include ActionView::Template::Handlers def test_layout_set_when_using_default_layout @controller = DefaultLayoutController.new diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index fee9cf46f9..6b9cace9cd 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -507,6 +507,13 @@ class RespondWithController < ActionController::Base end end + def using_responder_with_respond + responder = Class.new(ActionController::Responder) do + def respond; @controller.render :text => "respond #{format}"; end + end + respond_with(Customer.new("david", 13), :responder => responder) + end + protected def _render_js(js, options) @@ -592,14 +599,18 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_post_with_html + def test_using_resource_for_post_with_html_redirects_on_success with_test_route_set do post :using_resource assert_equal "text/html", @response.content_type assert_equal 302, @response.status assert_equal "http://www.example.com/customers/13", @response.location assert @response.redirect? + end + end + def test_using_resource_for_post_with_html_rerender_on_failure + with_test_route_set do errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) post :using_resource @@ -610,16 +621,20 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_post_with_xml + def test_using_resource_for_post_with_xml_yields_created_on_success with_test_route_set do @request.accept = "application/xml" - post :using_resource assert_equal "application/xml", @response.content_type assert_equal 201, @response.status assert_equal "<name>david</name>", @response.body assert_equal "http://www.example.com/customers/13", @response.location + end + end + def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure + with_test_route_set do + @request.accept = "application/xml" errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) post :using_resource @@ -630,14 +645,18 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_put_with_html + def test_using_resource_for_put_with_html_redirects_on_success with_test_route_set do put :using_resource assert_equal "text/html", @response.content_type assert_equal 302, @response.status assert_equal "http://www.example.com/customers/13", @response.location assert @response.redirect? + end + end + def test_using_resource_for_put_with_html_rerender_on_failure + with_test_route_set do errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) put :using_resource @@ -648,14 +667,16 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_put_with_xml + def test_using_resource_for_put_with_xml_yields_ok_on_success @request.accept = "application/xml" - put :using_resource assert_equal "application/xml", @response.content_type assert_equal 200, @response.status assert_equal " ", @response.body + end + def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure + @request.accept = "application/xml" errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) put :using_resource @@ -665,7 +686,7 @@ class RespondWithControllerTest < ActionController::TestCase assert_nil @response.location end - def test_using_resource_for_delete_with_html + def test_using_resource_for_delete_with_html_redirects_on_success with_test_route_set do Customer.any_instance.stubs(:destroyed?).returns(true) delete :using_resource @@ -675,7 +696,7 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_delete_with_xml + def test_using_resource_for_delete_with_xml_yields_ok_on_success Customer.any_instance.stubs(:destroyed?).returns(true) @request.accept = "application/xml" delete :using_resource @@ -684,6 +705,18 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal " ", @response.body end + def test_using_resource_for_delete_with_html_redirects_on_failure + with_test_route_set do + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + Customer.any_instance.stubs(:destroyed?).returns(false) + delete :using_resource + assert_equal "text/html", @response.content_type + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers/13", @response.location + end + end + def test_using_resource_with_parent_for_get @request.accept = "application/xml" get :using_resource_with_parent @@ -735,6 +768,16 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "foo - #{[:html].to_s}", @controller.response_body end + def test_respond_as_responder_entry_point + @request.accept = "text/html" + get :using_responder_with_respond + assert_equal "respond html", @response.body + + @request.accept = "application/xml" + get :using_responder_with_respond + assert_equal "respond xml", @response.body + end + def test_clear_respond_to @controller = InheritedRespondWithController.new @request.accept = "text/html" @@ -810,9 +853,11 @@ class RespondWithControllerTest < ActionController::TestCase def with_test_route_set with_routing do |set| set.draw do |map| - map.resources :customers - map.resources :quiz_stores, :has_many => :customers - map.connect ":controller/:action/:id" + resources :customers + resources :quiz_stores do + resources :customers + end + match ":controller/:action" end yield end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index ea278fd8f0..570ff4a41b 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -233,8 +233,8 @@ class RedirectTest < ActionController::TestCase def test_redirect_to_record with_routing do |set| set.draw do |map| - map.resources :workshops - map.connect ':controller/:action/:id' + resources :workshops + match ':controller/:action' end get :redirect_to_existing_record diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index 51c3c55545..dfc4f2db8c 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -2,6 +2,11 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' +ActionController.add_renderer :simon do |says, options| + self.content_type = Mime::TEXT + self.response_body = "Simon says: #{says}" +end + class RenderOtherTest < ActionController::TestCase class TestController < ActionController::Base protect_from_forgery @@ -109,6 +114,10 @@ class RenderOtherTest < ActionController::TestCase end end + def render_simon_says + render :simon => "foo" + end + private def default_render if @alternate_default_render @@ -240,4 +249,9 @@ class RenderOtherTest < ActionController::TestCase xhr :get, :render_alternate_default assert_equal %(Element.replace("foo", "partial html");), @response.body end + + def test_using_custom_render_option + get :render_simon_says + assert_equal "Simon says: foo", @response.body + end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index b32325fa20..f26b15d2e0 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -39,35 +39,35 @@ class TestController < ActionController::Base render :action => 'hello_world' end end - + def conditional_hello_with_public_header if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true) render :action => 'hello_world' end end - + def conditional_hello_with_public_header_and_expires_at expires_in 1.minute if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true) render :action => 'hello_world' end end - + def conditional_hello_with_expires_in - expires_in 1.minute + expires_in 60.1.seconds render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public expires_in 1.minute, :public => true render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public_with_more_keys expires_in 1.minute, :public => true, 'max-stale' => 5.hours render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax expires_in 1.minute, :public => true, :private => nil, 'max-stale' => 5.hours render :action => 'hello_world' @@ -272,7 +272,7 @@ class TestController < ActionController::Base def builder_layout_test render :action => "hello", :layout => "layouts/builder" end - + # :move: test this in ActionView def builder_partial_test render :action => "hello_world_container" @@ -1093,8 +1093,8 @@ class RenderTest < ActionController::TestCase def test_head_with_location_object with_routing do |set| set.draw do |map| - map.resources :customers - map.connect ':controller/:action/:id' + resources :customers + match ':controller/:action' end get :head_with_location_object @@ -1306,22 +1306,22 @@ class ExpiresInRenderTest < ActionController::TestCase def setup @request.host = "www.nextangle.com" end - + def test_expires_in_header get :conditional_hello_with_expires_in assert_equal "max-age=60, private", @response.headers["Cache-Control"] end - + def test_expires_in_header_with_public get :conditional_hello_with_expires_in_with_public assert_equal "max-age=60, public", @response.headers["Cache-Control"] end - + def test_expires_in_header_with_additional_headers get :conditional_hello_with_expires_in_with_public_with_more_keys assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"] end - + def test_expires_in_old_syntax get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"] @@ -1425,12 +1425,12 @@ class EtagRenderTest < ActionController::TestCase get :conditional_hello_with_bangs assert_response :not_modified end - + def test_etag_with_public_true_should_set_header get :conditional_hello_with_public_header assert_equal "public", @response.headers['Cache-Control'] end - + def test_etag_with_public_true_should_set_header_and_retain_other_headers get :conditional_hello_with_public_header_and_expires_at assert_equal "max-age=60, public", @response.headers['Cache-Control'] diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index 68a52c3e8c..b5b0d0b9d5 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -61,8 +61,8 @@ class RenderXmlTest < ActionController::TestCase def test_rendering_with_object_location_should_set_header_with_url_for with_routing do |set| set.draw do |map| - map.resources :customers - map.connect ':controller/:action/:id' + resources :customers + match ':controller/:action' end get :render_with_object_location diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 04e9acf855..1a03396ae9 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -403,7 +403,7 @@ class ResourcesTest < ActionController::TestCase with_restful_routing :messages do assert_restful_routes_for :messages do |options| assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get) - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post) end end @@ -689,11 +689,11 @@ class ResourcesTest < ActionController::TestCase options = { :controller => controller_name.to_s } collection_path = "/#{controller_name}" - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put) end - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete) end end @@ -1378,7 +1378,7 @@ class ResourcesTest < ActionController::TestCase end def assert_not_recognizes(expected_options, path) - assert_raise ActionController::RoutingError, ActionController::MethodNotAllowed, Assertion do + assert_raise ActionController::RoutingError, Assertion do assert_recognizes(expected_options, path) end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index b5effeda40..c15eaade58 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -554,10 +554,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def setup_request_method_routes_for(method) - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = method - @request.request_uri = "/match" - rs.draw do |r| r.connect '/match', :controller => 'books', :action => 'get', :conditions => { :method => :get } r.connect '/match', :controller => 'books', :action => 'post', :conditions => { :method => :post } @@ -569,8 +565,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase %w(GET POST PUT DELETE).each do |request_method| define_method("test_request_method_recognized_with_#{request_method}") do setup_request_method_routes_for(request_method) - assert_nothing_raised { rs.recognize(@request) } - assert_equal request_method.downcase, @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => request_method) + assert_equal request_method.downcase, params[:action] end end @@ -580,18 +576,11 @@ class LegacyRouteSetTests < Test::Unit::TestCase r.connect '/match', :controller => 'books', :action => 'not_get_or_post' end - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = 'POST' - @request.request_uri = "/match" - assert_nothing_raised { rs.recognize(@request) } - assert_equal 'get_or_post', @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => :post) + assert_equal 'get_or_post', params[:action] - # have to recreate or else the RouteSet uses a cached version: - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = 'PUT' - @request.request_uri = "/match" - assert_nothing_raised { rs.recognize(@request) } - assert_equal 'not_get_or_post', @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => :put) + assert_equal 'not_get_or_post', params[:action] end def test_subpath_recognized @@ -745,9 +734,7 @@ class RouteSetTest < ActiveSupport::TestCase set.draw do |map| map.connect '/users/index', :controller => :users, :action => :index end - @request = ActionController::TestRequest.new - @request.request_uri = '/users/index' - assert_nothing_raised { set.recognize(@request) } + params = set.recognize_path('/users/index', :method => :get) assert_equal 1, set.routes.size end @@ -980,55 +967,34 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.request_uri = "/people" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("index", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :get) + assert_equal("index", params[:action]) - request.env["REQUEST_METHOD"] = "POST" - assert_nothing_raised { set.recognize(request) } - assert_equal("create", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :post) + assert_equal("create", params[:action]) - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :put) + assert_equal("update", params[:action]) - assert_raise(ActionController::UnknownHttpMethod) { - request.env["REQUEST_METHOD"] = "BACON" - set.recognize(request) + assert_raise(ActionController::RoutingError) { + set.recognize_path("/people", :method => :bacon) } - request.recycle! - request.request_uri = "/people/5" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + params = set.recognize_path("/people/5", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + params = set.recognize_path("/people/5", :method => :put) + assert_equal("update", params[:action]) + assert_equal("5", params[:id]) - request.env["REQUEST_METHOD"] = "DELETE" - assert_nothing_raised { set.recognize(request) } - assert_equal("destroy", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + params = set.recognize_path("/people/5", :method => :delete) + assert_equal("destroy", params[:action]) + assert_equal("5", params[:id]) - begin - request.env["REQUEST_METHOD"] = "POST" - set.recognize(request) - flunk 'Should have raised MethodNotAllowed' - rescue ActionController::MethodNotAllowed => e - assert_equal [:get, :put, :delete], e.allowed_methods - end - request.recycle! + assert_raise(ActionController::RoutingError) { + set.recognize_path("/people/5", :method => :post) + } end def test_recognize_with_alias_in_conditions @@ -1038,17 +1004,13 @@ class RouteSetTest < ActiveSupport::TestCase map.root :people end - request.path = "/people" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/people", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) - request.path = "/" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) end def test_typo_recognition @@ -1058,14 +1020,12 @@ class RouteSetTest < ActiveSupport::TestCase :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/ end - request.path = "/articles/2005/11/05/a-very-interesting-article" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("permalink", request.path_parameters[:action]) - assert_equal("2005", request.path_parameters[:year]) - assert_equal("11", request.path_parameters[:month]) - assert_equal("05", request.path_parameters[:day]) - assert_equal("a-very-interesting-article", request.path_parameters[:title]) + params = set.recognize_path("/articles/2005/11/05/a-very-interesting-article", :method => :get) + assert_equal("permalink", params[:action]) + assert_equal("2005", params[:year]) + assert_equal("11", params[:month]) + assert_equal("05", params[:day]) + assert_equal("a-very-interesting-article", params[:title]) end def test_routing_traversal_does_not_load_extra_classes @@ -1074,9 +1034,7 @@ class RouteSetTest < ActiveSupport::TestCase map.connect '/profile', :controller => 'profile' end - request.path = '/profile' - - set.recognize(request) rescue nil + params = set.recognize_path("/profile") rescue nil assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded" end @@ -1090,24 +1048,17 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.request_uri = "/people/5" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + params = set.recognize_path("/people/5", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people/5", :method => :put) + assert_equal("update", params[:action]) - request.request_uri = "/people/5.png" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - assert_equal("png", request.path_parameters[:_format]) + params = set.recognize_path("/people/5.png", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) + assert_equal("png", params[:_format]) end def test_generate_with_default_action @@ -1123,11 +1074,9 @@ class RouteSetTest < ActiveSupport::TestCase def test_root_map set.draw { |map| map.root :controller => "people" } - request.path = "" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) end def test_namespace @@ -1139,11 +1088,9 @@ class RouteSetTest < ActiveSupport::TestCase end - request.path = "/api/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/api/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_namespaced_root_map @@ -1155,11 +1102,9 @@ class RouteSetTest < ActiveSupport::TestCase end - request.path = "/api" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/api", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("index", params[:action]) end def test_namespace_with_path_prefix @@ -1169,11 +1114,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/prefix/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/prefix/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_namespace_with_blank_path_prefix @@ -1183,11 +1126,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_generate_changes_controller_module @@ -1316,11 +1257,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/projects/1/milestones" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("milestones", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/projects/1/milestones", :method => :get) + assert_equal("milestones", params[:controller]) + assert_equal("index", params[:action]) end def test_setting_root_in_namespace_using_symbol @@ -1437,15 +1376,13 @@ class RouteSetTest < ActiveSupport::TestCase )/x} end - pending do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'}) - assert_equal "/page/david", url - assert_raise ActionController::RoutingError do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'}) - end - assert_raise ActionController::RoutingError do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) - end + url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'}) + assert_equal "/page/david", url + assert_raise ActionController::RoutingError do + url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'}) + end + assert_raise ActionController::RoutingError do + url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) end end @@ -1461,10 +1398,8 @@ class RouteSetTest < ActiveSupport::TestCase )/xi} end - pending do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) - assert_equal "/page/JAMIS", url - end + url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) + assert_equal "/page/JAMIS", url end def test_route_requirement_recognize_with_xi_modifiers @@ -1621,7 +1556,7 @@ class RouteSetTest < ActiveSupport::TestCase end def test_expand_array_build_query_string - assert_uri_equal '/foo?x%5B%5D=1&x%5B%5D=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]}) + assert_uri_equal '/foo?x[]=1&x[]=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]}) end def test_escape_spaces_build_query_string_selected_keys @@ -1639,9 +1574,7 @@ class RouteSetTest < ActiveSupport::TestCase map.connect ':controller/:action/:id' end - pending do - assert_equal '/ibocorp', set.generate({:controller => 'ibocorp', :page => 1}) - end + assert_equal '/ibocorp', set.generate({:controller => 'ibocorp', :page => 1}) end def test_generate_with_optional_params_recalls_last_request @@ -1674,9 +1607,7 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal("/blog/2006/07/25", set.generate({:day => 25}, last_request)) assert_equal("/blog/2005", set.generate({:year => 2005}, last_request)) assert_equal("/blog/show/123", set.generate({:action => "show" , :id => 123}, last_request)) - pending do - assert_equal("/blog/2006/07/28", set.generate({:year => 2006}, last_request)) - end + assert_equal("/blog/2006", set.generate({:year => 2006}, last_request)) assert_equal("/blog/2006", set.generate({:year => 2006, :month => nil}, last_request)) end @@ -1692,78 +1623,6 @@ class RouteSetTest < ActiveSupport::TestCase end end -class RouteLoadingTest < Test::Unit::TestCase - def setup - routes.instance_variable_set '@routes_last_modified', nil - Object.remove_const(:RAILS_ROOT) if defined?(::RAILS_ROOT) - Object.const_set :RAILS_ROOT, '.' - routes.add_configuration_file(File.join(RAILS_ROOT, 'config', 'routes.rb')) - - @stat = stub_everything - end - - def teardown - ActionController::Routing::Routes.configuration_files.clear - Object.send :remove_const, :RAILS_ROOT - end - - def test_load - File.expects(:stat).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)) - - routes.reload - end - - def test_no_reload_when_not_modified - @stat.expects(:mtime).times(2).returns(1) - File.expects(:stat).times(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).at_most_once - - 2.times { routes.reload } - end - - def test_reload_when_modified - @stat.expects(:mtime).at_least(2).returns(1, 2) - File.expects(:stat).at_least(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2) - - 2.times { routes.reload } - end - - def test_bang_forces_reload - @stat.expects(:mtime).at_least(2).returns(1) - File.expects(:stat).at_least(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2) - - 2.times { routes.reload! } - end - - def test_load_with_configuration - routes.configuration_files.clear - routes.add_configuration_file("foobarbaz") - File.expects(:stat).returns(@stat) - routes.expects(:load).with("foobarbaz") - - routes.reload - end - - def test_load_multiple_configurations - routes.add_configuration_file("engines.rb") - - File.expects(:stat).at_least_once.returns(@stat) - - routes.expects(:load).with('./config/routes.rb') - routes.expects(:load).with('engines.rb') - - routes.reload - end - - private - def routes - ActionController::Routing::Routes - end -end - class RackMountIntegrationTests < ActiveSupport::TestCase Model = Struct.new(:to_param) @@ -1853,11 +1712,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009'}, @routes.recognize_path('/blog/2009', :method => :get)) assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009', :month => '01'}, @routes.recognize_path('/blog/2009/01', :method => :get)) assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009', :month => '01', :day => '01'}, @routes.recognize_path('/blog/2009/01/01', :method => :get)) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/blog/123456789', :method => :get) } assert_equal({:controller => 'archive', :action => 'index', :year => '2010'}, @routes.recognize_path('/archive/2010')) assert_equal({:controller => 'archive', :action => 'index'}, @routes.recognize_path('/archive')) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/archive/january') } assert_equal({:controller => 'people', :action => 'index'}, @routes.recognize_path('/people', :method => :get)) assert_equal({:controller => 'people', :action => 'index', :format => 'xml'}, @routes.recognize_path('/people.xml', :method => :get)) @@ -2016,9 +1873,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal '/posts', @routes.generate({:controller => 'posts'}, {:controller => 'posts', :action => 'index'}) assert_equal '/posts/create', @routes.generate({:action => 'create'}, {:controller => 'posts'}) assert_equal '/posts?foo=bar', @routes.generate(:controller => 'posts', :foo => 'bar') - assert_equal '/posts?foo%5B%5D=bar&foo%5B%5D=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz']) + assert_equal '/posts?foo[]=bar&foo[]=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz']) assert_equal '/posts?page=2', @routes.generate(:controller => 'posts', :page => 2) - assert_equal '/posts?q%5Bfoo%5D%5Ba%5D=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}}) + assert_equal '/posts?q[foo][a]=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}}) assert_equal '/', @routes.generate(:controller => 'news', :action => 'index') assert_equal '/', @routes.generate(:controller => 'news', :action => 'index', :format => nil) diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 375878b755..0f074b32e6 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -456,8 +456,8 @@ XML def test_array_path_parameter_handled_properly with_routing do |set| set.draw do |map| - map.connect 'file/*path', :controller => 'test_test/test', :action => 'test_params' - map.connect ':controller/:action/:id' + match 'file/*path', :to => 'test_test/test#test_params' + match ':controller/:action' end get :test_params, :path => ['hello', 'world'] @@ -563,7 +563,7 @@ XML expected = File.read(path) expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding) - file = ActionController::TestUploadedFile.new(path, content_type) + file = Rack::Test::UploadedFile.new(path, content_type) assert_equal filename, file.original_filename assert_equal content_type, file.content_type assert_equal file.path, file.local_path @@ -580,10 +580,10 @@ XML path = "#{FILES_DIR}/#{filename}" content_type = 'image/png' - binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary) + binary_uploaded_file = Rack::Test::UploadedFile.new(path, content_type, :binary) assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read - plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type) + plain_uploaded_file = Rack::Test::UploadedFile.new(path, content_type) assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read end @@ -605,7 +605,7 @@ XML end def test_test_uploaded_file_exception_when_file_doesnt_exist - assert_raise(RuntimeError) { ActionController::TestUploadedFile.new('non_existent_file') } + assert_raise(RuntimeError) { Rack::Test::UploadedFile.new('non_existent_file') } end def test_redirect_url_only_cares_about_location_header @@ -628,17 +628,6 @@ XML assert_nothing_raised(NoMethodError) { @response.binary_content } end end - - protected - def with_foo_routing - with_routing do |set| - set.draw do |map| - map.generate_url 'foo', :controller => 'test' - map.connect ':controller/:action/:id' - end - yield set - end - end end class InferringClassNameTest < ActionController::TestCase @@ -673,7 +662,7 @@ class NamedRoutesControllerTest < ActionController::TestCase def test_should_be_able_to_use_named_routes_before_a_request_is_done with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } assert_equal 'http://test.host/contents/new', new_content_url assert_equal 'http://test.host/contents/1', content_url(:id => 1) end diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 3b14cbb2d8..428f40b9f8 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -247,7 +247,7 @@ class UrlWriterTests < ActionController::TestCase with_routing do |set| set.draw do |map| - map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index' + match '/home/sweet/home/:user', :to => 'home#index', :as => :home end kls = Class.new { include ActionController::UrlWriter } @@ -264,7 +264,7 @@ class UrlWriterTests < ActionController::TestCase with_routing do |set| set.draw do |map| match 'home/sweet/home/:user', :to => 'home#index', :as => :home - map.connect ':controller/:action/:id' + match ':controller/:action/:id' end # We need to create a new class in order to install the new named route. @@ -331,8 +331,8 @@ class UrlWriterTests < ActionController::TestCase def test_named_routes_with_nil_keys with_routing do |set| set.draw do |map| - map.main '', :controller => 'posts', :format => nil - map.resources :posts + match 'posts.:format', :to => 'posts#index', :as => :posts + match '/', :to => 'posts#index', :as => :main end # We need to create a new class in order to install the new named route. @@ -350,7 +350,7 @@ class UrlWriterTests < ActionController::TestCase def test_formatted_url_methods_are_deprecated with_routing do |set| set.draw do |map| - map.resources :posts + resources :posts end # We need to create a new class in order to install the new named route. kls = Class.new { include ActionController::UrlWriter } diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 0514c098bf..5882a8cfa3 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -255,9 +255,7 @@ class WebServiceTest < ActionController::IntegrationTest def with_test_route_set with_routing do |set| set.draw do |map| - map.with_options :controller => "web_service_test/test" do |c| - c.connect "/", :action => "assign_parameters" - end + match '/', :to => 'web_service_test/test#assign_parameters' end yield end diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 301080842e..40c5ac2d09 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -151,7 +151,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest def with_test_routing with_routing do |set| set.draw do |map| - map.connect ':action', :controller => "multipart_params_parsing_test/test" + match ':action', :to => 'multipart_params_parsing_test/test' end yield end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 496445fc34..425796b460 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -14,7 +14,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest stub_controllers do |routes| Routes = routes - Routes.draw do |map| + Routes.draw do controller :sessions do get 'login', :to => :new, :as => :login post 'login', :to => :create @@ -58,8 +58,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end resources :people do - namespace ":access_token" do - resource :avatar + nested do + namespace ":access_token" do + resource :avatar + end end member do @@ -93,9 +95,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope 'articles' do - scope ':title', :title => /[a-z]+/, :as => :with_title do - match ':id', :to => :with_id + scope '/articles', :name_prefix => 'article' do + scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do + match '/:id', :to => :with_id end end end @@ -194,14 +196,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'projects#index', @response.body assert_equal '/projects', projects_path + get '/projects.xml' + assert_equal 'projects#index', @response.body + assert_equal '/projects.xml', projects_path(:format => 'xml') + get '/projects/new' assert_equal 'projects#new', @response.body assert_equal '/projects/new', new_project_path + get '/projects/new.xml' + assert_equal 'projects#new', @response.body + assert_equal '/projects/new.xml', new_project_path(:format => 'xml') + get '/projects/1' assert_equal 'projects#show', @response.body assert_equal '/projects/1', project_path(:id => '1') + get '/projects/1.xml' + assert_equal 'projects#show', @response.body + assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml') + get '/projects/1/edit' assert_equal 'projects#edit', @response.body assert_equal '/projects/1/edit', edit_project_path(:id => '1') @@ -214,9 +228,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'involvements#index', @response.body assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1') + get '/projects/1/involvements/new' + assert_equal 'involvements#new', @response.body + assert_equal '/projects/1/involvements/new', new_project_involvement_path(:project_id => '1') + get '/projects/1/involvements/1' assert_equal 'involvements#show', @response.body assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1') + + put '/projects/1/involvements/1' + assert_equal 'involvements#update', @response.body + + delete '/projects/1/involvements/1' + assert_equal 'involvements#destroy', @response.body + + get '/projects/1/involvements/1/edit' + assert_equal 'involvements#edit', @response.body + assert_equal '/projects/1/involvements/1/edit', edit_project_involvement_path(:project_id => '1', :id => '1') end end @@ -236,10 +264,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest put '/projects/1/participants/update_all' assert_equal 'participants#update_all', @response.body - - pending do - assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1') - end + assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1') end end @@ -251,15 +276,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/companies/1/people' assert_equal 'people#index', @response.body - pending do - assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1') - end + assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1') get '/projects/1/companies/1/avatar' assert_equal 'avatars#show', @response.body - pending do - assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1') - end + assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1') end end @@ -271,9 +292,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest post '/projects/1/images/1/revise' assert_equal 'images#revise', @response.body - pending do - assert_equal '/projects/1/images/1/revise', revise_project_image_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/images/1/revise', revise_project_image_path(:project_id => '1', :id => '1') end end @@ -289,27 +308,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/people/1/7a2dec8/avatar' assert_equal 'avatars#show', @response.body - pending do - assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8') - end + assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8') put '/projects/1/people/1/accessible_projects' assert_equal 'people#accessible_projects', @response.body - pending do - assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1') post '/projects/1/people/1/resend' assert_equal 'people#resend', @response.body - pending do - assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1') post '/projects/1/people/1/generate_new_password' assert_equal 'people#generate_new_password', @response.body - pending do - assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1') end end @@ -321,39 +332,27 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/posts/archive' assert_equal 'posts#archive', @response.body - pending do - assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1') - end + assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1') get '/projects/1/posts/toggle_view' assert_equal 'posts#toggle_view', @response.body - pending do - assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1') - end + assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1') post '/projects/1/posts/1/preview' assert_equal 'posts#preview', @response.body - pending do - assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1') get '/projects/1/posts/1/subscription' assert_equal 'subscriptions#show', @response.body - pending do - assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1') get '/projects/1/posts/1/comments' assert_equal 'comments#index', @response.body - pending do - assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1') post '/projects/1/posts/1/comments/preview' assert_equal 'comments#preview', @response.body - pending do - assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1') end end @@ -411,7 +410,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_raise(ActionController::RoutingError) { get '/articles/123/1' } - assert_equal '/articles/rails/1', with_title_path(:title => 'rails', :id => 1) + assert_equal '/articles/rails/1', article_with_title_path(:title => 'rails', :id => 1) end end diff --git a/actionpack/test/lib/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb index 250327e6dc..09692f77b5 100644 --- a/actionpack/test/lib/controller/fake_controllers.rb +++ b/actionpack/test/lib/controller/fake_controllers.rb @@ -1,37 +1,48 @@ class << Object; alias_method :const_available?, :const_defined?; end class ContentController < ActionController::Base; end -class NotAController; end module Admin class << self; alias_method :const_available?, :const_defined?; end + class AccountsController < ActionController::Base; end class NewsFeedController < ActionController::Base; end class PostsController < ActionController::Base; end class StuffController < ActionController::Base; end class UserController < ActionController::Base; end + class UsersController < ActionController::Base; end end module Api + class UsersController < ActionController::Base; end class ProductsController < ActionController::Base; end end # TODO: Reduce the number of test controllers we use +class AccountController < ActionController::Base; end class AddressesController < ActionController::Base; end +class ArchiveController < ActionController::Base; end class ArticlesController < ActionController::Base; end class BarController < ActionController::Base; end +class BlogController < ActionController::Base; end class BooksController < ActionController::Base; end class BraveController < ActionController::Base; end +class CarsController < ActionController::Base; end +class CcController < ActionController::Base; end class CController < ActionController::Base; end class ElsewhereController < ActionController::Base; end class FooController < ActionController::Base; end +class GeocodeController < ActionController::Base; end class HiController < ActionController::Base; end class ImageController < ActionController::Base; end +class NewsController < ActionController::Base; end class NotesController < ActionController::Base; end class PeopleController < ActionController::Base; end class PostsController < ActionController::Base; end class SessionsController < ActionController::Base; end class StuffController < ActionController::Base; end class SubpathBooksController < ActionController::Base; end +class SymbolsController < ActionController::Base; end +class UserController < ActionController::Base; end class WeblogController < ActionController::Base; end # For speed test diff --git a/actionpack/test/template/ajax_test.rb b/actionpack/test/template/ajax_test.rb index 670ba92697..aeb7c09b09 100644 --- a/actionpack/test/template/ajax_test.rb +++ b/actionpack/test/template/ajax_test.rb @@ -6,7 +6,7 @@ class AjaxTestCase < ActiveSupport::TestCase def assert_html(html, matches) matches.each do |match| - assert_match Regexp.new(Regexp.escape(match)), html + assert_match(Regexp.new(Regexp.escape(match)), html) end end @@ -52,18 +52,18 @@ class LinkToRemoteTest < AjaxTestCase test "with a hash for :update" do link = link(:update => {:success => "#posts", :failure => "#error"}) - assert_match /data-update-success="#posts"/, link - assert_match /data-update-failure="#error"/, link + assert_match(/data-update-success="#posts"/, link) + assert_match(/data-update-failure="#error"/, link) end test "with positional parameters" do link = link(:position => :top, :update => "#posts") - assert_match /data\-update\-position="top"/, link + assert_match(/data\-update\-position="top"/, link) end test "with an optional method" do link = link(:method => "delete") - assert_match /data-method="delete"/, link + assert_match(/data-method="delete"/, link) end class LegacyLinkToRemoteTest < AjaxTestCase @@ -99,7 +99,7 @@ class ButtonToRemoteTest < AjaxTestCase button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) [/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/, /data-url="\/whatnot"/].each do |match| - assert_match match, button + assert_match(match, button) end end end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 6a5fb0acff..347cb98303 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -187,10 +187,9 @@ class ScrollsController < ActionController::Base end protected - - def rescue_action(e) - raise(e) - end + def rescue_action(e) + raise(e) + end end class AtomFeedTest < ActionController::TestCase @@ -311,11 +310,12 @@ class AtomFeedTest < ActionController::TestCase assert_select "summary div p", :text => "after 2" end end -private + + private def with_restful_routing(resources) with_routing do |set| set.draw do |map| - map.resources(resources) + resources(resources) end yield end diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 35c51ca7ea..fdf3db1cdb 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -106,8 +106,8 @@ module RenderTestCases def test_render_partial_with_errors @view.render(:partial => "test/raise") - flunk "Render did not raise TemplateError" - rescue ActionView::TemplateError => e + flunk "Render did not raise Template::Error" + rescue ActionView::Template::Error => e assert_match "undefined local variable or method `doesnt_exist'", e.message assert_equal "", e.sub_template_message assert_equal "1", e.line_number @@ -116,8 +116,8 @@ module RenderTestCases def test_render_sub_template_with_errors @view.render(:file => "test/sub_template_raise") - flunk "Render did not raise TemplateError" - rescue ActionView::TemplateError => e + flunk "Render did not raise Template::Error" + rescue ActionView::Template::Error => e assert_match "undefined local variable or method `doesnt_exist'", e.message assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message assert_equal "1", e.line_number diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index 05a409d05a..9a448ce328 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -114,7 +114,7 @@ module ActionView test "is able to use named routes" do with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } assert_equal 'http://test.host/contents/new', new_content_url assert_equal 'http://test.host/contents/1', content_url(:id => 1) end @@ -122,7 +122,7 @@ module ActionView test "named routes can be used from helper included in view" do with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } _helpers.module_eval do def render_from_helper new_content_url diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 5c463a4f60..d4b58efe1e 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -451,7 +451,7 @@ class UrlHelperControllerTest < ActionController::TestCase def with_url_helper_routing with_routing do |set| set.draw do |map| - map.show_named_route 'url_helper_controller_test/url_helper/show_named_route', :controller => 'url_helper_controller_test/url_helper', :action => 'show_named_route' + match 'url_helper_controller_test/url_helper/show_named_route', :to => 'url_helper_controller_test/url_helper#show_named_route', :as => :show_named_route end yield end @@ -505,7 +505,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase def with_restful_routing with_routing do |set| set.draw do |map| - map.resources :tasks + resources :tasks end yield end @@ -625,8 +625,8 @@ class PolymorphicControllerTest < ActionController::TestCase def with_restful_routing with_routing do |set| set.draw do |map| - map.resources :workshops do |w| - w.resources :sessions + resources :workshops do + resources :sessions end end yield diff --git a/activemodel/Rakefile b/activemodel/Rakefile index 1f4a8466c9..f098ce0671 100755 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -15,7 +15,6 @@ task :default => :test Rake::TestTask.new do |t| t.libs << "#{dir}/test" t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort - t.verbose = true t.warning = true end diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 505e16c195..e0de27b96d 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -21,33 +21,37 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require 'active_support' + module ActiveModel - autoload :AttributeMethods, 'active_model/attribute_methods' - autoload :Conversion, 'active_model/conversion' - autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' - autoload :Dirty, 'active_model/dirty' - autoload :Errors, 'active_model/errors' - autoload :Lint, 'active_model/lint' - autoload :Name, 'active_model/naming' - autoload :Naming, 'active_model/naming' - autoload :Observer, 'active_model/observing' - autoload :Observing, 'active_model/observing' - autoload :Serialization, 'active_model/serialization' - autoload :StateMachine, 'active_model/state_machine' - autoload :TestCase, 'active_model/test_case' - autoload :Translation, 'active_model/translation' - autoload :Validations, 'active_model/validations' - autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper' - autoload :Validator, 'active_model/validator' - autoload :VERSION, 'active_model/version' + extend ActiveSupport::Autoload + + autoload :AttributeMethods + autoload :Conversion + autoload :DeprecatedErrorMethods + autoload :Dirty + autoload :Errors + autoload :Lint + autoload :Name, 'active_model/naming' + autoload :Naming + autoload :Observer, 'active_model/observing' + autoload :Observing + autoload :Serialization + autoload :StateMachine + autoload :Translation + autoload :Validations + autoload :ValidationsRepairHelper + autoload :Validator + autoload :VERSION module Serializers - autoload :JSON, 'active_model/serializers/json' - autoload :Xml, 'active_model/serializers/xml' + extend ActiveSupport::Autoload + + autoload :JSON + autoload :Xml end end diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 977a101277..8f855958c6 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -235,6 +235,10 @@ module ActiveModel # It's also possible to instantiate related objects, so a Client class belonging to the clients # table with a +master_id+ foreign key can instantiate master through Client#master. def method_missing(method_id, *args, &block) + if method_id == :to_ary || method_id == :to_str + raise NoMethodError, "undefined method `#{method_id}' for #{inspect}:#{self.class}" + end + method_name = method_id.to_s if match = match_attribute_method?(method_name) guard_private_attribute_method!(method_name, args) diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index c1a3f6a4a7..30193956ea 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -1,15 +1,14 @@ -root = File.expand_path('../../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib") +lib = File.expand_path('../../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' require 'active_model' +require 'active_model/test_case' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true diff --git a/activemodel/test/cases/tests_database.rb b/activemodel/test/cases/tests_database.rb index 8dd00ea147..79668dd941 100644 --- a/activemodel/test/cases/tests_database.rb +++ b/activemodel/test/cases/tests_database.rb @@ -2,6 +2,7 @@ require 'logger' $:.unshift(File.dirname(__FILE__) + '/../../../activerecord/lib') require 'active_record' +require 'active_record/test_case' require 'active_record/fixtures' module ActiveModel diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d8bfb1916d..1f7a95a53a 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* MySQL: add_ and change_column support positioning. #3286 [Ben Marini] + +* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen, Gabe da Silveira] + * Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git [Pratik Naik] * PostgreSQL: XML datatype support. #1874 [Leonardo Borges] diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 8195e78826..2376bbd04a 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -21,75 +21,78 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) -activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib" -$:.unshift(activemodel_path) if File.directory?(activemodel_path) +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) require 'active_support' require 'active_model' require 'arel' module ActiveRecord - # TODO: Review explicit loads to see if they will automatically be handled by the initializer. - def self.load_all! - [Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter] - end + extend ActiveSupport::Autoload - autoload :VERSION, 'active_record/version' + autoload :VERSION autoload :ActiveRecordError, 'active_record/base' autoload :ConnectionNotEstablished, 'active_record/base' - autoload :Aggregations, 'active_record/aggregations' - autoload :AssociationPreload, 'active_record/association_preload' - autoload :Associations, 'active_record/associations' - autoload :AttributeMethods, 'active_record/attribute_methods' - autoload :Attributes, 'active_record/attributes' - autoload :AutosaveAssociation, 'active_record/autosave_association' - autoload :Relation, 'active_record/relation' - autoload :Base, 'active_record/base' - autoload :Batches, 'active_record/batches' - autoload :Calculations, 'active_record/calculations' - autoload :Callbacks, 'active_record/callbacks' - autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match' - autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match' - autoload :Migration, 'active_record/migration' + autoload :Aggregations + autoload :AssociationPreload + autoload :Associations + autoload :AttributeMethods + autoload :Attributes + autoload :AutosaveAssociation + autoload :Relation + autoload :Base + autoload :Batches + autoload :Calculations + autoload :Callbacks + autoload :DynamicFinderMatch + autoload :DynamicScopeMatch + autoload :Migration autoload :Migrator, 'active_record/migration' - autoload :NamedScope, 'active_record/named_scope' - autoload :NestedAttributes, 'active_record/nested_attributes' - autoload :Observer, 'active_record/observer' - autoload :QueryCache, 'active_record/query_cache' - autoload :Reflection, 'active_record/reflection' - autoload :Schema, 'active_record/schema' - autoload :SchemaDumper, 'active_record/schema_dumper' - autoload :Serialization, 'active_record/serialization' - autoload :SessionStore, 'active_record/session_store' - autoload :StateMachine, 'active_record/state_machine' - autoload :TestCase, 'active_record/test_case' - autoload :Timestamp, 'active_record/timestamp' - autoload :Transactions, 'active_record/transactions' - autoload :Types, 'active_record/types' - autoload :Validations, 'active_record/validations' + autoload :NamedScope + autoload :NestedAttributes + autoload :Observer + autoload :QueryCache + autoload :Reflection + autoload :Schema + autoload :SchemaDumper + autoload :Serialization + autoload :SessionStore + autoload :StateMachine + autoload :Timestamp + autoload :Transactions + autoload :Types + autoload :Validations module AttributeMethods - autoload :BeforeTypeCast, 'active_record/attribute_methods/before_type_cast' - autoload :Dirty, 'active_record/attribute_methods/dirty' - autoload :PrimaryKey, 'active_record/attribute_methods/primary_key' - autoload :Query, 'active_record/attribute_methods/query' - autoload :Read, 'active_record/attribute_methods/read' - autoload :TimeZoneConversion, 'active_record/attribute_methods/time_zone_conversion' - autoload :Write, 'active_record/attribute_methods/write' + extend ActiveSupport::Autoload + + autoload :BeforeTypeCast + autoload :Dirty + autoload :PrimaryKey + autoload :Query + autoload :Read + autoload :TimeZoneConversion + autoload :Write end module Attributes - autoload :Aliasing, 'active_record/attributes/aliasing' - autoload :Store, 'active_record/attributes/store' - autoload :Typecasting, 'active_record/attributes/typecasting' + extend ActiveSupport::Autoload + + autoload :Aliasing + autoload :Store + autoload :Typecasting end module Type + extend ActiveSupport::Autoload + autoload :Number, 'active_record/types/number' autoload :Object, 'active_record/types/object' autoload :Serialize, 'active_record/types/serialize' @@ -98,12 +101,16 @@ module ActiveRecord end module Locking - autoload :Optimistic, 'active_record/locking/optimistic' - autoload :Pessimistic, 'active_record/locking/pessimistic' + extend ActiveSupport::Autoload + + autoload :Optimistic + autoload :Pessimistic end module ConnectionAdapters - autoload :AbstractAdapter, 'active_record/connection_adapters/abstract_adapter' + extend ActiveSupport::Autoload + + autoload :AbstractAdapter end end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0fcd288fc5..8dcb3a7711 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1325,7 +1325,7 @@ module ActiveRecord if association.nil? || force_reload association = association_proxy_class.new(self, reflection) - retval = association.reload + retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload if retval.nil? and association_proxy_class == BelongsToAssociation association_instance_set(reflection.name, nil) return nil @@ -1370,7 +1370,7 @@ module ActiveRecord association_instance_set(reflection.name, association) end - association.reload if force_reload + reflection.klass.uncached { association.reload } if force_reload association end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 056f29f029..321bba466e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -967,6 +967,29 @@ module ActiveRecord #:nodoc: connection.select_value(sql, "#{name} Count").to_i end + # Resets one or more counter caches to their correct value using an SQL + # count query. This is useful when adding new counter caches, or if the + # counter has been corrupted or modified directly by SQL. + # + # ==== Parameters + # + # * +id+ - The id of the object you wish to reset a counter on. + # * +counters+ - One or more counter names to reset + # + # ==== Examples + # + # # For Post with id #1 records reset the comments_count + # Post.reset_counters(1, :comments) + def reset_counters(id, *counters) + object = find(id) + counters.each do |association| + child_class = reflect_on_association(association).klass + counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + + connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE") + end + end + # A generic "counter updater" implementation, intended primarily to be # used by increment_counter and decrement_counter, but which may also # be useful on its own. It simply does a direct SQL update for the record diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index ad36ff22e3..fa28bc64df 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -470,6 +470,13 @@ module ActiveRecord execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" end + def add_column(table_name, column_name, type, options = {}) + add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(add_column_sql, options) + add_column_position!(add_column_sql, options) + execute(add_column_sql) + end + def change_column_default(table_name, column_name, default) #:nodoc: column = column_for(table_name, column_name) change_column table_name, column_name, column.sql_type, :default => default @@ -498,6 +505,7 @@ module ActiveRecord change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(change_column_sql, options) + add_column_position!(change_column_sql, options) execute(change_column_sql) end @@ -529,6 +537,13 @@ module ActiveRecord end end + def add_column_position!(sql, options) + if options[:first] + sql << " FIRST" + elsif options[:after] + sql << " AFTER #{quote_column_name(options[:after])}" + end + end # SHOW VARIABLES LIKE 'name' def show_variable(name) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 86d14c9c81..ce7eedbb54 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1179,4 +1179,3 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal firm.name, client.firm_name end end - diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index e429c1d157..9bc34bd750 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -64,6 +64,16 @@ class AssociationsTest < ActiveRecord::TestCase assert !firm.clients(true).empty?, "New firm should have reloaded client objects" assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count" end + + def test_force_reload_is_uncached + firm = Firm.create!("name" => "A New Firm, Inc") + client = Client.create!("name" => "TheClient.com", :firm => firm) + ActiveRecord::Base.cache do + firm.clients.each {} + assert_queries(0) { assert_not_nil firm.clients.each {} } + assert_queries(1) { assert_not_nil firm.clients(true).each {} } + end + end end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5c2911eca1..4c16cb4804 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -680,6 +680,16 @@ class BasicsTest < ActiveRecord::TestCase assert_equal -2, Topic.find(2).replies_count end + def test_reset_counters + assert_equal 1, Topic.find(1).replies_count + + Topic.increment_counter("replies_count", 1) + assert_equal 2, Topic.find(1).replies_count + + Topic.reset_counters(1, :replies) + assert_equal 1, Topic.find(1).replies_count + end + def test_update_counter category = categories(:general) assert_nil category.categorizations_count diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 25613da912..307320b964 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -1,11 +1,9 @@ -root = File.expand_path('../../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib") +lib = File.expand_path('../../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6d3f938799..0ef34e440a 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -527,6 +527,53 @@ if ActiveRecord::Base.connection.supports_migrations? assert !Person.column_methods_hash.include?(:last_name) end + if current_adapter?(:MysqlAdapter) + def testing_table_for_positioning + Person.connection.create_table :testings, :id => false do |t| + t.column :first, :integer + t.column :second, :integer + t.column :third, :integer + end + + yield Person.connection + ensure + Person.connection.drop_table :testings rescue nil + end + protected :testing_table_for_positioning + + def test_column_positioning + testing_table_for_positioning do |conn| + assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name } + end + end + + def test_add_column_with_positioning + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer + assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer, :first => true + assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer, :after => :first + assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name } + end + end + + def test_change_column_with_positioning + testing_table_for_positioning do |conn| + conn.change_column :testings, :second, :integer, :first => true + assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.change_column :testings, :second, :integer, :after => :third + assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name } + end + end + end + def test_add_rename Person.delete_all diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb new file mode 100644 index 0000000000..f221def6b6 --- /dev/null +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -0,0 +1,11 @@ +require "cases/helper" +require 'models/topic' + +class YamlSerializationTest < ActiveRecord::TestCase + def test_to_yaml_with_time_with_zone_should_not_raise_exception + Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + ActiveRecord::Base.time_zone_aware_attributes = true + topic = Topic.new(:written_on => DateTime.now) + assert_nothing_raised { topic.to_yaml } + end +end diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 6566e84d4c..9fa1f86914 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -27,11 +27,8 @@ task :default => [ :test ] # Run the unit tests Rake::TestTask.new { |t| - activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib" - t.libs << activesupport_path if File.directory?(activesupport_path) t.libs << "test" t.pattern = 'test/**/*_test.rb' - t.verbose = true t.warning = true } diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index fd4c199b48..84baf4227a 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -21,20 +21,23 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) -require 'active_support' +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) -activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib" -$:.unshift(activemodel_path) if File.directory?(activemodel_path) +require 'active_support' require 'active_model' module ActiveResource - autoload :Base, 'active_resource/base' - autoload :Connection, 'active_resource/connection' - autoload :CustomMethods, 'active_resource/custom_methods' - autoload :Formats, 'active_resource/formats' - autoload :Observing, 'active_resource/observing' - autoload :Validations, 'active_resource/validations' - autoload :HttpMock, 'active_resource/http_mock' + extend ActiveSupport::Autoload + + autoload :Base + autoload :Connection + autoload :CustomMethods + autoload :Formats + autoload :Observing + autoload :Validations + autoload :HttpMock end diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 10849be20c..5fa6d3023b 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -1,18 +1,17 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'rubygems' require 'test/unit' +require 'active_resource' require 'active_support' require 'active_support/test_case' -require 'active_resource' +require 'active_model/test_case' $:.unshift "#{File.dirname(__FILE__)}/../test" require 'setter_trap' diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb index 1d3f7891ec..91349b810a 100644 --- a/activeresource/test/cases/base_test.rb +++ b/activeresource/test/cases/base_test.rb @@ -543,9 +543,9 @@ class BaseTest < Test::Unit::TestCase assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male') assert Person.collection_path(:gender => 'male', :student => true).include?('student=true') - assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) + assert_equal '/people.xml?name[]=bob&name[]=your+uncle%2Bme&name[]=&name[]=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) - assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'}) + assert_equal '/people.xml?struct[a][]=2&struct[a][]=1&struct[b]=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'}) end def test_custom_element_path @@ -581,7 +581,7 @@ class BaseTest < Test::Unit::TestCase assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work') assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work') assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1) - assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) + assert_equal '/people/1/addresses/1.xml?type[]=work&type[]=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) end def test_custom_element_path_with_prefix_and_parameters diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 4edeadf10c..cc4a2ff90e 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls [DHH] + * Update Edinburgh TimeZone to use "Europe/London" instead of "Europe/Dublin" #3310 [Phil Ross] * Update bundled TZInfo to v0.3.15 [Geoff Buesing] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 2ada91830f..08af1d6fca 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -18,7 +18,6 @@ task :default => :test Rake::TestTask.new do |t| t.libs << 'test' t.pattern = 'test/**/*_test.rb' - t.verbose = true t.warning = true end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 0478ae4ebc..9e21b3faf3 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -34,7 +34,38 @@ module ActiveSupport end end -require 'active_support/autoload' +require "active_support/dependencies/autoload" + +module ActiveSupport + extend ActiveSupport::Autoload + + autoload :BacktraceCleaner + autoload :Base64 + autoload :BasicObject + autoload :Benchmarkable + autoload :BufferedLogger + autoload :Cache + autoload :Callbacks + autoload :Concern + autoload :Configurable + autoload :DeprecatedCallbacks + autoload :Deprecation + autoload :Gzip + autoload :Inflector + autoload :Memoizable + autoload :MessageEncryptor + autoload :MessageVerifier + autoload :Multibyte + autoload :OptionMerger + autoload :OrderedHash + autoload :OrderedOptions + autoload :Notifications + autoload :Rescuable + autoload :SecureRandom + autoload :StringInquirer + autoload :XmlMini +end + require 'active_support/vendor' require 'i18n' diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb deleted file mode 100644 index 63f7338a68..0000000000 --- a/activesupport/lib/active_support/autoload.rb +++ /dev/null @@ -1,28 +0,0 @@ -module ActiveSupport - autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' - autoload :Base64, 'active_support/base64' - autoload :BasicObject, 'active_support/basic_object' - autoload :Benchmarkable, 'active_support/benchmarkable' - autoload :BufferedLogger, 'active_support/buffered_logger' - autoload :Cache, 'active_support/cache' - autoload :Callbacks, 'active_support/callbacks' - autoload :Concern, 'active_support/concern' - autoload :Configurable, 'active_support/configurable' - autoload :DependencyModule, 'active_support/dependency_module' - autoload :DeprecatedCallbacks, 'active_support/deprecated_callbacks' - autoload :Deprecation, 'active_support/deprecation' - autoload :Gzip, 'active_support/gzip' - autoload :Inflector, 'active_support/inflector' - autoload :Memoizable, 'active_support/memoizable' - autoload :MessageEncryptor, 'active_support/message_encryptor' - autoload :MessageVerifier, 'active_support/message_verifier' - autoload :Multibyte, 'active_support/multibyte' - autoload :OptionMerger, 'active_support/option_merger' - autoload :OrderedHash, 'active_support/ordered_hash' - autoload :OrderedOptions, 'active_support/ordered_options' - autoload :Notifications, 'active_support/notifications' - autoload :Rescuable, 'active_support/rescuable' - autoload :SecureRandom, 'active_support/secure_random' - autoload :StringInquirer, 'active_support/string_inquirer' - autoload :XmlMini, 'active_support/xml_mini' -end diff --git a/activesupport/lib/active_support/core_ext/class/removal.rb b/activesupport/lib/active_support/core_ext/class/removal.rb index 2dea3c24d5..652be4ed78 100644 --- a/activesupport/lib/active_support/core_ext/class/removal.rb +++ b/activesupport/lib/active_support/core_ext/class/removal.rb @@ -2,7 +2,11 @@ require 'active_support/core_ext/object/extending' require 'active_support/core_ext/module/introspection' class Class #:nodoc: - + + def reachable? + eval("defined?(::#{self}) && ::#{self}.equal?(self)") + end + # Unassociates the class with its subclasses and removes the subclasses # themselves. # 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 5f01bc4fd6..47a31839a6 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -78,7 +78,18 @@ class DateTime # Converts self to a floating-point number of seconds since the Unix epoch def to_f - days_since_unix_epoch = self - ::DateTime.civil(1970) - (days_since_unix_epoch * 86_400).to_f + seconds_since_unix_epoch.to_f + end + + # Converts self to an integer number of seconds since the Unix epoch + def to_i + seconds_since_unix_epoch.to_i + end + + private + + def seconds_since_unix_epoch + seconds_per_day = 86_400 + (self - ::DateTime.civil(1970)) * seconds_per_day end end diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index b11c916f61..d0821a7c68 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -101,6 +101,11 @@ module Enumerable size = block_given? ? select(&block).size : self.size size > 1 end + + # The negative of the Enumerable#include?. Returns true if the collection does not include the object. + def exclude?(object) + !include?(object) + end end class Range #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index e4df8fe338..22749229a3 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -137,10 +137,10 @@ class Logger attr_writer :formatter public :formatter= - alias old_format_datetime format_datetime if method_defined?(:format_datetime) + alias old_format_datetime format_datetime def format_datetime(datetime) datetime end - alias old_msg2str msg2str if method_defined?(:msg2str) + alias old_msg2str msg2str def msg2str(msg) msg end end end diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb index 0cc74c8298..de8121f274 100644 --- a/activesupport/lib/active_support/core_ext/object/extending.rb +++ b/activesupport/lib/active_support/core_ext/object/extending.rb @@ -1,46 +1,53 @@ -class Object - def remove_subclasses_of(*superclasses) #:nodoc: - Class.remove_class(*subclasses_of(*superclasses)) - end - - begin - ObjectSpace.each_object(Class.new) {} - - # 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 Class + # Rubinius + if defined?(Class.__subclasses__) + def descendents subclasses = [] - - superclasses.each do |sup| - ObjectSpace.each_object(class << sup; self; end) do |k| - if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id")) - subclasses << k - end - end - end - + __subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents } subclasses end - rescue RuntimeError - # JRuby and any implementations which cannot handle the objectspace traversal - # above fall back to this implementation - def subclasses_of(*superclasses) #:nodoc: - subclasses = [] + else + # MRI + begin + ObjectSpace.each_object(Class.new) {} - superclasses.each do |sup| + def descendents + subclasses = [] + ObjectSpace.each_object(class << self; self; end) do |k| + subclasses << k unless k == self + end + subclasses + end + # JRuby + rescue StandardError + def descendents + subclasses = [] ObjectSpace.each_object(Class) do |k| - if superclasses.any? { |superclass| k < superclass } && - (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id")) - subclasses << k - end + subclasses << k if k < self end subclasses.uniq! + subclasses end - subclasses end end +end + +class Object + def remove_subclasses_of(*superclasses) #:nodoc: + Class.remove_class(*subclasses_of(*superclasses)) + 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 subclasses_of(*superclasses) #:nodoc: + subclasses = [] + superclasses.each do |klass| + subclasses.concat klass.descendents.select {|k| k.name.blank? || k.reachable?} + end + subclasses + end def extended_by #:nodoc: ancestors = class << self; ancestors end diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index a5e2260791..7ca763cbad 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -38,7 +38,7 @@ class Hash # ==== Examples # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish" # - # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" + # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user[name]=David&user[nationality]=Danish" def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb index 3f1540f685..c9981895b4 100644 --- a/activesupport/lib/active_support/core_ext/object/to_query.rb +++ b/activesupport/lib/active_support/core_ext/object/to_query.rb @@ -7,7 +7,7 @@ class Object # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work. def to_query(key) require 'cgi' unless defined?(CGI) && defined?(CGI::escape) - "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}" + "#{CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(to_param.to_s)}" end end @@ -15,7 +15,7 @@ class Array # Converts an array into a string suitable for use as a URL query string, # using the given +key+ as the param name. # - # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" + # ['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding" def to_query(key) prefix = "#{key}[]" collect { |value| value.to_query(prefix) }.join '&' diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 2cca4763f4..a2a88eb7df 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -7,11 +7,11 @@ class String @_rails_html_safe = true self end - + def html_safe dup.html_safe! end - + alias original_plus + def +(other) result = original_plus(other) @@ -21,7 +21,7 @@ class String result end end - + alias original_concat << def <<(other) result = original_concat(other) @@ -30,14 +30,15 @@ class String end result end - + + remove_method :concat def concat(other) self << other end - + private def also_html_safe?(other) other.respond_to?(:html_safe?) && other.html_safe? end - + end
\ No newline at end of file diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb new file mode 100644 index 0000000000..96ab04c61a --- /dev/null +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -0,0 +1,49 @@ +require "active_support/inflector/methods" + +module ActiveSupport + module Autoload + @@autoloads = {} + @@under_path = nil + @@at_path = nil + @@autoload_defer = false + + def autoload(const_name, path = @@at_path) + full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") + location = path || Inflector.underscore(full) + + unless @@autoload_defer + @@autoloads[const_name] = location + end + super const_name, location + end + + def autoload_under(path) + @@under_path, old_path = path, @@under_path + yield + ensure + @@under_path = old_path + end + + def autoload_at(path) + @@at_path, old_path = path, @@at_path + yield + ensure + @@at_path = old_path + end + + def deferrable + old_defer, @@autoload_defer = @@autoload_defer, true + yield + ensure + @@autoload_defer = old_defer + end + + def self.eager_autoload! + @@autoloads.values.each { |file| require file } + end + + def autoloads + @@autoloads + end + end +end diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index e2540cd598..d9bfcbfcab 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -1,7 +1,4 @@ -require 'thread' require 'active_support/core_ext/module/delegation' -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/secure_random' module ActiveSupport # Notifications provides an instrumentation API for Ruby. To instrument an @@ -41,173 +38,42 @@ module ActiveSupport # to subscribers in a thread. You can use any queue implementation you want. # module Notifications - mattr_accessor :queue, :listener + autoload :Instrumenter, 'active_support/notifications/instrumenter' + autoload :Event, 'active_support/notifications/instrumenter' + autoload :Fanout, 'active_support/notifications/fanout' class << self - delegate :instrument, :transaction_id, :transaction, :to => :instrumenter + attr_writer :notifier + delegate :publish, :subscribe, :instrument, :to => :notifier - def instrumenter - Thread.current[:notifications_instrumeter] ||= Instrumenter.new(publisher) - end - - def publisher - @publisher ||= Publisher.new(queue) - end - - def subscriber - @subscriber ||= Subscriber.new(queue) - end - - def subscribe(pattern=nil, options={}, &block) - with = options[:with] || listener - subscriber.bind(with, pattern).subscribe(&block) + def notifier + @notifier ||= Notifier.new end end - class Instrumenter - def initialize(publisher) - @publisher = publisher - @id = random_id - end - - def transaction - @id, old_id = random_id, @id - yield - ensure - @id = old_id - end - - def transaction_id - @id - end - - def instrument(name, payload={}) - time = Time.now - result = yield if block_given? - ensure - @publisher.publish(name, time, Time.now, result, @id, payload) - end - - private - def random_id - SecureRandom.hex(10) - end - end - - class Publisher - def initialize(queue) + class Notifier + def initialize(queue = Fanout.new) @queue = queue end def publish(*args) @queue.publish(*args) end - end - - class Subscriber - def initialize(queue) - @queue = queue - end - - def bind(listener, pattern) - @listener = listener - @pattern = pattern - self - end - def subscribe - @queue.subscribe(@listener, @pattern) do |*args| - yield(*args) - end + def subscribe(pattern = nil, &block) + @queue.bind(pattern).subscribe(&block) end - end - - class Event - attr_reader :name, :time, :end, :transaction_id, :result, :payload - def initialize(name, start, ending, result, transaction_id, payload) - @name = name - @payload = payload.dup - @time = start - @transaction_id = transaction_id - @end = ending - @result = result + def wait + @queue.wait end - def duration - @duration ||= 1000.0 * (@end - @time) - end + delegate :instrument, :to => :current_instrumenter - def parent_of?(event) - start = (self.time - event.time) * 1000 - start <= 0 && (start + duration >= event.duration) - end - end - - class AsyncListener - def initialize(pattern, &block) - @pattern = pattern - @subscriber = block - @queue = Queue.new - Thread.new { consume } - end - - def publish(name, *args) - if !@pattern || @pattern === name.to_s - @queue << args.unshift(name) - end - end - - def consume - while args = @queue.shift - @subscriber.call(*args) - end - end - - def drained? - @queue.size.zero? - end - end - - class SyncListener - def initialize(pattern, &block) - @pattern = pattern - @subscriber = block - end - - def publish(name, *args) - if !@pattern || @pattern === name.to_s - @subscriber.call(*args.unshift(name)) + private + def current_instrumenter + Thread.current[:"instrumentation_#{object_id}"] ||= Notifications::Instrumenter.new(self) end - end - - def drained? - true - end - end - - # This is a default queue implementation that ships with Notifications. It - # consumes events in a thread and publish them to all registered subscribers. - # - class LittleFanout - def initialize - @listeners = [] - end - - def publish(*args) - @listeners.each { |l| l.publish(*args) } - end - - def subscribe(listener, pattern=nil, &block) - @listeners << listener.new(pattern, &block) - end - - def drained? - @listeners.all? &:drained? - end end end - - Notifications.queue = Notifications::LittleFanout.new - Notifications.listener = Notifications::AsyncListener end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb new file mode 100644 index 0000000000..bb07e4765c --- /dev/null +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -0,0 +1,101 @@ +require 'thread' + +module ActiveSupport + module Notifications + # This is a default queue implementation that ships with Notifications. It + # consumes events in a thread and publish them to all registered subscribers. + # + class Fanout + def initialize(sync = false) + @subscriber_klass = sync ? Subscriber : AsyncSubscriber + @subscribers = [] + end + + def bind(pattern) + Binding.new(self, pattern) + end + + def subscribe(pattern = nil, &block) + @subscribers << @subscriber_klass.new(pattern, &block) + end + + def publish(*args) + @subscribers.each { |s| s.publish(*args) } + end + + def wait + sleep(0.05) until @subscribers.all?(&:drained?) + end + + # Used for internal implementation only. + class Binding #:nodoc: + def initialize(queue, pattern) + @queue = queue + @pattern = + case pattern + when Regexp, NilClass + pattern + else + /^#{Regexp.escape(pattern.to_s)}/ + end + end + + def subscribe(&block) + @queue.subscribe(@pattern, &block) + end + end + + class Subscriber #:nodoc: + def initialize(pattern, &block) + @pattern = pattern + @block = block + end + + def publish(*args) + push(*args) if matches?(args.first) + end + + def drained? + true + end + + private + def matches?(name) + !@pattern || @pattern =~ name.to_s + end + + def push(*args) + @block.call(*args) + end + end + + # Used for internal implementation only. + class AsyncSubscriber < Subscriber #:nodoc: + def initialize(pattern, &block) + super + @events = Queue.new + start_consumer + end + + def drained? + @events.empty? + end + + private + def start_consumer + Thread.new { consume } + end + + def consume + while args = @events.shift + @block.call(*args) + end + end + + def push(*args) + @events << args + end + end + end + end +end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb new file mode 100644 index 0000000000..fb95422af2 --- /dev/null +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -0,0 +1,47 @@ +require 'active_support/secure_random' +require 'active_support/core_ext/module/delegation' + +module ActiveSupport + module Notifications + class Instrumenter + def initialize(notifier) + @id = unique_id + @notifier = notifier + end + + def instrument(name, payload={}) + time = Time.now + result = yield if block_given? + ensure + @notifier.publish(name, time, Time.now, result, @id, payload) + end + + private + def unique_id + SecureRandom.hex(10) + end + end + + class Event + attr_reader :name, :time, :end, :transaction_id, :result, :payload + + def initialize(name, start, ending, result, transaction_id, payload) + @name = name + @payload = payload.dup + @time = start + @transaction_id = transaction_id + @end = ending + @result = result + end + + def duration + @duration ||= 1000.0 * (@end - @time) + end + + def parent_of?(event) + start = (self.time - event.time) * 1000 + start <= 0 && (start + duration >= event.duration) + end + end + end +end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index ab34f975f6..66e32fa5d7 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,450 +1,454 @@ -require 'ruby-prof' - -require 'fileutils' -require 'rails/version' - -module ActiveSupport - module Testing - module Performance - DEFAULTS = - if benchmark = ARGV.include?('--benchmark') # HAX for rake test - { :benchmark => true, - :runs => 4, - :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], - :output => 'tmp/performance' } - else - { :benchmark => false, - :runs => 1, - :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree], - :output => 'tmp/performance' } - end.freeze - - def self.included(base) - base.superclass_delegating_accessor :profile_options - base.profile_options = DEFAULTS - end +begin + require 'ruby-prof' + + require 'fileutils' + require 'rails/version' + require 'active_support/core_ext/class/delegating_attributes' + + module ActiveSupport + module Testing + module Performance + DEFAULTS = + if benchmark = ARGV.include?('--benchmark') # HAX for rake test + { :benchmark => true, + :runs => 4, + :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], + :output => 'tmp/performance' } + else + { :benchmark => false, + :runs => 1, + :min_percent => 0.01, + :metrics => [:process_time, :memory, :objects], + :formats => [:flat, :graph_html, :call_tree], + :output => 'tmp/performance' } + end.freeze + + def self.included(base) + base.superclass_delegating_accessor :profile_options + base.profile_options = DEFAULTS + end - def full_test_name - "#{self.class.name}##{method_name}" - end + def full_test_name + "#{self.class.name}##{method_name}" + end - def run(result) - return if method_name =~ /^default_test$/ + def run(result) + return if method_name =~ /^default_test$/ - yield(self.class::STARTED, name) - @_result = result + yield(self.class::STARTED, name) + @_result = result - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run + run_warmup + if profile_options && metrics = profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + result.add_run + end end end - end - yield(self.class::FINISHED, name) - end + yield(self.class::FINISHED, name) + end - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError - add_error($!) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each + def run_test(metric, mode) + run_callbacks :setup + setup + metric.send(mode) { __send__ @method_name } rescue ::Test::Unit::AssertionFailedError => e add_failure(e.message, e.backtrace) rescue StandardError, ScriptError add_error($!) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError + add_error($!) + end end - end - protected - def run_warmup - GC.start + protected + def run_warmup + GC.start - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - GC.start - end - - def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) + GC.start + end - performer.run - puts performer.report - performer.record - end + def run_profile(metric) + klass = profile_options[:benchmark] ? Benchmarker : Profiler + performer = klass.new(self, metric) - class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + performer.run + puts performer.report + performer.record + end - def initialize(harness, metric) - @harness, @metric = harness, metric - end + class Performer + delegate :run_test, :profile_options, :full_test_name, :to => :@harness - def report - rate = @total / profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] - end + def initialize(harness, metric) + @harness, @metric = harness, metric + end - protected - def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + def report + rate = @total / profile_options[:runs] + '%20s: %s' % [@metric.name, @metric.format(rate)] end - end - class Benchmarker < Performer - def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total + protected + def output_filename + "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + end end - def record - avg = @metric.total / profile_options[:runs].to_i - now = Time.now.utc.xmlschema - with_output_file do |file| - file.puts "#{avg},#{now},#{environment}" + class Benchmarker < Performer + def run + profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + @total = @metric.total end - end - def environment - unless defined? @env - app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - - rails = Rails::VERSION::STRING - if File.directory?('vendor/rails/.git') - Dir.chdir('vendor/rails') do - rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - end + def record + avg = @metric.total / profile_options[:runs].to_i + now = Time.now.utc.xmlschema + with_output_file do |file| + file.puts "#{avg},#{now},#{environment}" end - - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - - @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - @env - end + def environment + unless defined? @env + app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - protected - HEADER = 'measurement,created_at,app,rails,ruby,platform' + rails = Rails::VERSION::STRING + if File.directory?('vendor/rails/.git') + Dir.chdir('vendor/rails') do + rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ + end + end - def with_output_file - fname = output_filename + ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) + @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end + @env end - def output_filename - "#{super}.csv" - end - end + protected + HEADER = 'measurement,created_at,app,rails,ruby,platform' - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.measure_mode rescue false - end + def with_output_file + fname = output_filename + + if new = !File.exist?(fname) + FileUtils.mkdir_p(File.dirname(fname)) + end - def run - return unless @supported + File.open(fname, 'ab') do |file| + file.puts(HEADER) if new + yield file + end + end - RubyProf.measure_mode = @metric.measure_mode - RubyProf.start - RubyProf.pause - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time } + def output_filename + "#{super}.csv" + end end - def report - if @supported + class Profiler < Performer + def initialize(*args) super - else - '%20s: unsupported' % @metric.name + @supported = @metric.measure_mode rescue false end - end - def record - return unless @supported + def run + return unless @supported - klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + RubyProf.measure_mode = @metric.measure_mode + RubyProf.start + RubyProf.pause + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time } + end - klasses.each do |klass| - fname = output_filename(klass) - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - klass.new(@data).print(file, profile_options.slice(:min_percent)) + def report + if @supported + super + else + '%20s: unsupported' % @metric.name end end - end - protected - def output_filename(printer_class) - suffix = - case printer_class.name.demodulize - when 'FlatPrinter'; 'flat.txt' - when 'GraphPrinter'; 'graph.txt' - when 'GraphHtmlPrinter'; 'graph.html' - when 'CallTreePrinter'; 'tree.txt' - else printer_class.name.sub(/Printer$/, '').underscore - end + def record + return unless @supported + + klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact - "#{super()}_#{suffix}" + klasses.each do |klass| + fname = output_filename(klass) + FileUtils.mkdir_p(File.dirname(fname)) + File.open(fname, 'wb') do |file| + klass.new(@data).print(file, profile_options.slice(:min_percent)) + end + end end - end - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil + protected + def output_filename(printer_class) + suffix = + case printer_class.name.demodulize + when 'FlatPrinter'; 'flat.txt' + when 'GraphPrinter'; 'graph.txt' + when 'GraphHtmlPrinter'; 'graph.html' + when 'CallTreePrinter'; 'tree.txt' + else printer_class.name.sub(/Printer$/, '').underscore + end + + "#{super()}_#{suffix}" + end end - class Base - attr_reader :total - - def initialize - @total = 0 + module Metrics + def self.[](name) + const_get(name.to_s.camelize) + rescue NameError + nil end - def name - @name ||= self.class.name.demodulize.underscore - end + class Base + attr_reader :total - def measure_mode - self.class::Mode - end + def initialize + @total = 0 + end - def measure - 0 - end + def name + @name ||= self.class.name.demodulize.underscore + end - def benchmark - with_gc_stats do - before = measure - yield - @total += (measure - before) + def measure_mode + self.class::Mode end - end - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end + def measure + 0 + end - protected - if GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - elsif defined?(GC::Profiler) - def with_gc_stats - GC.start - GC.disable - GC::Profiler.enable - yield - ensure - GC::Profiler.disable - GC.enable - end - else - def with_gc_stats + def benchmark + with_gc_stats do + before = measure yield + @total += (measure - before) end end - end - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 2 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement + def profile + RubyProf.resume + yield + ensure + RubyProf.pause end - end - end - - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME - - def measure - RubyProf.measure_process_time - end - end - - class WallTime < Time - Mode = RubyProf::WALL_TIME - def measure - RubyProf.measure_wall_time + protected + if GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + elsif defined?(GC::Profiler) + def with_gc_stats + GC.start + GC.disable + GC::Profiler.enable + yield + ensure + GC::Profiler.disable + GC.enable + end + else + def with_gc_stats + yield + end + end end - end - class CpuTime < Time - Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - - def initialize(*args) - # FIXME: yeah my CPU is 2.33 GHz - RubyProf.cpu_frequency = 2.33e9 - super - end + class Time < Base + def measure + ::Time.now.to_f + end - def measure - RubyProf.measure_cpu_time + def format(measurement) + if measurement < 2 + '%d ms' % (measurement * 1000) + else + '%.2f sec' % measurement + end + end end - end - class Memory < Base - Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + class ProcessTime < Time + Mode = RubyProf::PROCESS_TIME - # ruby-prof wrapper - if RubyProf.respond_to?(:measure_memory) def measure - RubyProf.measure_memory / 1024.0 + RubyProf.measure_process_time end + end - # Ruby 1.8 + railsbench patch - elsif GC.respond_to?(:allocated_size) - def measure - GC.allocated_size / 1024.0 - end + class WallTime < Time + Mode = RubyProf::WALL_TIME - # Ruby 1.8 + lloyd patch - elsif GC.respond_to?(:heap_info) def measure - GC.heap_info['heap_current_memory'] / 1024.0 + RubyProf.measure_wall_time end + end - # Ruby 1.9 with total_malloc_allocated_size patch - elsif GC.respond_to?(:malloc_total_allocated_size) - def measure - GC.total_malloc_allocated_size / 1024.0 - end + class CpuTime < Time + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - # Ruby 1.9 unpatched - elsif GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 + def initialize(*args) + # FIXME: yeah my CPU is 2.33 GHz + RubyProf.cpu_frequency = 2.33e9 + super end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) def measure - GC.enable - GC.start - kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 - GC.disable - kb + RubyProf.measure_cpu_time end end - def format(measurement) - '%.2f KB' % measurement - end - end + class Memory < Base + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - class Objects < Base - Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + # ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end - if RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end + # Ruby 1.8 + railsbench patch + elsif GC.respond_to?(:allocated_size) + def measure + GC.allocated_size / 1024.0 + end - # Ruby 1.8 + railsbench patch - elsif ObjectSpace.respond_to?(:allocated_objects) - def measure - ObjectSpace.allocated_objects + # Ruby 1.8 + lloyd patch + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['heap_current_memory'] / 1024.0 + end + + # Ruby 1.9 with total_malloc_allocated_size patch + elsif GC.respond_to?(:malloc_total_allocated_size) + def measure + GC.total_malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 unpatched + elsif GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 + GC.disable + kb + end end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) - def measure - GC.enable - GC.start - last = GC::Profiler.data.last - count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] - GC.disable - count + def format(measurement) + '%.2f KB' % measurement end end - def format(measurement) - measurement.to_i.to_s - end - end + class Objects < Base + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + if RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end - if RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - elsif GC.respond_to?(:collections) - def measure - GC.collections - end - elsif GC.respond_to?(:heap_info) - def measure - GC.heap_info['num_gc_passes'] + # Ruby 1.8 + railsbench patch + elsif ObjectSpace.respond_to?(:allocated_objects) + def measure + ObjectSpace.allocated_objects + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + last = GC::Profiler.data.last + count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] + GC.disable + count + end end - end - def format(measurement) - measurement.to_i.to_s + def format(measurement) + measurement.to_i.to_s + end end - end - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - if RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + elsif GC.respond_to?(:collections) + def measure + GC.collections + end + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['num_gc_passes'] + end end - elsif GC.respond_to?(:time) - def measure - GC.time + + def format(measurement) + measurement.to_i.to_s end end - def format(measurement) - '%d ms' % (measurement / 1000) + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time + end + elsif GC.respond_to?(:time) + def measure + GC.time + end + end + + def format(measurement) + '%d ms' % (measurement / 1000) + end end end end end end -end +rescue LoadError +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb index c4aaba7ab3..c3ed659d6b 100644 --- a/activesupport/lib/active_support/whiny_nil.rb +++ b/activesupport/lib/active_support/whiny_nil.rb @@ -45,7 +45,7 @@ class NilClass def method_missing(method, *args, &block) # Ruby 1.9.2: disallow explicit coercion via method_missing. if method == :to_ary || method == :to_str - super + raise NoMethodError, "undefined method `#{method}' for nil:NilClass" elsif klass = METHOD_CLASS_MAP[method] raise_nil_warning_for klass, method, caller else diff --git a/activesupport/test/autoload.rb b/activesupport/test/autoload.rb new file mode 100644 index 0000000000..5d8026a9ca --- /dev/null +++ b/activesupport/test/autoload.rb @@ -0,0 +1,80 @@ +require 'abstract_unit' + +class TestAutoloadModule < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + module ::Fixtures + extend ActiveSupport::Autoload + + module Autoload + extend ActiveSupport::Autoload + end + end + + test "the autoload module works like normal autoload" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "when specifying an :eager constant it still works like normal autoload by default" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test ":eager constants can be triggered via ActiveSupport::Autoload.eager_autoload!" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + ActiveSupport::Autoload.eager_autoload! + assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "the location of autoloaded constants defaults to :name.underscore" do + module ::Fixtures::Autoload + autoload :SomeClass + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "the location of :eager autoloaded constants defaults to :name.underscore" do + module ::Fixtures::Autoload + autoload :SomeClass + end + + ActiveSupport::Autoload.eager_autoload! + assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "a directory for a block of autoloads can be specified" do + module ::Fixtures + autoload_under "autoload" do + autoload :AnotherClass + end + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert_nothing_raised { ::Fixtures::AnotherClass } + end + + test "a path for a block of autoloads can be specified" do + module ::Fixtures + autoload_at "fixtures/autoload/another_class" do + autoload :AnotherClass + end + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert_nothing_raised { ::Fixtures::AnotherClass } + end +end
\ No newline at end of file diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 4341ead488..278c05797b 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -350,6 +350,10 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f end + def test_to_i + assert_equal 946684800, DateTime.civil(2000).to_i + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 66f5f9fbde..4650b796b6 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -89,4 +89,9 @@ class EnumerableTests < Test::Unit::TestCase assert ![ 1, 2 ].many? {|x| x > 1 } assert [ 1, 2, 2 ].many? {|x| x > 1 } end + + def test_exclude? + assert [ 1 ].exclude?(2) + assert ![ 1 ].exclude?(1) + end end diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index 0fb15be654..4d655913cc 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -17,22 +17,22 @@ class ToQueryTest < Test::Unit::TestCase end def test_nested_conversion - assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas', + assert_query_equal 'person[login]=seckar&person[name]=Nicholas', :person => {:name => 'Nicholas', :login => 'seckar'} end def test_multiple_nested - assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10', + assert_query_equal 'account[person][id]=20&person[id]=10', :person => {:id => 10}, :account => {:person => {:id => 20}} end def test_array_values - assert_query_equal 'person%5Bid%5D%5B%5D=10&person%5Bid%5D%5B%5D=20', + assert_query_equal 'person[id][]=10&person[id][]=20', :person => {:id => [10, 20]} end def test_array_values_are_not_sorted - assert_query_equal 'person%5Bid%5D%5B%5D=20&person%5Bid%5D%5B%5D=10', + assert_query_equal 'person[id][]=20&person[id][]=10', :person => {:id => [20, 10]} end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index bb60968a4f..3a12100e86 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -284,6 +284,12 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 946684800, result assert result.is_a?(Integer) end + + def test_to_i_with_wrapped_datetime + datetime = DateTime.civil(2000, 1, 1, 0) + twz = ActiveSupport::TimeWithZone.new(datetime, @time_zone) + assert_equal 946684800, twz.to_i + end def test_to_time assert_equal @twz, @twz.to_time diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index a3ae39d071..cf27357b32 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -62,7 +62,7 @@ class DeprecationTest < ActiveSupport::TestCase end def test_deprecate_class_method - assert_deprecated(/none is deprecated.*test_deprecate_class_method at/) do + assert_deprecated(/none is deprecated.*test_deprecate_class_method/) do assert_equal 1, @dtc.none end diff --git a/activesupport/test/fixtures/autoload/another_class.rb b/activesupport/test/fixtures/autoload/another_class.rb new file mode 100644 index 0000000000..a240b3de41 --- /dev/null +++ b/activesupport/test/fixtures/autoload/another_class.rb @@ -0,0 +1,2 @@ +class Fixtures::AnotherClass +end
\ No newline at end of file diff --git a/activesupport/test/fixtures/autoload/some_class.rb b/activesupport/test/fixtures/autoload/some_class.rb new file mode 100644 index 0000000000..13b3c73ef5 --- /dev/null +++ b/activesupport/test/fixtures/autoload/some_class.rb @@ -0,0 +1,2 @@ +class Fixtures::Autoload::SomeClass +end
\ No newline at end of file diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 35d44367cf..4f880d0db7 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -1,221 +1,170 @@ require 'abstract_unit' -# Allow LittleFanout to be cleaned. -class ActiveSupport::Notifications::LittleFanout - def clear - @listeners.clear - end -end - -class NotificationsEventTest < Test::Unit::TestCase - def test_events_are_initialized_with_details - event = event(:foo, Time.now, Time.now + 1, 1, random_id, :payload => :bar) - assert_equal :foo, event.name - assert_equal Hash[:payload => :bar], event.payload - end - - def test_events_consumes_information_given_as_payload - time = Time.now - event = event(:foo, time, time + 0.01, 1, random_id, {}) - - assert_equal Hash.new, event.payload - assert_equal time, event.time - assert_equal 1, event.result - assert_equal 10.0, event.duration - end - - def test_event_is_parent_based_on_time_frame - time = Time.utc(2009, 01, 01, 0, 0, 1) - - parent = event(:foo, Time.utc(2009), Time.utc(2009) + 100, nil, random_id, {}) - child = event(:foo, time, time + 10, nil, random_id, {}) - not_child = event(:foo, time, time + 100, nil, random_id, {}) - - assert parent.parent_of?(child) - assert !child.parent_of?(parent) - assert !parent.parent_of?(not_child) - assert !not_child.parent_of?(parent) - end +module Notifications + class TestCase < ActiveSupport::TestCase + def setup + Thread.abort_on_exception = true + + @notifier = ActiveSupport::Notifications::Notifier.new + @events = [] + @notifier.subscribe { |*args| @events << event(*args) } + end -protected + def teardown + Thread.abort_on_exception = false + end - def random_id - @random_id ||= ActiveSupport::SecureRandom.hex(10) - end + private + def event(*args) + ActiveSupport::Notifications::Event.new(*args) + end - def event(*args) - ActiveSupport::Notifications::Event.new(*args) + def drain + @notifier.wait + end end -end -class NotificationsMainTest < Test::Unit::TestCase - def setup - @events = [] - Thread.abort_on_exception = true - ActiveSupport::Notifications.subscribe do |*args| - @events << ActiveSupport::Notifications::Event.new(*args) + class PubSubTest < TestCase + def test_events_are_published_to_a_listener + @notifier.publish :foo + @notifier.wait + assert_equal [[:foo]], @events end - end - def teardown - Thread.abort_on_exception = false - ActiveSupport::Notifications.queue.clear - end - - def test_notifications_returns_action_result - result = ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - 1 + 1 - end + def test_subscriber_with_pattern + events = [] + @notifier.subscribe('1') { |*args| events << args } - assert_equal 2, result - end + @notifier.publish '1' + @notifier.publish '1.a' + @notifier.publish 'a.1' + @notifier.wait - def test_events_are_published_to_a_listener - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - 1 + 1 + assert_equal [['1'], ['1.a']], events end - drain - - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end + def test_subscriber_with_pattern_as_regexp + events = [] + @notifier.subscribe(/\d/) { |*args| events << args } - def test_nested_events_can_be_instrumented - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - ActiveSupport::Notifications.instrument(:wot, :payload => "child") do - 1 + 1 - end + @notifier.publish '1' + @notifier.publish 'a.1' + @notifier.publish '1.a' + @notifier.wait - drain - - assert_equal 1, @events.size - assert_equal :wot, @events.first.name - assert_equal Hash[:payload => "child"], @events.first.payload + assert_equal [['1'], ['a.1'], ['1.a']], events end - drain + def test_multiple_subscribers + @another = [] + @notifier.subscribe { |*args| @another << args } + @notifier.publish :foo + @notifier.wait - assert_equal 2, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end - - def test_event_is_pushed_even_if_block_fails - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - raise "OMG" - end rescue RuntimeError - - drain + assert_equal [[:foo]], @events + assert_equal [[:foo]], @another + end - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload + private + def event(*args) + args + end end - def test_event_is_pushed_even_without_block - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") - drain + class SyncPubSubTest < PubSubTest + def setup + Thread.abort_on_exception = true - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload + @notifier = ActiveSupport::Notifications::Notifier.new(ActiveSupport::Notifications::Fanout.new(true)) + @events = [] + @notifier.subscribe { |*args| @events << event(*args) } + end end - def test_subscribed_in_a_transaction - @another = [] - - ActiveSupport::Notifications.subscribe("cache") do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + class InstrumentationTest < TestCase + def test_instrument_returns_block_result + assert_equal 2, @notifier.instrument(:awesome) { 1 + 1 } end - ActiveSupport::Notifications.instrument(:cache){ 1 } - ActiveSupport::Notifications.transaction do - ActiveSupport::Notifications.instrument(:cache){ 1 } - end - ActiveSupport::Notifications.instrument(:cache){ 1 } + def test_nested_events_can_be_instrumented + @notifier.instrument(:awesome, :payload => "notifications") do + @notifier.instrument(:wot, :payload => "child") do + 1 + 1 + end - drain + drain - assert_equal 3, @another.size - before, during, after = @another.map {|e| e.transaction_id } - assert_equal before, after - assert_not_equal before, during - end + assert_equal 1, @events.size + assert_equal :wot, @events.first.name + assert_equal Hash[:payload => "child"], @events.first.payload + end - def test_subscriber_with_pattern - @another = [] + drain - ActiveSupport::Notifications.subscribe("cache") do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + assert_equal 2, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload end - ActiveSupport::Notifications.instrument(:cache){ 1 } - - drain + def test_instrument_publishes_when_exception_is_raised + begin + @notifier.instrument(:awesome, :payload => "notifications") do + raise "OMG" + end + flunk + rescue + end - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result - end + drain - def test_subscriber_with_pattern_as_regexp - @another = [] - ActiveSupport::Notifications.subscribe(/cache/) do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + assert_equal 1, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload end - ActiveSupport::Notifications.instrument(:something){ 0 } - ActiveSupport::Notifications.instrument(:cache){ 1 } - - drain + def test_event_is_pushed_even_without_block + @notifier.instrument(:awesome, :payload => "notifications") + drain - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result + assert_equal 1, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload + end end - def test_subscriber_allows_sync_listeners - @another = [] - ActiveSupport::Notifications.subscribe(/cache/, :with => ActiveSupport::Notifications::SyncListener) do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + class EventTest < TestCase + def test_events_are_initialized_with_details + event = event(:foo, Time.now, Time.now + 1, 1, random_id, :payload => :bar) + assert_equal :foo, event.name + assert_equal Hash[:payload => :bar], event.payload end - Thread.expects(:new).never - ActiveSupport::Notifications.instrument(:something){ 0 } - ActiveSupport::Notifications.instrument(:cache){ 1 } + def test_events_consumes_information_given_as_payload + time = Time.now + event = event(:foo, time, time + 0.01, 1, random_id, {}) - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result - end - - def test_with_several_consumers_and_several_events - @another = [] - ActiveSupport::Notifications.subscribe do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + assert_equal Hash.new, event.payload + assert_equal time, event.time + assert_equal 1, event.result + assert_equal 10.0, event.duration end - 1.upto(100) do |i| - ActiveSupport::Notifications.instrument(:value){ i } - end + def test_event_is_parent_based_on_time_frame + time = Time.utc(2009, 01, 01, 0, 0, 1) - drain + parent = event(:foo, Time.utc(2009), Time.utc(2009) + 100, nil, random_id, {}) + child = event(:foo, time, time + 10, nil, random_id, {}) + not_child = event(:foo, time, time + 100, nil, random_id, {}) - assert_equal 100, @events.size - assert_equal :value, @events.first.name - assert_equal 1, @events.first.result - assert_equal 100, @events.last.result + assert parent.parent_of?(child) + assert !child.parent_of?(parent) + assert !parent.parent_of?(not_child) + assert !not_child.parent_of?(parent) + end - assert_equal 100, @another.size - assert_equal :value, @another.first.name - assert_equal 1, @another.first.result - assert_equal 100, @another.last.result + protected + def random_id + @random_id ||= ActiveSupport::SecureRandom.hex(10) + end end - - private - def drain - sleep(0.05) until ActiveSupport::Notifications.queue.drained? - end end diff --git a/rack b/rack new file mode 160000 +Subproject adf996587aecdd604eff441b8b69e4c47a8c261 diff --git a/rack-mount b/rack-mount deleted file mode 160000 -Subproject 3784e633b42f43a4131e02519be60080d179da2 diff --git a/railties/rails.gemspec b/rails.gemspec index dc66e1efea..878a342903 100644 --- a/railties/rails.gemspec +++ b/rails.gemspec @@ -8,22 +8,16 @@ Gem::Specification.new do |s| on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. EOF - s.add_dependency('rake', '>= 0.8.3') s.add_dependency('activesupport', '= 3.0.pre') - s.add_dependency('activerecord', '= 3.0.pre') s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('actionmailer', '= 3.0.pre') + s.add_dependency('activerecord', '= 3.0.pre') s.add_dependency('activeresource', '= 3.0.pre') + s.add_dependency('actionmailer', '= 3.0.pre') + s.add_dependency('railties', '= 3.0.pre') s.rdoc_options << '--exclude' << '.' s.has_rdoc = false - s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'builtin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] - s.require_path = 'lib' - s.bindir = "bin" - s.executables = ["rails"] - s.default_executable = "rails" - s.author = "David Heinemeier Hansson" s.email = "david@loudthinking.com" s.homepage = "http://www.rubyonrails.org" diff --git a/railties/Rakefile b/railties/Rakefile index e6f698fc74..cb482c90bf 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -10,7 +10,7 @@ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/lib" require 'rails/version' PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' -PKG_NAME = ENV['PKG_NAME'] || 'rails' +PKG_NAME = ENV['PKG_NAME'] || 'railties' PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}" @@ -141,7 +141,7 @@ Rake::RDocTask.new { |rdoc| # Generate GEM ---------------------------------------------------------------------------- -spec = eval(File.read('rails.gemspec')) +spec = eval(File.read('railties.gemspec')) Rake::GemPackageTask.new(spec) do |pkg| pkg.gem_spec = spec diff --git a/railties/bin/rails b/railties/bin/rails index 808df97429..0f51d5739f 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -4,9 +4,10 @@ rescue LoadError # If people are not using gems, the load path must still # be correct. # TODO: Remove the begin / rescue block somehow - $:.unshift File.dirname(__FILE__) + '/../lib' - $:.unshift File.dirname(__FILE__) + '/../../activesupport/lib' - retry + $:.unshift File.expand_path('../../lib', __FILE__) + $:.unshift File.expand_path('../../../activesupport/lib', __FILE__) + $:.unshift File.expand_path('../../../actionpack/lib', __FILE__) + require 'rails/ruby_version_check' end Signal.trap("INT") { puts; exit } diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index c23b67e321..85aeb4af24 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,7 +1,13 @@ require "pathname" +require 'active_support' +require 'active_support/core_ext/kernel/reporting' +require 'active_support/core_ext/logger' +require 'action_dispatch' + require 'rails/initializable' require 'rails/application' +require 'rails/plugin' require 'rails/railties_path' require 'rails/version' require 'rails/rack' @@ -10,4 +16,18 @@ require 'rails/core' require 'rails/configuration' require 'rails/deprecation' require 'rails/initializer' -require 'rails/plugin'
\ No newline at end of file +require 'rails/ruby_version_check' + +# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the +# multibyte safe operations. Plugin authors supporting other encodings +# should override this behaviour and set the relevant +default_charset+ +# on ActionController::Base. +# +# For Ruby 1.9, UTF-8 is the default internal and external encoding. +if RUBY_VERSION < '1.9' + $KCODE='u' +else + Encoding.default_external = Encoding::UTF_8 +end + +RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 7c2d8eab67..e65c20de2c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -3,11 +3,6 @@ module Rails include Initializable class << self - def inherited(klass) - Rails.application ||= klass unless klass.name =~ /Rails/ - super - end - # Stub out App initialize def initialize! new @@ -18,7 +13,11 @@ module Rails end def config - @config ||= Configuration.new + @config ||= begin + config = Configuration.new + Plugin.plugins.each { |p| config.merge(p.config) } + config + end end # TODO: change the plugin loader to use config @@ -32,12 +31,28 @@ module Rails config.root end + def load_tasks + require "rails/tasks" + Dir["#{root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } + Dir["#{root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } + task :environment do + $rails_rake_task = true + initialize! + end + end + def call(env) new.call(env) end end + attr_reader :route_configuration_files + def initialize + Rails.application ||= self + + @route_configuration_files = [] + run_initializers(self) end @@ -45,6 +60,10 @@ module Rails self.class.config end + def root + config.root + end + alias configuration config def middleware @@ -55,6 +74,32 @@ module Rails ActionController::Routing::Routes end + def routes_changed_at + routes_changed_at = nil + + route_configuration_files.each do |config| + config_changed_at = File.stat(config).mtime + + if routes_changed_at.nil? || config_changed_at > routes_changed_at + routes_changed_at = config_changed_at + end + end + + routes_changed_at + end + + def reload_routes! + routes.disable_clear_and_finalize = true + + routes.clear! + route_configuration_files.each { |config| load(config) } + routes.finalize! + + nil + ensure + routes.disable_clear_and_finalize = false + end + def initializers initializers = super plugins.each { |p| initializers += p.initializers } @@ -63,6 +108,8 @@ module Rails def plugins @plugins ||= begin + plugin_names = config.plugins || [:all] + Plugin.plugins.select { |p| plugin_names.include?(p.plugin_name) } + Plugin::Vendored.all(config.plugins || [:all], config.paths.vendor.plugins) end end @@ -72,8 +119,21 @@ module Rails @app.call(env) end - initializer :initialize_rails do - Rails.run_initializers + + # Loads the environment specified by Configuration#environment_path, which + # is typically one of development, test, or production. + initializer :load_environment do + next unless File.file?(config.environment_path) + + config = self.config + + Kernel.class_eval do + meth = instance_method(:config) if Object.respond_to?(:config) + define_method(:config) { config } + require config.environment_path + remove_method :config + define_method(:config, &meth) if meth + end end # Set the <tt>$LOAD_PATH</tt> based on the value of @@ -87,18 +147,8 @@ module Rails # list. By default, all frameworks (Active Record, Active Support, # Action Pack, Action Mailer, and Active Resource) are loaded. initializer :require_frameworks do - begin - require 'active_support' - require 'active_support/core_ext/kernel/reporting' - require 'active_support/core_ext/logger' - - # TODO: This is here to make Sam Ruby's tests pass. Needs discussion. - require 'active_support/core_ext/numeric/bytes' - config.frameworks.each { |framework| require(framework.to_s) } - rescue LoadError => e - # Re-raise as RuntimeError because Mongrel would swallow LoadError. - raise e.to_s - end + require 'active_support/all' unless config.active_support.bare + config.frameworks.each { |framework| require(framework.to_s) } end # Set the paths from which Rails will automatically load source files, and @@ -127,24 +177,6 @@ module Rails end end - # Loads the environment specified by Configuration#environment_path, which - # is typically one of development, test, or production. - initializer :load_environment do - silence_warnings do - next if @environment_loaded - next unless File.file?(config.environment_path) - - @environment_loaded = true - constants = self.class.constants - - eval(IO.read(config.environment_path), binding, config.environment_path) - - (self.class.constants - constants).each do |const| - Object.const_set(const, self.class.const_get(const)) - end - end - end - # Preload all frameworks specified by the Configuration#frameworks. # Used by Passenger to ensure everything's loaded before forking and # to avoid autoload race conditions in JRuby. @@ -178,9 +210,9 @@ module Rails initializer :initialize_middleware_stack do if config.frameworks.include?(:action_controller) - config.middleware.use(::Rack::Lock) unless ActionController::Base.allow_concurrency - config.middleware.use(ActionDispatch::ShowExceptions, ActionController::Base.consider_all_requests_local) - config.middleware.use(ActionDispatch::Callbacks, ActionController::Dispatcher.prepare_each_request) + config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) + config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) + config.middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) config.middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) config.middleware.use(ActionDispatch::ParamsParser) config.middleware.use(::Rack::MethodOverride) @@ -302,9 +334,6 @@ module Rails base_class.send("#{setting}=", value) end end - config.active_support.each do |setting, value| - ActiveSupport.send("#{setting}=", value) - end end # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ @@ -367,6 +396,18 @@ module Rails next unless configuration.frameworks.include?(:action_controller) require 'rails/dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) + + unless configuration.cache_classes + # Setup dev mode route reloading + routes_last_modified = routes_changed_at + reload_routes = lambda do + unless routes_changed_at == routes_last_modified + routes_last_modified = routes_changed_at + reload_routes! + end + end + ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call } + end end # Routing must be initialized after plugins to allow the former to extend the routes @@ -376,10 +417,8 @@ module Rails # loading module used to lazily load controllers (Configuration#controller_paths). initializer :initialize_routing do next unless configuration.frameworks.include?(:action_controller) - - ActionController::Routing.controller_paths += configuration.controller_paths - ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) - ActionController::Routing::Routes.reload! + route_configuration_files << configuration.routes_configuration_file + reload_routes! end # # # Observers are loaded after plugins in case Observers or observed models are modified by plugins. diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index fc22ad64a9..37eb6d40ea 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -6,8 +6,12 @@ module Rails class Console ENVIRONMENTS = %w(production development test) - def self.start - new.start + def self.start(app) + new(app).start + end + + def initialize(app) + @app = app end def start @@ -25,7 +29,7 @@ module Rails ENV['RAILS_ENV'] = ENVIRONMENTS.find { |e| e.index(env) } || env end - require "#{Rails.root}/config/environment" + @app.initialize! require "rails/console_app" require "rails/console_sandbox" if options[:sandbox] require "rails/console_with_helpers" @@ -41,10 +45,10 @@ module Rails end if options[:sandbox] - puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})" + puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})" puts "Any modifications you make will be rolled back on exit" else - puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})" + puts "Loading #{Rails.env} environment (Rails #{Rails.version})" end IRB.start end diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index 4e699acf6b..77c3404343 100644 --- a/railties/lib/rails/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb @@ -2,86 +2,99 @@ require 'erb' require 'yaml' require 'optparse' -include_password = false -options = {} - -OptionParser.new do |opt| - opt.banner = "Usage: dbconsole [options] [environment]" - opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| - include_password = true - end - - opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], - "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| - options['mode'] = mode - end - - opt.on("-h", "--header") do |h| - options['header'] = h - end - - opt.parse!(ARGV) - abort opt.to_s unless (0..1).include?(ARGV.size) -end - -env = ARGV.first || ENV['RAILS_ENV'] || 'development' -unless config = YAML::load(ERB.new(IO.read(Rails.root + "/config/database.yml")).result)[env] - abort "No database is configured for the environment '#{env}'" -end - +module Rails + class DBConsole + def self.start(app) + new(app).start + end -def find_cmd(*commands) - dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) - commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ + def initialize(app) + @app = app + end - full_path_command = nil - found = commands.detect do |cmd| - dir = dirs_on_path.detect do |path| - full_path_command = File.join(path, cmd) - File.executable? full_path_command + def start + include_password = false + options = {} + OptionParser.new do |opt| + opt.banner = "Usage: dbconsole [options] [environment]" + opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| + include_password = true + end + + opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], + "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| + options['mode'] = mode + end + + opt.on("-h", "--header") do |h| + options['header'] = h + end + + opt.parse!(ARGV) + abort opt.to_s unless (0..1).include?(ARGV.size) + end + + env = ARGV.first || ENV['RAILS_ENV'] || 'development' + unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[env] + abort "No database is configured for the environment '#{env}'" + end + + + def find_cmd(*commands) + dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) + commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ + + full_path_command = nil + found = commands.detect do |cmd| + dir = dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + File.executable? full_path_command + end + end + found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") + end + + case config["adapter"] + when "mysql" + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set' + }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + + if config['password'] && include_password + args << "--password=#{config['password']}" + elsif config['password'] && !config['password'].to_s.empty? + args << "-p" + end + + args << config['database'] + + exec(find_cmd('mysql', 'mysql5'), *args) + + when "postgresql" + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password + exec(find_cmd('psql'), config["database"]) + + when "sqlite" + exec(find_cmd('sqlite'), config["database"]) + + when "sqlite3" + args = [] + + args << "-#{options['mode']}" if options['mode'] + args << "-header" if options['header'] + args << config['database'] + + exec(find_cmd('sqlite3'), *args) + else + abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" + end end end - found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") -end - -case config["adapter"] -when "mysql" - args = { - 'host' => '--host', - 'port' => '--port', - 'socket' => '--socket', - 'username' => '--user', - 'encoding' => '--default-character-set' - }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact - - if config['password'] && include_password - args << "--password=#{config['password']}" - elsif config['password'] && !config['password'].to_s.empty? - args << "-p" - end - - args << config['database'] - - exec(find_cmd('mysql', 'mysql5'), *args) - -when "postgresql" - ENV['PGUSER'] = config["username"] if config["username"] - ENV['PGHOST'] = config["host"] if config["host"] - ENV['PGPORT'] = config["port"].to_s if config["port"] - ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password - exec(find_cmd('psql'), config["database"]) - -when "sqlite" - exec(find_cmd('sqlite'), config["database"]) - -when "sqlite3" - args = [] - - args << "-#{options['mode']}" if options['mode'] - args << "-header" if options['header'] - args << config['database'] - - exec(find_cmd('sqlite3'), *args) -else - abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" -end +end
\ No newline at end of file diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index ff2282a534..3687b4460e 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -37,6 +37,15 @@ module Rails Options.new end + def self.start(app) + new(app).start + end + + def initialize(app_const) + super() # Call Rack::Server#initialize without passing any options to use. + @app_const = app_const + end + def start puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}" @@ -54,20 +63,24 @@ module Rails def middleware middlewares = [] - middlewares << [Rails::Rack::LogTailer] unless options[:daemonize] + middlewares << [Rails::Rack::LogTailer, log_path] unless options[:daemonize] middlewares << [Rails::Rack::Debugger] if options[:debugger] Hash.new(middlewares) end + def log_path + "#{File.expand_path(@app_const.root)}/log/#{options[:environment]}.log" + end + def default_options { :Port => 3000, :Host => "0.0.0.0", :environment => (ENV['RAILS_ENV'] || "development").dup, - :rack_file => "#{Rails.root}/config.ru", + :rack_file => "#{@app_const.root}/config.ru", :daemonize => false, :debugger => false, - :pid => "#{Rails.root}/tmp/pids/server.pid", + :pid => "#{@app_const.root}/tmp/pids/server.pid", :AccessLog => [] } end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 3f43a48e2e..0fa42091dd 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -1,27 +1,71 @@ require 'active_support/ordered_options' module Rails - class Configuration - attr_accessor :cache_classes, :load_paths, :load_once_paths, :after_initialize_blocks, - :frameworks, :framework_root_path, :root, :gems, :plugins, - :i18n, :gems, :whiny_nils, :consider_all_requests_local, - :action_controller, :active_record, :action_view, :active_support, - :action_mailer, :active_resource, - :reload_plugins, :log_path, :log_level, :logger, :preload_frameworks, - :database_configuration_file, :cache_store, :time_zone, - :view_path, :metals, :controller_paths, :routes_configuration_file, - :eager_load_paths, :dependency_loading, :paths, :serve_static_assets + # Temporarily separate the plugin configuration class from the main + # configuration class while this bit is being cleaned up. + class Plugin::Configuration def initialize + @options = Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } + end + + def middleware + @middleware ||= ActionDispatch::MiddlewareStack.new + end + + def respond_to?(name) + super || name.to_s =~ config_key_regexp + end + + def merge(config) + @options = config.options.merge(@options) + end + + protected + + attr_reader :options + + private + + def method_missing(name, *args, &blk) + if name.to_s =~ config_key_regexp + return $2 == '=' ? @options[$1] = args.first : @options[$1] + end + + super + end + + def config_key_regexp + bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|') + /^(#{bits})(?:=)?$/ + end + + def config_keys + ([ :active_support, :active_record, :action_controller, + :action_view, :action_mailer, :active_resource ] + + Plugin.plugin_names).map { |n| n.to_s }.uniq + end + end + + class Configuration < Plugin::Configuration + attr_accessor :after_initialize_blocks, :cache_classes, + :consider_all_requests_local, :dependency_loading, :gems, + :load_once_paths, :logger, :metals, :plugins, + :preload_frameworks, :reload_plugins, :serve_static_assets, + :time_zone, :whiny_nils + + attr_writer :cache_store, :controller_paths, + :database_configuration_file, :eager_load_paths, + :frameworks, :framework_root_path, :i18n, :load_paths, + :log_level, :log_path, :paths, :routes_configuration_file, + :view_path + + def initialize + super @load_once_paths = [] @after_initialize_blocks = [] @dependency_loading = true @serve_static_assets = true - - for framework in frameworks - self.send("#{framework}=", ActiveSupport::OrderedOptions.new) - end - self.active_support = ActiveSupport::OrderedOptions.new end def after_initialize(&blk) @@ -80,7 +124,10 @@ module Rails self.preload_frameworks = true self.cache_classes = true self.dependency_loading = false - self.action_controller.allow_concurrency = true + + if respond_to?(:action_controller) + action_controller.allow_concurrency = true + end self end @@ -99,11 +146,6 @@ module Rails defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root}/vendor/rails" end - def middleware - require 'action_dispatch' - @middleware ||= ActionDispatch::MiddlewareStack.new - end - # Loads and returns the contents of the #database_configuration_file. The # contents of the file are processed via ERB before being sent through # YAML::load. diff --git a/railties/lib/rails/console_app.rb b/railties/lib/rails/console_app.rb index 1ad62e5058..2c4a7a51e8 100644 --- a/railties/lib/rails/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -27,6 +27,6 @@ end def reload! puts "Reloading..." ActionDispatch::Callbacks.new(lambda {}, true) - ActionController::Routing::Routes.reload + Rails.application.reload_routes! true end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 85c2fd52a4..0e66c9f58f 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -1,5 +1,6 @@ -activesupport_path = "#{File.dirname(__FILE__)}/../../../activesupport/lib" -$LOAD_PATH.unshift(activesupport_path) if File.directory?(activesupport_path) +activesupport_path = File.expand_path('../../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + require 'active_support' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/metaclass' @@ -9,7 +10,7 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/inflections' # TODO: Do not always push on vendored thor -$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.0/lib") +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.1/lib") require 'rails/generators/base' require 'rails/generators/named_base' diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 8677bf283b..2efdf29127 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -104,7 +104,7 @@ module Rails # file in config/environments. # def environment(data=nil, options={}, &block) - sentinel = "Rails::Initializer.run do |config|" + sentinel = /class [a-z_:]+ < Rails::Application/i data = block.call if !data && block_given? in_root do @@ -269,7 +269,7 @@ module Rails # # === Example # - # route "map.root :controller => :welcome" + # route "root :to => 'welcome'" # def route(routing_code) log :route, routing_code diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 2bcea4bc8f..ae18fa843b 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -48,9 +48,9 @@ module Rails::Generators end def create_root_files - copy_file "Rakefile" copy_file "README" - copy_file "config.ru" + template "Rakefile" + template "config.ru" template "Gemfile" end @@ -123,10 +123,10 @@ module Rails::Generators end def create_script_files - directory "script" do |file| - prepend_file file, "#{shebang}\n", :verbose => false - chmod file, 0755, :verbose => false + directory "script" do |content| + "#{shebang}\n" + content end + chmod "script", 0755, :verbose => false end def create_test_files @@ -181,6 +181,10 @@ module Rails::Generators @app_name ||= File.basename(destination_root) end + def app_const + @app_const ||= app_name.classify + end + def app_secret ActiveSupport::SecureRandom.hex(64) end diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile index 6b6d07e8cc..c19ad0e945 100755 --- a/railties/lib/rails/generators/rails/app/templates/Rakefile +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile @@ -7,4 +7,4 @@ require 'rake' require 'rake/testtask' require 'rake/rdoctask' -require 'rails/tasks' +<%= app_const %>.load_tasks diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru index 509a0da5b7..f3bf3d6117 100644 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -2,4 +2,4 @@ require ::File.expand_path('../config/environment', __FILE__) # Dispatch the request -run Rails.application +run <%= app_const%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 8008c6ba07..2c17de2a23 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -1,6 +1,6 @@ require File.expand_path('../boot', __FILE__) -Rails::Initializer.run do |config| +class <%= app_const %> < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb index 0bb191f205..1684986a59 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb @@ -2,4 +2,4 @@ require File.expand_path('../application', __FILE__) # Initialize the rails application -Rails.initialize! +<%= app_const %>.initialize! diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index ea14ce1bfc..e0f9ca8a12 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -1,43 +1,60 @@ ActionController::Routing::Routes.draw do |map| - # The priority is based upon order of creation: first created -> highest priority. + # The priority is based upon order of creation: + # first created -> highest priority. # Sample of regular route: - # map.connect 'products/:id', :controller => 'catalog', :action => 'view' + # match 'products/:id', :to => 'catalog#view' # Keep in mind you can assign values other than :controller and :action # Sample of named route: - # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' + # match 'products/:id/purchase', :to => 'catalog#purchase', :as => :purchase # This route can be invoked with purchase_url(:id => product.id) # Sample resource route (maps HTTP verbs to controller actions automatically): - # map.resources :products + # resources :products # Sample resource route with options: - # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get } + # resources :products do + # member do + # get :short + # post :toggle + # end + # + # collection do + # get :sold + # end + # end # Sample resource route with sub-resources: - # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller - + # resources :products do + # resources :comments, :sales + # resource :seller + # end + # Sample resource route with more complex sub-resources - # map.resources :products do |products| - # products.resources :comments - # products.resources :sales, :collection => { :recent => :get } + # resources :products do + # resources :comments + # resources :sales do + # get :recent, :on => :collection + # end # end # Sample resource route within a namespace: - # map.namespace :admin do |admin| - # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb) - # admin.resources :products + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products # end - # You can have the root of your site routed with map.root -- just remember to delete public/index.html. - # map.root :controller => "welcome" + # You can have the root of your site routed with "root" + # just remember to delete public/index.html. + # root :to => "welcome" # See how all your routes lay out with "rake routes" - # Install the default routes as the lowest priority. - # Note: These default routes make all actions in every controller accessible via GET requests. You should - # consider removing or commenting them out if you're using named routes and resources. - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' + # Install the default route as the lowest priority. + # Note: The default route make all actions in every controller accessible + # via GET requests. You should consider removing or commenting it out if + # you're using named routes and resources. + match ':controller(/:action(/:id(.:format)))' end diff --git a/railties/lib/rails/generators/rails/app/templates/script/console b/railties/lib/rails/generators/rails/app/templates/script/console.tt index 6043f3792b..4262439e52 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/console +++ b/railties/lib/rails/generators/rails/app/templates/script/console.tt @@ -1,3 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/console' -Rails::Console.start +Rails::Console.start(<%= app_const %>) diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt index e6a1c59394..9dfa24c378 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/dbconsole +++ b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt @@ -1,2 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/dbconsole' +Rails::DBConsole.start(<%= app_const %>)
\ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/script/server b/railties/lib/rails/generators/rails/app/templates/script/server.tt index 709ca002df..d98f677475 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/server +++ b/railties/lib/rails/generators/rails/app/templates/script/server.tt @@ -1,3 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/server' -Rails::Server.start +Rails::Server.start(<%= app_const %>) diff --git a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt index 675f00043f..d8757460e4 100644 --- a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt +++ b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt @@ -1,5 +1,5 @@ class <%= class_name %>Generator < Rails::Generators::NamedBase def self.source_root - @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates')) + @source_root ||= File.expand_path('../templates', __FILE__) end end diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile index 85e8ff1834..23c2245a41 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile @@ -10,7 +10,6 @@ Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' t.pattern = 'test/**/*_test.rb' - t.verbose = true end desc 'Generate documentation for the <%= file_name %> plugin.' diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index e49f9aea1b..a89ce7faed 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -16,7 +16,7 @@ module Rails class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" def add_resource_route - route "map.resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + route "resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" end protected diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE index 71edd2f469..530ccdaf0a 100644 --- a/railties/lib/rails/generators/rails/scaffold/USAGE +++ b/railties/lib/rails/generators/rails/scaffold/USAGE @@ -17,7 +17,7 @@ Description: For example, 'scaffold post title:string body:text published:boolean' gives you a model with those three attributes, a controller that handles the create/show/update/destroy, forms to create and edit your posts, and - an index that lists them all, as well as a map.resources :posts + an index that lists them all, as well as a resources :posts declaration in config/routes.rb. If you want to remove all the generated files, run diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 3866b856b2..add10bd207 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -5,7 +5,7 @@ module Rails end class Initializer - attr_reader :name, :before, :after, :global, :block + attr_reader :name, :block def initialize(name, context, options, &block) @name, @context, @options, @block = name, context, options, block @@ -62,7 +62,7 @@ module Rails end def run_initializers(*args) - return if @ran + return if instance_variable_defined?(:@ran) initializers.each do |initializer| initializer.run(*args) end @@ -106,26 +106,4 @@ module Rails end end end - - include Initializable - - # Check for valid Ruby version (1.8.2 or 1.8.4 or higher). This is done in an - # external file, so we can use it from the `rails` program as well without duplication. - initializer :check_ruby_version, :global => true do - require 'rails/ruby_version_check' - end - - # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the - # multibyte safe operations. Plugin authors supporting other encodings - # should override this behaviour and set the relevant +default_charset+ - # on ActionController::Base. - # - # For Ruby 1.9, UTF-8 is the default internal and external encoding. - initializer :initialize_encoding, :global => true do - if RUBY_VERSION < '1.9' - $KCODE='u' - else - Encoding.default_external = Encoding::UTF_8 - end - end end
\ No newline at end of file diff --git a/railties/lib/rails/initializer.rb b/railties/lib/rails/initializer.rb index 44101dcc94..95478428ec 100644 --- a/railties/lib/rails/initializer.rb +++ b/railties/lib/rails/initializer.rb @@ -1,7 +1,5 @@ require "rails" # In case people require this file directly -RAILS_ENV = (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development').dup unless defined?(RAILS_ENV) - module Rails class Initializer class Error < StandardError ; end diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 86bf032641..90dc1ad8dd 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -2,6 +2,27 @@ module Rails class Plugin include Initializable + def self.plugin_name + @plugin_name || name.demodulize.underscore + end + + def self.inherited(klass) + @plugins ||= [] + @plugins << klass unless klass == Vendored + end + + def self.plugins + @plugins + end + + def self.plugin_names + plugins.map { |p| p.plugin_name } + end + + def self.config + @config ||= Configuration.new + end + class Vendored < Plugin def self.all(list, paths) plugins = [] @@ -55,8 +76,8 @@ module Rails initializer :add_routing_file, :after => :initialize_routing do |app| routing_file = "#{path}/config/routes.rb" if File.exist?(routing_file) - app.routes.add_configuration_file(routing_file) - app.routes.reload! + app.route_configuration_files << routing_file + app.reload_routes! end end end diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb index a237cee6bc..077311be3c 100644 --- a/railties/lib/rails/rack/log_tailer.rb +++ b/railties/lib/rails/rack/log_tailer.rb @@ -1,12 +1,10 @@ module Rails module Rack class LogTailer - EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log" - def initialize(app, log = nil) @app = app - path = Pathname.new(log || EnvironmentLog).cleanpath + path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath @cursor = ::File.size(path) @last_checked = Time.now.to_f diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb index 82113a297c..dc886f4a4d 100644 --- a/railties/lib/rails/tasks.rb +++ b/railties/lib/rails/tasks.rb @@ -16,8 +16,3 @@ $VERBOSE = nil ).each do |task| load "rails/tasks/#{task}.rake" end - -# Load any custom rakefile extensions -# TODO: Don't hardcode these paths. -Dir["#{Rails.root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } -Dir["#{Rails.root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 1611d1d94d..f7b53885c8 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -92,7 +92,7 @@ namespace :rails do namespace :update do def invoke_from_app_generator(method) - require 'generators' + require 'rails/generators' require 'rails/generators/rails/app/app_generator' generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true }, @@ -100,6 +100,12 @@ namespace :rails do generator.invoke(method) end + desc "Update config/boot.rb from your current rails install" + task :configs do + invoke_from_app_generator :create_boot_file + invoke_from_app_generator :create_config_files + end + desc "Update Prototype javascripts from your current rails install" task :javascripts do invoke_from_app_generator :create_prototype_files diff --git a/railties/lib/rails/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake index 7f244ebaed..9433b3556a 100644 --- a/railties/lib/rails/tasks/misc.rake +++ b/railties/lib/rails/tasks/misc.rake @@ -1,8 +1,4 @@ task :default => :test -task :environment do - $rails_rake_task = true - require(File.join(Rails.root, 'config', 'environment')) -end task :rails_env do unless defined? RAILS_ENV diff --git a/railties/lib/rails/tasks/testing.rake b/railties/lib/rails/tasks/testing.rake index fd5e52a05b..57857fb911 100644 --- a/railties/lib/rails/tasks/testing.rake +++ b/railties/lib/rails/tasks/testing.rake @@ -48,7 +48,7 @@ task :test do task end end.compact - abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any? + abort "Errors running #{errors * ', '}!" if errors.any? end namespace :test do @@ -59,7 +59,6 @@ namespace :test do recent_tests('app/controllers/**/*.rb', 'test/functional', since) t.libs << 'test' - t.verbose = true t.test_files = touched.uniq end Rake::Task['test:recent'].comment = "Test recent changes" @@ -84,35 +83,30 @@ namespace :test do end t.libs << 'test' - t.verbose = true end Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)" Rake::TestTask.new(:units => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/unit/**/*_test.rb' - t.verbose = true end Rake::Task['test:units'].comment = "Run the unit tests in test/unit" Rake::TestTask.new(:functionals => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/functional/**/*_test.rb' - t.verbose = true end Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional" Rake::TestTask.new(:integration => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/integration/**/*_test.rb' - t.verbose = true end Rake::Task['test:integration'].comment = "Run the integration tests in test/integration" Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' - t.verbose = true t.options = '-- --benchmark' end Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests' @@ -120,7 +114,6 @@ namespace :test do Rake::TestTask.new(:profile => 'db:test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' - t.verbose = true end Rake::Task['test:profile'].comment = 'Profile the performance tests' @@ -132,8 +125,6 @@ namespace :test do else t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' end - - t.verbose = true end Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)" end diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index 9f6c42945f..b89b7b5c27 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -8,8 +8,13 @@ gem "rack-test", "~> 0.5.0" require 'test/unit' require 'active_support/core_ext/kernel/requires' + +# AP is always present +require 'action_controller/test_case' require 'action_view/test_case' + require 'action_mailer/test_case' if defined?(ActionMailer) +require 'active_model/test_case' if defined?(ActiveModel) if defined?(ActiveRecord) require 'active_record/test_case' diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb deleted file mode 100644 index 885230fac4..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Thor - VERSION = "0.11.8".freeze -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc b/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc index adedfeca9d..606a0cdb52 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc +++ b/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc @@ -17,7 +17,7 @@ * thor help now show information about any class/task. All those calls are possible: - + thor help describe thor help describe:amazing @@ -47,7 +47,7 @@ are in the 'standard' group. Running 'thor -T' will only show the standard tasks - adding --all will show all tasks. You can also filter on a specific group using the --group option: thor -T --group advanced - + == 0.9.6, released 2008-09-13 * Generic improvements diff --git a/railties/lib/rails/vendor/thor-0.12.0/LICENSE b/railties/lib/rails/vendor/thor-0.12.1/LICENSE index 98722da459..98722da459 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/LICENSE +++ b/railties/lib/rails/vendor/thor-0.12.1/LICENSE diff --git a/railties/lib/rails/vendor/thor-0.12.0/README.rdoc b/railties/lib/rails/vendor/thor-0.12.1/README.rdoc index f1106f02b6..ee545f3d97 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/README.rdoc +++ b/railties/lib/rails/vendor/thor-0.12.1/README.rdoc @@ -7,7 +7,7 @@ Example: class App < Thor # [1] map "-L" => :list # [2] - + desc "install APP_NAME", "install one of the available apps" # [3] method_options :force => :boolean, :alias => :string # [4] def install(name) @@ -17,7 +17,7 @@ Example: end # other code end - + desc "list [SEARCH]", "list all of the available apps, limited by SEARCH" def list(search="") # list everything @@ -126,13 +126,13 @@ invoked only once. For example: invoke :two invoke :three end - + desc "two", "Prints 2, 3" def two puts 2 invoke :three end - + desc "three", "Prints 3" def three puts 3 @@ -155,15 +155,15 @@ Thor::Group as this: class Counter < Thor::Group desc "Prints 1, 2, 3" - + def one puts 1 end - + def two puts 2 end - + def three puts 3 end @@ -184,15 +184,15 @@ Besides, Thor::Group can parse arguments and options as Thor tasks: # number will be available as attr_accessor argument :number, :type => :numeric, :desc => "The number to start counting" desc "Prints the 'number' given upto 'number+2'" - + def one puts number + 0 end - + def two puts number + 1 end - + def three puts number + 2 end diff --git a/railties/lib/rails/vendor/thor-0.12.0/Thorfile b/railties/lib/rails/vendor/thor-0.12.1/Thorfile index f71a1e57e2..ff1cb4498a 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/Thorfile +++ b/railties/lib/rails/vendor/thor-0.12.1/Thorfile @@ -56,7 +56,7 @@ class Default < Thor s.test_files.exclude 'spec/sandbox/**/*' end - Jeweler::RubyforgeTasks.new + Jeweler::GemcutterTasks.new rescue LoadError puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb index 68944f140d..68944f140d 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb index d561ccb2aa..4bfb7c2870 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb @@ -114,7 +114,7 @@ class Thor @source_paths ||= self.class.source_paths_for_search end - # Receives a file or directory and search for it in the source paths. + # Receives a file or directory and search for it in the source paths. # def find_in_source_paths(file) relative_root = relative_to_original_destination_root(destination_root, false) @@ -222,7 +222,7 @@ class Thor run "#{command}", config.merge(:with => Thor::Util.ruby_command) end - # Run a thor command. A hash of options can be given and it's converted to + # Run a thor command. A hash of options can be given and it's converted to # switches. # # ==== Parameters diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb index a3d9296823..a3d9296823 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb index 467e63732a..2e0b459fa3 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb @@ -79,11 +79,9 @@ class Thor next if dirname == given_destination base.empty_directory(dirname, config) when /\.tt$/ - destination = base.template(file_source, file_destination[0..-4], config) - @block.call(destination) if @block + destination = base.template(file_source, file_destination[0..-4], config, &@block) else - destination = base.copy_file(file_source, file_destination, config) - @block.call(destination) if @block + destination = base.copy_file(file_source, file_destination, config, &@block) end end end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb index 484cb820f8..484cb820f8 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb index d77d90d448..8a45c83f25 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb @@ -18,12 +18,14 @@ class Thor # # copy_file "doc/README" # - def copy_file(source, destination=nil, config={}) + def copy_file(source, destination=nil, config={}, &block) destination ||= source source = File.expand_path(find_in_source_paths(source.to_s)) create_file destination, nil, config do - File.read(source) + content = File.read(source) + content = block.call(content) if block + content end end @@ -72,13 +74,15 @@ class Thor # # template "doc/README" # - def template(source, destination=nil, config={}) + def template(source, destination=nil, config={}, &block) destination ||= source source = File.expand_path(find_in_source_paths(source.to_s)) context = instance_eval('binding') create_file destination, nil, config do - ERB.new(::File.read(source), nil, '-').result(context) + content = ERB.new(::File.read(source), nil, '-').result(context) + content = block.call(content) if block + content end end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb index 0636ec6591..6b0b42ea02 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb @@ -11,7 +11,7 @@ class Thor # data<String>:: Data to add to the file. Can be given as a block. # config<Hash>:: give :verbose => false to not log the status and the flag # for injection (:after or :before). - # + # # ==== Examples # # inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb index 700d794123..700d794123 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb index 78bc5cf4bf..40d201d9e4 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb @@ -65,7 +65,7 @@ class Thor else self[$1] == args.first end - else + else self[method] end end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb index 27fea5bb35..27fea5bb35 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb index f9b31a35d1..f9b31a35d1 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb index 0964a9667a..021a067a3e 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb @@ -132,7 +132,7 @@ class Thor::Group names.each do |name| unless class_options.key?(name) - raise ArgumentError, "You have to define the option #{name.inspect} " << + raise ArgumentError, "You have to define the option #{name.inspect} " << "before setting invoke_from_option." end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb index 32e6a72454..32e6a72454 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb index 57a3f6e1a5..57a3f6e1a5 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb index aa8ace4719..aa8ace4719 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb index fb5d965e06..fb5d965e06 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb index 9e40ec73fa..e09b4901e2 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb @@ -36,7 +36,7 @@ class Thor # string (--foo=value) or booleans (just --foo). # # By default all options are optional, unless :required is given. - # + # def self.parse(key, value) if key.is_a?(Array) name, *aliases = key diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb index 75092308b5..75092308b5 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb index 0d0757fdda..0d0757fdda 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb index 9dc70ea069..079f9e0c65 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb @@ -36,7 +36,7 @@ class Thor::Runner < Thor #:nodoc: def install(name) initialize_thorfiles - # If a directory name is provided as the argument, look for a 'main.thor' + # If a directory name is provided as the argument, look for a 'main.thor' # task in said directory. begin if File.directory?(File.expand_path(name)) diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb index 1dc8f0e5b4..1dc8f0e5b4 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb index ea9665380b..f6be3575ca 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb @@ -143,7 +143,7 @@ class Thor answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}] case answer - when is?(:yes), is?(:force) + when is?(:yes), is?(:force), "" return true when is?(:no), is?(:skip) return false diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/color.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb index 24704f7885..24704f7885 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/color.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/task.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb index 91c7564d3f..91c7564d3f 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/task.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb index ebae0a3193..ebae0a3193 100644 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb new file mode 100644 index 0000000000..650253d648 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb @@ -0,0 +1,3 @@ +class Thor + VERSION = "0.12.1".freeze +end diff --git a/railties/railties.gemspec b/railties/railties.gemspec new file mode 100644 index 0000000000..a060c3c301 --- /dev/null +++ b/railties/railties.gemspec @@ -0,0 +1,28 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'railties' + s.version = '3.0.pre' + s.summary = "Controls boot-up, rake tasks and generators for the Rails framework." + s.description = <<-EOF + Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick + on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. + EOF + + s.add_dependency('rake', '>= 0.8.3') + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('actionpack', '= 3.0.pre') + + s.rdoc_options << '--exclude' << '.' + s.has_rdoc = false + + s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'builtin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] + s.require_path = 'lib' + s.bindir = "bin" + s.executables = ["rails"] + s.default_executable = "rails" + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "rails" +end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index a3e1916494..89337b7f66 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -44,5 +44,35 @@ module ApplicationTests assert_equal Pathname.new("#{app_path}/app"), Rails.application.root end end + + test "config.active_support.bare does not require all of ActiveSupport" do + add_to_config "config.frameworks = []; config.active_support.bare = true" + + Dir.chdir("#{app_path}/app") do + require "#{app_path}/config/environment" + assert_raises(NoMethodError) { 1.day } + end + end + + test "marking the application as threadsafe sets the correct config variables" do + add_to_config <<-RUBY + config.threadsafe! + RUBY + + require "#{app_path}/config/application" + assert AppTemplate.configuration.action_controller.allow_concurrency + end + + test "the application can be marked as threadsafe when there are no frameworks" do + FileUtils.rm_rf("#{app_path}/config/environments") + add_to_config <<-RUBY + config.frameworks = [] + config.threadsafe! + RUBY + + assert_nothing_raised do + require "#{app_path}/config/application" + end + end end -end
\ No newline at end of file +end diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index ccbcd84176..2ed49d1057 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -11,8 +11,16 @@ module ApplicationTests require "rails/generators" end + def app_const + @app_const ||= Class.new(Rails::Application) + end + + def with_config + yield app_const.config + end + test "generators default values" do - Rails::Initializer.run do |c| + with_config do |c| assert_equal(true, c.generators.colorize_logging) assert_equal({}, c.generators.aliases) assert_equal({}, c.generators.options) @@ -20,7 +28,7 @@ module ApplicationTests end test "generators set rails options" do - Rails::Initializer.run do |c| + with_config do |c| c.generators.orm = :datamapper c.generators.test_framework = :rspec c.generators.helper = false @@ -30,7 +38,7 @@ module ApplicationTests end test "generators set rails aliases" do - Rails::Initializer.run do |c| + with_config do |c| c.generators.aliases = { :rails => { :test_framework => "-w" } } expected = { :rails => { :test_framework => "-w" } } assert_equal expected, c.generators.aliases @@ -38,14 +46,14 @@ module ApplicationTests end test "generators aliases and options on initialization" do - Rails::Initializer.run do |c| + application = with_config do |c| c.frameworks = [] c.generators.rails :aliases => { :test_framework => "-w" } c.generators.orm :datamapper c.generators.test_framework :rspec end # Initialize the application - Rails.initialize! + app_const.initialize! Rails::Generators.configure! assert_equal :rspec, Rails::Generators.options[:rails][:test_framework] @@ -53,19 +61,19 @@ module ApplicationTests end test "generators no color on initialization" do - Rails::Initializer.run do |c| + with_config do |c| c.frameworks = [] c.generators.colorize_logging = false end # Initialize the application - Rails.initialize! + app_const.initialize! Rails::Generators.configure! assert_equal Thor::Base.shell, Thor::Shell::Basic end test "generators with hashes for options and aliases" do - Rails::Initializer.run do |c| + with_config do |c| c.generators do |g| g.orm :datamapper, :migration => false g.plugin :aliases => { :generator => "-g" }, @@ -84,7 +92,7 @@ module ApplicationTests end test "generators with hashes are deep merged" do - Rails::Initializer.run do |c| + with_config do |c| c.generators do |g| g.orm :datamapper, :migration => false g.plugin :aliases => { :generator => "-g" }, diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 719520bf68..fa00d287ca 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -6,26 +6,11 @@ module ApplicationTests def setup build_app + FileUtils.rm_rf("#{app_path}/config/environments") boot_rails require "rails" end - test "initializing an application initializes rails" do - Rails::Initializer.run do |config| - config.root = app_path - end - - if RUBY_VERSION < '1.9' - $KCODE = '' - Rails.initialize! - assert_equal 'UTF8', $KCODE - else - Encoding.default_external = Encoding::US_ASCII - Rails.initialize! - assert_equal Encoding::UTF_8, Encoding.default_external - end - end - test "initializing an application adds the application paths to the load path" do Rails::Initializer.run do |config| config.root = app_path @@ -41,7 +26,9 @@ module ApplicationTests config.frameworks << :action_foo end - assert_raises RuntimeError do + require "active_support/core_ext/load_error" + + assert_raises MissingSourceFile do Rails.initialize! end end diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb index 3da51c4355..e17f1ebdb0 100644 --- a/railties/test/application/load_test.rb +++ b/railties/test/application/load_test.rb @@ -7,21 +7,9 @@ module ApplicationTests include ActiveSupport::Testing::Isolation def rackup - config = "#{app_path}/config.ru" - # Copied from ActionDispatch::Utils.parse_config - # ActionDispatch is not necessarily available at this point. - require 'rack' - if config =~ /\.ru$/ - cfgfile = ::File.read(config) - if cfgfile[/^#\\(.*)/] - opts.parse! $1.split(/\s+/) - end - inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", - nil, config - else - require config - inner_app = Object.const_get(::File.basename(config, '.rb').capitalize) - end + require "rack" + app, options = Rack::Builder.parse_file("#{app_path}/config.ru") + app end def setup @@ -40,14 +28,14 @@ module ApplicationTests test "Rails.application is available after config.ru has been racked up" do rackup - assert Rails.application < Rails::Application + assert Rails.application.is_a?(Rails::Application) end # Passenger still uses AC::Dispatcher, so we need to # keep it working for now test "deprecated ActionController::Dispatcher still works" do rackup - assert ActionController::Dispatcher.new < Rails::Application + assert ActionController::Dispatcher.new.is_a?(Rails::Application) end test "the config object is available on the application object" do diff --git a/railties/test/application/notifications_test.rb b/railties/test/application/notifications_test.rb index 28dfdfcd83..71e406f2c1 100644 --- a/railties/test/application/notifications_test.rb +++ b/railties/test/application/notifications_test.rb @@ -5,21 +5,8 @@ module ApplicationTests include ActiveSupport::Testing::Isolation class MyQueue - attr_reader :events, :subscribers - - def initialize - @events = [] - @subscribers = [] - @listeners = [] - end - def publish(name, *args) - @events << name - end - - def subscribe(listener, pattern=nil, &block) - @listeners << listener - @subscribers << pattern + raise name end end @@ -28,21 +15,16 @@ module ApplicationTests boot_rails require "rails" require "active_support/notifications" + @events = [] Rails::Initializer.run do |c| - c.notifications.queue = MyQueue.new - c.notifications.subscribe(/listening/) do - puts "Cool" - end + c.notifications.notifier = ActiveSupport::Notifications::Notifier.new(MyQueue.new) end end test "new queue is set" do - ActiveSupport::Notifications.instrument(:foo) - assert_equal :foo, ActiveSupport::Notifications.queue.events.first - end - - test "configuration subscribers are loaded" do - assert_equal 1, ActiveSupport::Notifications.queue.subscribers.count { |s| s == /listening/ } + assert_raise RuntimeError do + ActiveSupport::Notifications.publish('foo') + end end end end diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb new file mode 100644 index 0000000000..decde056fd --- /dev/null +++ b/railties/test/application/routing_test.rb @@ -0,0 +1,155 @@ +require 'isolation/abstract_unit' +require 'rack/test' + +module ApplicationTests + class RoutingTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + def setup + build_app + end + + def app + @app ||= begin + boot_rails + require "#{app_path}/config/environment" + + Rails.application + end + end + + test "simple controller" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + end + + test "multiple controllers" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + controller :bar, <<-RUBY + class BarController < ActionController::Base + def index + render :text => "bar" + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + + get '/bar' + assert_equal 'bar', last_response.body + end + + test "nested controller" do + controller 'foo', <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + controller 'admin/foo', <<-RUBY + module Admin + class FooController < ActionController::Base + def index + render :text => "admin::foo" + end + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + + get '/admin/foo' + assert_equal 'admin::foo', last_response.body + end + + test "merges with plugin routes" do + controller 'foo', <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#index' + end + RUBY + + plugin 'bar', 'require File.dirname(__FILE__) + "/app/controllers/bar"' do |plugin| + plugin.write 'app/controllers/bar.rb', <<-RUBY + class BarController < ActionController::Base + def index + render :text => "bar" + end + end + RUBY + + plugin.write 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'bar', :to => 'bar#index' + end + RUBY + end + + get '/foo' + assert_equal 'foo', last_response.body + + get '/bar' + assert_equal 'bar', last_response.body + end + + test "reloads routes when configuration is changed" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def bar + render :text => "bar" + end + + def baz + render :text => "baz" + end + end + RUBY + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#bar' + end + RUBY + + get '/foo' + assert_equal 'bar', last_response.body + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#baz' + end + RUBY + + get '/foo' + assert_equal 'baz', last_response.body + end + end +end diff --git a/railties/test/fixtures/plugins/engines/engine/config/routes.rb b/railties/test/fixtures/plugins/engines/engine/config/routes.rb index cca8d1b146..da44595693 100644 --- a/railties/test/fixtures/plugins/engines/engine/config/routes.rb +++ b/railties/test/fixtures/plugins/engines/engine/config/routes.rb @@ -1,3 +1,3 @@ ActionController::Routing::Routes.draw do |map| - map.connect '/engine', :controller => "engine" + match '/engine', :to => "engine" end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 7d03a37f2a..b69f23c965 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -171,7 +171,7 @@ class ActionsTest < GeneratorsTestCase def test_route_should_add_data_to_the_routes_block_in_config_routes run_generator - route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" + route_command = "route '/login', :controller => 'sessions', :action => 'new'" action :route, route_command assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/ end diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 886af01b22..dff3908ea1 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -62,7 +62,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator assert_file "config/routes.rb" do |route| - assert_match /map\.resources :accounts$/, route + assert_match /resources :accounts$/, route end end @@ -70,7 +70,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator ["account", "--singleton"] assert_file "config/routes.rb" do |route| - assert_match /map\.resource :account$/, route + assert_match /resource :account$/, route end end @@ -93,7 +93,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator ["account"], :behavior => :revoke assert_file "config/routes.rb" do |route| - assert_no_match /map\.resources :accounts$/, route + assert_no_match /resources :accounts$/, route end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index c0652c034f..0b961cee19 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -25,7 +25,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase # Route assert_file "config/routes.rb" do |route| - assert_match /map\.resources :product_lines$/, route + assert_match /resources :product_lines$/, route end # Controller @@ -99,7 +99,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase # Route assert_file "config/routes.rb" do |route| - assert_no_match /map\.resources :product_lines$/, route + assert_no_match /resources :product_lines$/, route end # Controller diff --git a/railties/test/initializer/check_ruby_version_test.rb b/railties/test/initializer/check_ruby_version_test.rb index 97d884e1be..0691caad9d 100644 --- a/railties/test/initializer/check_ruby_version_test.rb +++ b/railties/test/initializer/check_ruby_version_test.rb @@ -7,7 +7,6 @@ module InitializerTests def setup build_app boot_rails - require "rails" end test "rails does not initialize with ruby version 1.8.1" do @@ -50,8 +49,7 @@ module InitializerTests def assert_rails_boots(version = nil) set_ruby_version(version) if version assert_nothing_raised "It appears that rails does not boot" do - Rails::Initializer.run { |c| c.frameworks = [] } - Rails.initialize! + require "rails" end end @@ -59,8 +57,7 @@ module InitializerTests set_ruby_version(version) $stderr = File.open("/dev/null", "w") assert_raises(SystemExit) do - Rails::Initializer.run { |c| c.frameworks = [] } - Rails.initialize! + require "rails" end end end diff --git a/railties/test/plugins/configuration_test.rb b/railties/test/plugins/configuration_test.rb new file mode 100644 index 0000000000..edf8bb37f5 --- /dev/null +++ b/railties/test/plugins/configuration_test.rb @@ -0,0 +1,36 @@ +require "isolation/abstract_unit" + +module PluginsTest + class ConfigurationTest < Test::Unit::TestCase + def setup + build_app + boot_rails + require "rails" + end + + test "config is available to plugins" do + class Foo < Rails::Plugin ; end + assert_nil Foo.config.action_controller.foo + end + + test "a config name is available for the plugin" do + class Foo < Rails::Plugin ; config.foo.greetings = "hello" ; end + assert_equal "hello", Foo.config.foo.greetings + end + + test "plugin configurations are available in the application" do + class Foo < Rails::Plugin ; config.foo.greetings = "hello" ; end + require "#{app_path}/config/application" + assert_equal "hello", AppTemplate.config.foo.greetings + end + + test "plugin config merges are deep" do + class Foo < Rails::Plugin ; config.foo.greetings = 'hello' ; end + class MyApp < Rails::Application + config.foo.bar = "bar" + end + assert_equal "hello", MyApp.config.foo.greetings + assert_equal "bar", MyApp.config.foo.bar + end + end +end
\ No newline at end of file diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index 39f23fa0be..435bd34925 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'action_controller' +require 'action_controller/test_case' require 'rails/info' require 'rails/info_controller' @@ -15,7 +16,7 @@ class InfoControllerTest < ActionController::TestCase def setup ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' + match ':controller/:action' end @controller.stubs(:consider_all_requests_local => false, :local_request? => true) end |