diff options
851 files changed, 7775 insertions, 11198 deletions
diff --git a/.gitignore b/.gitignore index 43c4d7b124..1c94c4b0f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,20 @@ +.DS_Store debug.log doc/rdoc +activemodel/doc activeresource/doc 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 @@ -23,3 +27,8 @@ railties/guides/output *.rbc *.swp *.swo +actionpack/bin +vendor/gems/ +*/vendor/gems/ +bin/ +railties/tmp @@ -3,7 +3,7 @@ require 'rake/rdoctask' env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD'] -PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord railties) +PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord activemodel railties) Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path| require version_path @@ -12,7 +12,7 @@ end desc 'Run all tests by default' task :default => :test -%w(test isolated_test rdoc pgem package release gem).each do |task_name| +%w(test isolated_test rdoc pgem package release gem gemspec).each do |task_name| desc "Run #{task_name} task for all projects" task task_name do errors = [] @@ -27,9 +27,9 @@ task :install => :gem do (PROJECTS - ["railties"]).each do |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") end - desc "Generate documentation for the Rails framework" Rake::RDocTask.new do |rdoc| rdoc.rdoc_dir = 'doc/rdoc' diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index ebcca0d246..a756f35ee8 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -46,39 +46,14 @@ Rake::RDocTask.new { |rdoc| rdoc.rdoc_files.include('lib/action_mailer/*.rb') } +spec = eval(File.read('actionmailer.gemspec')) -# Create compressed packages -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = PKG_NAME - s.summary = "Service layer for easy email delivery and testing." - s.description = %q{Makes it trivial to test and deliver emails sent from a single service layer.} - s.version = PKG_VERSION - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.rubyforge_project = "actionmailer" - s.homepage = "http://www.rubyonrails.org" - - s.add_dependency('actionpack', '= 3.0.pre' + PKG_BUILD) - - s.has_rdoc = true - s.requirements << 'none' - s.require_path = 'lib' - s.autorequire = 'action_mailer' - - s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ] - s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) } -end - Rake::GemPackageTask.new(spec) do |p| p.gem_spec = spec p.need_tar = true p.need_zip = true end - desc "Publish the API documentation" task :pgem => [:package] do require 'rake/contrib/sshpublisher' diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec new file mode 100644 index 0000000000..efe50c4f84 --- /dev/null +++ b/actionmailer/actionmailer.gemspec @@ -0,0 +1,20 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'actionmailer' + s.summary = "Service layer for easy email delivery and testing." + s.description = %q{Makes it trivial to test and deliver emails sent from a single service layer.} + s.version = '3.0.pre' + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.rubyforge_project = "actionmailer" + s.homepage = "http://www.rubyonrails.org" + + s.add_dependency('actionpack', '= 3.0.pre') + + s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] + s.has_rdoc = true + s.requirements << 'none' + s.require_path = 'lib' + s.autorequire = 'action_mailer' +end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 5ecefe7c09..b3005c2e5e 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -256,6 +256,8 @@ module ActionMailer #:nodoc: # +implicit_parts_order+. class Base include AdvAttrAccessor, PartContainer, Quoting, Utils + extend AbstractController::RenderingController + if Object.const_defined?(:ActionController) include ActionController::UrlWriter include ActionController::Layout @@ -479,58 +481,62 @@ module ActionMailer #:nodoc: # Initialize the mailer via the given +method_name+. The body will be # rendered and a new TMail::Mail object created. def create!(method_name, *parameters) #:nodoc: - initialize_defaults(method_name) - __send__(method_name, *parameters) - - # If an explicit, textual body has not been set, we check assumptions. - unless String === @body - # First, we look to see if there are any likely templates that match, - # which include the content-type in their file name (i.e., - # "the_template_file.text.html.erb", etc.). Only do this if parts - # have not already been specified manually. - # if @parts.empty? - template_root.find_all_by_parts(@template, {}, template_path).each do |template| - @parts << Part.new( - :content_type => template.mime_type ? template.mime_type.to_s : "text/plain", - :disposition => "inline", - :charset => charset, - :body => render_template(template, @body) - ) - end - - if @parts.size > 1 - @content_type = "multipart/alternative" if @content_type !~ /^multipart/ - @parts = sort_parts(@parts, @implicit_parts_order) - end - # end - - # Then, if there were such templates, we check to see if we ought to - # also render a "normal" template (without the content type). If a - # normal template exists (or if there were no implicit parts) we render - # it. - # ==== - # TODO: Revisit this - # template_exists = @parts.empty? - # template_exists ||= template_root.find("#{mailer_name}/#{@template}") - # @body = render_message(@template, @body) if template_exists - - # Finally, if there are other message parts and a textual body exists, - # we shift it onto the front of the parts and set the body to nil (so - # that create_mail doesn't try to render it in addition to the parts). - # ==== - # TODO: Revisit this - # if !@parts.empty? && String === @body - # @parts.unshift Part.new(:charset => charset, :body => @body) - # @body = nil - # end - end + ActiveSupport::Orchestra.instrument(:create_mail, :name => method_name) do + initialize_defaults(method_name) + __send__(method_name, *parameters) + + # If an explicit, textual body has not been set, we check assumptions. + unless String === @body + # First, we look to see if there are any likely templates that match, + # which include the content-type in their file name (i.e., + # "the_template_file.text.html.erb", etc.). Only do this if parts + # have not already been specified manually. + # if @parts.empty? + template_root.find_all(@template, {}, template_path).each do |template| + @parts << Part.new( + :content_type => template.mime_type ? template.mime_type.to_s : "text/plain", + :disposition => "inline", + :charset => charset, + :body => render_template(template, @body) + ) + end + + if @parts.size > 1 + @content_type = "multipart/alternative" if @content_type !~ /^multipart/ + @parts = sort_parts(@parts, @implicit_parts_order) + end + # end + + # Then, if there were such templates, we check to see if we ought to + # also render a "normal" template (without the content type). If a + # normal template exists (or if there were no implicit parts) we render + # it. + # ==== + # TODO: Revisit this + # template_exists = @parts.empty? + # template_exists ||= template_root.find("#{mailer_name}/#{@template}") + # @body = render_message(@template, @body) if template_exists + + # Finally, if there are other message parts and a textual body exists, + # we shift it onto the front of the parts and set the body to nil (so + # that create_mail doesn't try to render it in addition to the parts). + # ==== + # TODO: Revisit this + # if !@parts.empty? && String === @body + # @parts.unshift Part.new(:charset => charset, :body => @body) + # @body = nil + # end + end + + # If this is a multipart e-mail add the mime_version if it is not + # already set. + @mime_version ||= "1.0" if !@parts.empty? - # If this is a multipart e-mail add the mime_version if it is not - # already set. - @mime_version ||= "1.0" if !@parts.empty? + # build the mail object itself + @mail = create_mail + end - # build the mail object itself - @mail = create_mail + @mail end # Delivers a TMail::Mail object. By default, it delivers the cached mail @@ -538,19 +544,21 @@ module ActionMailer #:nodoc: # no alternate has been given as the parameter, this will fail. def deliver!(mail = @mail) raise "no mail object available for delivery!" unless mail - + unless logger.nil? logger.info "Sent mail to #{Array(recipients).join(', ')}" logger.debug "\n#{mail.encoded}" end - begin - __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries - rescue Exception => e # Net::SMTP errors or sendmail pipe errors - raise e if raise_delivery_errors + ActiveSupport::Orchestra.instrument(:deliver_mail, :mail => @mail) do + begin + __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries + rescue Exception => e # Net::SMTP errors or sendmail pipe errors + raise e if raise_delivery_errors + end end - return mail + mail end private diff --git a/actionmailer/lib/action_mailer/helpers.rb b/actionmailer/lib/action_mailer/helpers.rb index 1bb8682315..ecd8f0f5b6 100644 --- a/actionmailer/lib/action_mailer/helpers.rb +++ b/actionmailer/lib/action_mailer/helpers.rb @@ -106,7 +106,7 @@ module ActionMailer private # Extend the template class instance with our controller's helper module. def initialize_template_class_with_helper(assigns) - returning(template = initialize_template_class_without_helper(assigns)) do + initialize_template_class_without_helper(assigns).tap do |template| template.extend self.class.master_helper_module end end diff --git a/actionmailer/lib/actionmailer.rb b/actionmailer/lib/actionmailer.rb deleted file mode 100644 index 506416292c..0000000000 --- a/actionmailer/lib/actionmailer.rb +++ /dev/null @@ -1 +0,0 @@ -require 'action_mailer' diff --git a/actionpack/Gemfile b/actionpack/Gemfile new file mode 100644 index 0000000000..7d99e0601b --- /dev/null +++ b/actionpack/Gemfile @@ -0,0 +1,14 @@ +rails_root = Pathname.new(File.dirname(__FILE__)).join("..") + +gem "rack", "~> 1.0.0" +gem "rack-test", "~> 0.5.0" +gem "activesupport", "3.0.pre", :vendored_at => rails_root.join("activesupport") +gem "activemodel", "3.0.pre", :vendored_at => rails_root.join("activemodel") + +only :test do + gem "mocha" + gem "sqlite3-ruby" + gem "RedCloth" +end + +disable_system_gems diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 1fc5018561..e186037aeb 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -17,68 +17,47 @@ RUBY_FORGE_PROJECT = "actionpack" RUBY_FORGE_USER = "webster132" desc "Default Task" -task :default => [ :test ] +task :default => :test + +task :bundle do + puts "Checking if the bundled testing requirements are up to date..." + result = system "gem bundle" + unless result + puts "The gem bundler is not installed. Installing." + system "gem install bundler" + system "gem bundle" + end +end # Run the unit tests desc "Run all unit tests" -task :test => [:test_action_pack, :test_active_record_integration, :test_new_base] +task :test => [:test_action_pack, :test_active_record_integration] -test_lib_dirs = ENV["NEW"] ? ["test/new_base"] : [] -test_lib_dirs.push "test", "test/lib" -# test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] Rake::TestTask.new(:test_action_pack) do |t| - t.libs.concat test_lib_dirs + t.libs << 'test' # make sure we include the tests in alphabetical order as on some systems # this will not happen automatically and the tests (as a whole) will error - t.test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort + t.test_files = Dir.glob('test/{abstract,controller,dispatch,template}/**/*_test.rb').sort t.verbose = true - #t.warning = true + # t.warning = true end -task :isolated_test do - ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - Dir.glob("test/{controller,dispatch,template}/**/*_test.rb").all? do |file| - system(ruby, "-Ilib:#{test_lib_dirs * ':'}", file) - end or raise "Failures" +namespace :test do + Rake::TestTask.new(:isolated) do |t| + t.pattern = 'test/ts_isolated.rb' + end end desc 'ActiveRecord Integration Tests' Rake::TestTask.new(:test_active_record_integration) do |t| - t.libs.concat test_lib_dirs + t.libs << 'test' t.test_files = Dir.glob("test/activerecord/*_test.rb") t.verbose = true end -desc 'New Controller Tests' -Rake::TestTask.new(:test_new_base) do |t| - t.libs << "test/new_base" << "test/lib" - t.test_files = Dir.glob("test/{abstract_controller,new_base}/*_test.rb") - t.verbose = true -end - -desc 'Old Controller Tests on New Base' -Rake::TestTask.new(:test_new_base_on_old_tests) do |t| - t.libs << "test/new_base" << "test/lib" - - t.verbose = true - # ==== Not ported - # * filters - - t.test_files = Dir.glob( "test/{dispatch,template}/**/*_test.rb" ).sort + %w( - action_pack_assertions addresses_render assert_select - base benchmark caching capture content_type cookie dispatcher - filter_params flash helper http_basic_authentication - http_digest_authentication integration layout logging mime_responds - record_identifier redirect render render_js render_json - render_other render_xml request_forgery_protection rescue - resources routing selector send_file test url_rewriter - verification view_paths webservice - ).map { |name| "test/controller/#{name}_test.rb" } -end - # Genereate the RDoc documentation Rake::RDocTask.new { |rdoc| @@ -97,35 +76,8 @@ Rake::RDocTask.new { |rdoc| end } -# Create compressed packages -dist_dirs = [ "lib", "test" ] - -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Web-flow and rendering framework putting the VC in MVC." - s.description = %q{Eases web-request routing, handling, and response as a half-way front, half-way page controller. Implemented with specific emphasis on enabling easy unit/integration testing that doesn't require a browser.} #' - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.rubyforge_project = "actionpack" - s.homepage = "http://www.rubyonrails.org" - - s.has_rdoc = true - s.requirements << 'none' +spec = eval(File.read('actionpack.gemspec')) - s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD) - - s.require_path = 'lib' - s.autorequire = 'action_controller' - - s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "MIT-LICENSE" ] - dist_dirs.each do |dir| - s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - end -end - Rake::GemPackageTask.new(spec) do |p| p.gem_spec = spec p.need_tar = true diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec new file mode 100644 index 0000000000..1930416358 --- /dev/null +++ b/actionpack/actionpack.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'actionpack' + s.version = '3.0.pre' + s.summary = "Web-flow and rendering framework putting the VC in MVC." + s.description = %q{Eases web-request routing, handling, and response as a half-way front, half-way page controller. Implemented with specific emphasis on enabling easy unit/integration testing that doesn't require a browser.} #' + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.rubyforge_project = "actionpack" + s.homepage = "http://www.rubyonrails.org" + + s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] + s.has_rdoc = true + s.requirements << 'none' + + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('activemodel', '= 3.0.pre') + s.add_dependency('rack', '~> 1.0.0') + s.add_dependency('rack-test', '~> 0.5.0') + + s.require_path = 'lib' + s.autorequire = 'action_controller' +end diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index cdeb55b915..76c5845f5b 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -3,7 +3,6 @@ require "active_support/core_ext/module/delegation" module AbstractController autoload :Base, "abstract_controller/base" - autoload :Benchmarker, "abstract_controller/benchmarker" autoload :Callbacks, "abstract_controller/callbacks" autoload :Helpers, "abstract_controller/helpers" autoload :Layouts, "abstract_controller/layouts" diff --git a/actionpack/lib/abstract_controller/benchmarker.rb b/actionpack/lib/abstract_controller/benchmarker.rb deleted file mode 100644 index 58e9564c2f..0000000000 --- a/actionpack/lib/abstract_controller/benchmarker.rb +++ /dev/null @@ -1,38 +0,0 @@ -module AbstractController - module Benchmarker - extend ActiveSupport::Concern - - include Logger - - module ClassMethods - # Execute the passed in block, timing the duration of the block in ms. - # - # ==== Parameters - # title<#to_s>:: The title of block to benchmark - # log_level<Integer>:: A valid log level. Defaults to Logger::DEBUG - # use_silence<TrueClass, FalseClass>:: Whether or not to silence the - # logger for the duration of the block. - # - # ==== Returns - # Object:: The result of the block - def benchmark(title, log_level = ::Logger::DEBUG, use_silence = true) - if logger && logger.level >= log_level - result = nil - ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } - logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)") - result - else - yield - end - end - - # Silences the logger for the duration of the block. - def silence - old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger - yield - ensure - logger.level = old_logger_level if logger - end - end - end -end diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index ea4b59466e..379eaf6d8e 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -10,7 +10,7 @@ module AbstractController include ActiveSupport::NewCallbacks included do - define_callbacks :process_action, "response_body" + define_callbacks :process_action, :terminator => "response_body" end # Override AbstractController::Base's process_action to run the diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index ef66b24dd6..796ef40584 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -119,17 +119,17 @@ module AbstractController when true raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" when nil - self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def _layout(details) self.class.cache_layout(details) do - if view_paths.exists?("#{_implied_layout_name}", details, "layouts") + if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts") "#{_implied_layout_name}" else super end end end - ruby_eval + RUBY end self.class_eval { private :_layout } end @@ -167,7 +167,7 @@ module AbstractController # details<Hash{Symbol => Object}>:: A list of details to restrict # the lookup to. By default, layout lookup is limited to the # formats specified for the current request. - def _layout_for_name(name, details = {:formats => formats}) + def _layout_for_name(name, details) name && _find_layout(name, details) end @@ -183,7 +183,7 @@ module AbstractController def _find_layout(name, details) # TODO: Make prefix actually part of details in ViewPath#find_by_parts prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" - view_paths.find(name, details, prefix) + find_template(name, details, :_prefix => prefix) end # Returns the default layout for this controller and a given set of details. diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb index 1b879b963b..f4d017b8e5 100644 --- a/actionpack/lib/abstract_controller/logger.rb +++ b/actionpack/lib/abstract_controller/logger.rb @@ -4,6 +4,26 @@ module AbstractController module Logger extend ActiveSupport::Concern + included do + cattr_accessor :logger + end + + module ClassMethods #:nodoc: + # Logs a message appending the value measured. + def log_with_time(message, time, log_level=::Logger::DEBUG) + return unless logger && logger.level >= log_level + logger.add(log_level, "#{message} (%.1fms)" % time) + end + + # Silences the logger for the duration of the block. + def silence + old_logger_level, logger.level = logger.level, ::Logger::ERROR if logge + yield + ensure + logger.level = old_logger_level if logger + end + end + # A class that allows you to defer expensive processing # until the logger actually tries to log. Otherwise, you are # forced to do the processing in advance, and send the @@ -11,43 +31,44 @@ module AbstractController # just discard the String if the log level is too low. # # TODO: Require that Rails loggers accept a block. - class DelayedLog - def initialize(&blk) - @blk = blk + class DelayedLog < ActiveSupport::BasicObject + def initialize(&block) + @str, @block = nil, block end - def to_s - @blk.call + def method_missing(*args, &block) + unless @str + @str, @block = @block.call, nil + end + @str.send(*args, &block) end - alias to_str to_s - end - - included do - cattr_accessor :logger end # Override process_action in the AbstractController::Base # to log details about the method. def process_action(action) - retval = super + event = ActiveSupport::Orchestra.instrument(:process_action, + :controller => self, :action => action) do + super + end if logger log = DelayedLog.new do "\n\nProcessing #{self.class.name}\##{action_name} " \ - "to #{request.formats} " \ - "(for #{request_origin}) [#{request.method.to_s.upcase}]" + "to #{request.formats} (for #{request_origin}) " \ + "(%.1fms) [#{request.method.to_s.upcase}]" % event.duration end logger.info(log) end - retval + event.result end private + # Returns the request origin with the IP and time. This needs to be cached, + # otherwise we would get different results for each time it calls. def request_origin - # this *needs* to be cached! - # otherwise you'd get different results if calling it more than once @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" end end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index feca1bc4b7..bbf941aa32 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -112,12 +112,18 @@ module AbstractController name = (options[:_template_name] || action_name).to_s options[:_template] ||= with_template_cache(name) do - view_paths.find( - name, { :formats => formats }, options[:_prefix], options[:_partial] - ) + find_template(name, { :formats => formats }, options) end end - + + def find_template(name, details, options) + view_paths.find(name, details, options[:_prefix], options[:_partial]) + end + + def template_exists?(name, details, options) + view_paths.exists?(name, details, options[:_prefix], options[:_partial]) + end + def with_template_cache(name) yield end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index d27a867efe..6702cb47f8 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,10 +1,12 @@ module ActionController autoload :Base, "action_controller/base" + autoload :Benchmarking, "action_controller/metal/benchmarking" autoload :ConditionalGet, "action_controller/metal/conditional_get" + 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 :Layouts, "action_controller/metal/layouts" autoload :RackConvenience, "action_controller/metal/rack_convenience" autoload :Rails2Compatibility, "action_controller/metal/compatibility" autoload :Redirector, "action_controller/metal/redirector" @@ -12,17 +14,18 @@ module ActionController 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" - autoload :Session, "action_controller/metal/session" - autoload :Helpers, "action_controller/metal/helpers" # Ported modules # require 'action_controller/routing' autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Integration, 'action_controller/testing/integration' + 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/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' @@ -42,7 +45,6 @@ module ActionController autoload :Cookies, 'action_controller/metal/cookies' autoload :ActionControllerError, 'action_controller/metal/exceptions' - autoload :SessionRestoreError, 'action_controller/metal/exceptions' autoload :RenderError, 'action_controller/metal/exceptions' autoload :RoutingError, 'action_controller/metal/exceptions' autoload :MethodNotAllowed, 'action_controller/metal/exceptions' @@ -59,9 +61,8 @@ end autoload :HTML, 'action_controller/vendor/html-scanner' autoload :AbstractController, 'abstract_controller' -autoload :Rack, 'action_dispatch' -autoload :ActionDispatch, 'action_dispatch' -autoload :ActionView, 'action_view' +require 'action_dispatch' +require 'action_view' # 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 698189bd46..5338a70104 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -2,7 +2,6 @@ module ActionController class Base < Metal abstract! - include AbstractController::Benchmarker include AbstractController::Callbacks include AbstractController::Logger @@ -15,6 +14,7 @@ module ActionController include ActionController::Layouts include ActionController::ConditionalGet include ActionController::RackConvenience + include ActionController::Benchmarking # Legacy modules include SessionManagement @@ -51,7 +51,7 @@ module ActionController def method_for_action(action_name) super || begin - if view_paths.exists?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) + if template_exists?(action_name.to_s, {:formats => formats}, :_prefix => controller_path) "default_render" end end diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 4ef600bea0..59e24619e3 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -53,11 +53,11 @@ module ActionController #:nodoc: return content unless cache_configured? key = fragment_cache_key(key) - - self.class.benchmark "Cached fragment miss: #{key}" do + event = ActiveSupport::Orchestra.instrument(:write_fragment, :key => key) do cache_store.write(key, content, options) end + self.class.log_with_time("Cached fragment miss: #{key}", event.duration) content end @@ -66,10 +66,12 @@ module ActionController #:nodoc: return unless cache_configured? key = fragment_cache_key(key) - - self.class.benchmark "Cached fragment hit: #{key}" do + event = ActiveSupport::Orchestra.instrument(:read_fragment, :key => key) do cache_store.read(key, options) end + + self.class.log_with_time("Cached fragment hit: #{key}", event.duration) + event.result end # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats) @@ -77,10 +79,12 @@ module ActionController #:nodoc: return unless cache_configured? key = fragment_cache_key(key) - - self.class.benchmark "Cached fragment exists?: #{key}" do + event = ActiveSupport::Orchestra.instrument(:fragment_exist?, :key => key) do cache_store.exist?(key, options) end + + self.class.log_with_time("Cached fragment exists?: #{key}", event.duration) + event.result end # Removes fragments from the cache. @@ -103,17 +107,21 @@ module ActionController #:nodoc: def expire_fragment(key, options = nil) return unless cache_configured? - key = key.is_a?(Regexp) ? key : fragment_cache_key(key) + key = fragment_cache_key(key) unless key.is_a?(Regexp) + message = nil - if key.is_a?(Regexp) - self.class.benchmark "Expired fragments matching: #{key.source}" do + event = ActiveSupport::Orchestra.instrument(:expire_fragment, :key => key) do + if key.is_a?(Regexp) + message = "Expired fragments matching: #{key.source}" cache_store.delete_matched(key, options) - end - else - self.class.benchmark "Expired fragment: #{key}" do + else + message = "Expired fragment: #{key}" cache_store.delete(key, options) end end + + self.class.log_with_time(message, event.duration) + event.result end end end diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index bd3b5a5875..4fb154470f 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -62,21 +62,29 @@ module ActionController #:nodoc: # expire_page "/lists/show" def expire_page(path) return unless perform_caching + path = page_cache_path(path) - benchmark "Expired page: #{page_cache_file(path)}" do - File.delete(page_cache_path(path)) if File.exist?(page_cache_path(path)) + event = ActiveSupport::Orchestra.instrument(:expire_page, :path => path) do + File.delete(path) if File.exist?(path) end + + log_with_time("Expired page: #{path}", event.duration) + event.result end # Manually cache the +content+ in the key determined by +path+. Example: # cache_page "I'm the cached content", "/lists/show" def cache_page(content, path) return unless perform_caching + path = page_cache_path(path) - benchmark "Cached page: #{page_cache_file(path)}" do - FileUtils.makedirs(File.dirname(page_cache_path(path))) - File.open(page_cache_path(path), "wb+") { |f| f.write(content) } + event = ActiveSupport::Orchestra.instrument(:cache_page, :path => path) do + FileUtils.makedirs(File.dirname(path)) + File.open(path, "wb+") { |f| f.write(content) } end + + log_with_time("Cached page: #{path}", event.duration) + event.result end # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that @@ -149,4 +157,4 @@ module ActionController #:nodoc: end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb new file mode 100644 index 0000000000..86336b6bc4 --- /dev/null +++ b/actionpack/lib/action_controller/deprecated/integration_test.rb @@ -0,0 +1,2 @@ +ActionController::Integration = ActionDispatch::Integration +ActionController::IntegrationTest = ActionDispatch::IntegrationTest diff --git a/actionpack/lib/action_controller/deprecated/performance_test.rb b/actionpack/lib/action_controller/deprecated/performance_test.rb new file mode 100644 index 0000000000..fcf47d31a7 --- /dev/null +++ b/actionpack/lib/action_controller/deprecated/performance_test.rb @@ -0,0 +1 @@ +ActionController::PerformanceTest = ActionDispatch::PerformanceTest diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb index 9ad1cadfd3..e04da42637 100644 --- a/actionpack/lib/action_controller/dispatch/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb @@ -7,15 +7,6 @@ module ActionController cattr_accessor :prepare_each_request self.prepare_each_request = false - cattr_accessor :router - self.router = Routing::Routes - - cattr_accessor :middleware - self.middleware = ActionDispatch::MiddlewareStack.new do |middleware| - middlewares = File.join(File.dirname(__FILE__), "middlewares.rb") - middleware.instance_eval(File.read(middlewares), middlewares, 1) - end - class << self def define_dispatcher_callbacks(cache_classes) unless cache_classes @@ -24,7 +15,7 @@ module ActionController # Development mode callbacks ActionDispatch::Callbacks.before_dispatch do |app| - ActionController::Dispatcher.router.reload + ActionController::Routing::Routes.reload end ActionDispatch::Callbacks.after_dispatch do @@ -54,11 +45,12 @@ module ActionController end end - delegate :to_prepare, :prepare_dispatch, :before_dispatch, :after_dispatch, + delegate :to_prepare, :before_dispatch, :around_dispatch, :after_dispatch, :to => ActionDispatch::Callbacks def new - @@middleware.build(@@router) + # DEPRECATE Rails application fallback + Rails.application end end end diff --git a/actionpack/lib/action_controller/dispatch/middlewares.rb b/actionpack/lib/action_controller/dispatch/middlewares.rb deleted file mode 100644 index b25ed3fd3f..0000000000 --- a/actionpack/lib/action_controller/dispatch/middlewares.rb +++ /dev/null @@ -1,18 +0,0 @@ -use "Rack::Lock", :if => lambda { - !ActionController::Base.allow_concurrency -} - -use "ActionDispatch::ShowExceptions", lambda { ActionController::Base.consider_all_requests_local } -use "ActionDispatch::Callbacks", lambda { ActionController::Dispatcher.prepare_each_request } -use "ActionDispatch::Rescue", lambda { - controller = (::ApplicationController rescue ActionController::Base) - # TODO: Replace with controller.action(:_rescue_action) - controller.method(:rescue_action) -} - -use lambda { ActionController::Base.session_store }, - lambda { ActionController::Base.session_options } - -use "ActionDispatch::ParamsParser" -use "Rack::MethodOverride" -use "Rack::Head"
\ No newline at end of file diff --git a/actionpack/lib/action_controller/legacy/layout.rb b/actionpack/lib/action_controller/legacy/layout.rb index 43aea0eba2..53762158fc 100644 --- a/actionpack/lib/action_controller/legacy/layout.rb +++ b/actionpack/lib/action_controller/legacy/layout.rb @@ -191,7 +191,7 @@ module ActionController #:nodoc: def memoized_find_layout(layout, formats) #:nodoc: return layout if layout.nil? || layout.respond_to?(:render) prefix = layout.to_s =~ /layouts\// ? nil : "layouts" - view_paths.find(layout.to_s, {:formats => formats}, prefix) + find_template(layout.to_s, {:formats => formats}, :_prefix => prefix) end def find_layout(*args) @@ -200,7 +200,7 @@ module ActionController #:nodoc: end def layout_list #:nodoc: - Array(view_paths).sum([]) { |path| Dir["#{path.to_str}/layouts/**/*"] } + Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] } end memoize :layout_list diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 51fbba3661..e9007d3631 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/class/inheritable_attributes' + module ActionController # ActionController::Metal provides a way to get a valid Rack application from a controller. # @@ -79,6 +81,15 @@ module ActionController end class ActionEndpoint + @@endpoints = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + + def self.for(controller, action, stack) + @@endpoints[controller][action][stack] ||= begin + endpoint = new(controller, action) + stack.build(endpoint) + end + end + def initialize(controller, action) @controller, @action = controller, action end @@ -88,6 +99,16 @@ module ActionController end end + extlib_inheritable_accessor(:middleware_stack) { ActionDispatch::MiddlewareStack.new } + + def self.use(*args) + middleware_stack.use(*args) + end + + def self.middleware + middleware_stack + end + # Return a rack endpoint for the given action. Memoize the endpoint, so # multiple calls into MyController.action will return the same object # for the same action. @@ -98,8 +119,7 @@ module ActionController # ==== Returns # Proc:: A rack application def self.action(name) - @actions ||= {} - @actions[name.to_s] ||= ActionEndpoint.new(self, name) + ActionEndpoint.for(self, name, middleware_stack) end end end diff --git a/actionpack/lib/action_controller/metal/benchmarking.rb b/actionpack/lib/action_controller/metal/benchmarking.rb new file mode 100644 index 0000000000..d4cb1e122d --- /dev/null +++ b/actionpack/lib/action_controller/metal/benchmarking.rb @@ -0,0 +1,92 @@ +require 'benchmark' + +module ActionController #:nodoc: + # The benchmarking module times the performance of actions and reports to the logger. If the Active Record + # package has been included, a separate timing section for database calls will be added as well. + module Benchmarking #:nodoc: + extend ActiveSupport::Concern + + module ClassMethods + # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it + # (unless <tt>use_silence</tt> is set to false). + # + # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it + # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark + # will only be conducted if the log level is low enough. + def benchmark(title, log_level = Logger::DEBUG, use_silence = true) + if logger && logger.level == log_level + result = nil + ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } + logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)") + result + else + yield + end + end + end + + protected + def render(*args, &block) + if logger + if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + db_runtime = ActiveRecord::Base.connection.reset_runtime + end + + render_output = nil + @view_runtime = Benchmark.ms { render_output = super } + + if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + @db_rt_before_render = db_runtime + @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime + @view_runtime -= @db_rt_after_render + end + + render_output + else + super + end + end + + private + def process_action(*args) + if logger + ms = [Benchmark.ms { super }, 0.01].max + logging_view = defined?(@view_runtime) + logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? + + log_message = 'Completed in %.0fms' % ms + + if logging_view || logging_active_record + log_message << " (" + log_message << view_runtime if logging_view + + if logging_active_record + log_message << ", " if logging_view + log_message << active_record_runtime + ")" + else + ")" + end + end + + log_message << " | #{response.status}" + log_message << " [#{complete_request_uri rescue "unknown"}]" + + logger.info(log_message) + response.headers["X-Runtime"] = "%.0f" % ms + else + super + end + end + + def view_runtime + "View: %.0f" % @view_runtime + end + + def active_record_runtime + db_runtime = ActiveRecord::Base.connection.reset_runtime + db_runtime += @db_rt_before_render if @db_rt_before_render + db_runtime += @db_rt_after_render if @db_rt_after_render + "DB: %.0f" % db_runtime + end + end +end diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index d0811254cb..b9d23da3e0 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -2,9 +2,6 @@ module ActionController class ActionControllerError < StandardError #:nodoc: end - class SessionRestoreError < ActionControllerError #:nodoc: - end - class RenderError < ActionControllerError #:nodoc: end diff --git a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb index 065e62a37f..4259d9de19 100644 --- a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb +++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb @@ -49,7 +49,7 @@ module ActionController end elsif block_given? key = key.dup - value = value.dup if value + value = value.dup if value.duplicable? yield key, value filtered_parameters[key] = value else diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 950105e63f..3026067868 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -179,21 +179,8 @@ module ActionController #:nodoc: def respond_to(*mimes, &block) raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? - collector = Collector.new - mimes = collect_mimes_from_class_level if mimes.empty? - mimes.each { |mime| collector.send(mime) } - block.call(collector) if block_given? - - if format = request.negotiate_mime(collector.order) - self.formats = [format.to_sym] - - if response = collector.response_for(format) - response.call - else - default_render - end - else - head :not_acceptable + if response = retrieve_response_from_mimes(mimes, &block) + response.call end end @@ -227,10 +214,11 @@ module ActionController #:nodoc: # a proc to it. # def respond_with(*resources, &block) - respond_to(&block) - rescue ActionView::MissingTemplate - options = resources.extract_options! - (options.delete(:responder) || responder).call(self, resources, options) + if response = retrieve_response_from_mimes([], &block) + options = resources.extract_options! + options.merge!(:default_response => response) + (options.delete(:responder) || responder).call(self, resources, options) + end end def responder @@ -258,11 +246,29 @@ module ActionController #:nodoc: end end + # Collects mimes and return the response for the negotiated format. Returns + # nil if :not_acceptable was sent to the client. + # + def retrieve_response_from_mimes(mimes, &block) + collector = Collector.new { default_render } + mimes = collect_mimes_from_class_level if mimes.empty? + mimes.each { |mime| collector.send(mime) } + block.call(collector) if block_given? + + if format = request.negotiate_mime(collector.order) + self.formats = [format.to_sym] + collector.response_for(format) + else + head :not_acceptable + nil + end + end + class Collector #:nodoc: attr_accessor :order - def initialize - @order, @responses = [], {} + def initialize(&block) + @order, @responses, @default_response = [], {}, block end def any(*args, &block) @@ -276,13 +282,12 @@ module ActionController #:nodoc: def custom(mime_type, &block) mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s) - @order << mime_type @responses[mime_type] ||= block end def response_for(mime) - @responses[mime] || @responses[Mime::ALL] + @responses[mime] || @responses[Mime::ALL] || @default_response end def self.generate_method_for_mime(mime) diff --git a/actionpack/lib/action_controller/metal/rescuable.rb b/actionpack/lib/action_controller/metal/rescuable.rb index 029e643d93..bbca1b2179 100644 --- a/actionpack/lib/action_controller/metal/rescuable.rb +++ b/actionpack/lib/action_controller/metal/rescuable.rb @@ -1,52 +1,13 @@ module ActionController #:nodoc: - # Actions that fail to perform as expected throw exceptions. These - # exceptions can either be rescued for the public view (with a nice - # user-friendly explanation) or for the developers view (with tons of - # debugging information). The developers view is already implemented by - # the Action Controller, but the public view should be tailored to your - # specific application. - # - # The default behavior for public exceptions is to render a static html - # file with the name of the error code thrown. If no such file exists, an - # empty response is sent with the correct status code. - # - # You can override what constitutes a local request by overriding the - # <tt>local_request?</tt> method in your own controller. Custom rescue - # behavior is achieved by overriding the <tt>rescue_action_in_public</tt> - # and <tt>rescue_action_locally</tt> methods. module Rescue extend ActiveSupport::Concern - - included do - include ActiveSupport::Rescuable - end - - module ClassMethods - # This can be removed once we can move action(:_rescue_action) into middlewares.rb - # Currently, it does controller.method(:rescue_action), which is hiding the implementation - # difference between the old and new base. - def rescue_action(env) - action(:_rescue_action).call(env) - end - end - - attr_internal :rescued_exception + include ActiveSupport::Rescuable private - def method_for_action(action_name) - return action_name if self.rescued_exception = request.env.delete("action_dispatch.rescue.exception") - super - end - - def _rescue_action - rescue_with_handler(rescued_exception) || raise(rescued_exception) - end - - def process_action(*) + def process_action(*args) super rescue Exception => exception - self.rescued_exception = exception - _rescue_action + rescue_with_handler(exception) || raise(exception) end end end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index fc01a0924a..a16ed97131 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -79,15 +79,16 @@ module ActionController #:nodoc: # Check polymorphic_url documentation for more examples. # class Responder - attr_reader :controller, :request, :format, :resource, :resource_location, :options + attr_reader :controller, :request, :format, :resource, :resources, :options def initialize(controller, resources, options={}) @controller = controller @request = controller.request @format = controller.formats.first @resource = resources.is_a?(Array) ? resources.last : resources - @resource_location = options[:location] || resources + @resources = resources @options = options + @default_response = options.delete(:default_response) end delegate :head, :render, :redirect_to, :to => :controller @@ -109,8 +110,10 @@ module ActionController #:nodoc: # template. # def to_html + default_render + rescue ActionView::MissingTemplate if get? - render + raise elsif has_errors? render :action => default_action else @@ -118,12 +121,14 @@ module ActionController #:nodoc: end end - # All others formats try to render the resource given instead. For this - # purpose a helper called display as a shortcut to render a resource with - # the current format. + # All others formats follow the procedure below. First we try to render a + # template, if the template is not available, we verify if the resource + # responds to :to_format and display it. # def to_format - return render unless resourceful? + default_render + rescue ActionView::MissingTemplate + raise unless resourceful? if get? display resource @@ -144,6 +149,20 @@ module ActionController #:nodoc: resource.respond_to?(:"to_#{format}") end + # Returns the resource location by retrieving it from the options or + # returning the resources array. + # + def resource_location + options[:location] || resources + end + + # If a given response block was given, use it, otherwise call render on + # controller. + # + def default_render + @default_response.call + end + # display is just a shortcut to render a resource with the current format. # # display @user, :status => :ok @@ -162,7 +181,7 @@ module ActionController #:nodoc: # render :xml => @user, :status => :created # def display(resource, given_options={}) - render given_options.merge!(options).merge!(format => resource) + controller.render given_options.merge!(options).merge!(format => resource) end # Check if the resource has errors or not. diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 4185b803c5..bbc7f3c8f9 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -1,78 +1,7 @@ -require 'action_dispatch' -require 'rack/session/abstract/id' require 'active_support/core_ext/object/conversions' +require "rack/test" module ActionController #:nodoc: - class TestRequest < ActionDispatch::TestRequest #:nodoc: - def initialize(env = {}) - super - - self.session = TestSession.new - self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16)) - end - - def assign_parameters(controller_path, action, parameters) - parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) - extra_keys = ActionController::Routing::Routes.extra_keys(parameters) - non_path_parameters = get? ? query_parameters : request_parameters - parameters.each do |key, value| - if value.is_a? Fixnum - value = value.to_s - elsif value.is_a? Array - value = ActionController::Routing::PathSegment::Result.new(value) - end - - if extra_keys.include?(key.to_sym) - non_path_parameters[key] = value - else - path_parameters[key.to_s] = value - end - end - - params = self.request_parameters.dup - - %w(controller action only_path).each do |k| - params.delete(k) - params.delete(k.to_sym) - end - - data = params.to_query - @env['CONTENT_LENGTH'] = data.length.to_s - @env['rack.input'] = StringIO.new(data) - end - - def recycle! - @formats = nil - @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } - @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } - @env['action_dispatch.request.query_parameters'] = {} - end - end - - class TestResponse < ActionDispatch::TestResponse - def recycle! - @status = 200 - @header = {} - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - @body = [] - @charset = nil - @content_type = nil - - @request = @template = nil - end - end - - class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc: - DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS - - def initialize(session = {}) - replace(session.stringify_keys) - @loaded = true - end - end - # 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 @@ -84,78 +13,9 @@ module ActionController #:nodoc: # # 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::Utils::Multipart::UploadedFile + TestUploadedFile = Rack::Test::UploadedFile module TestProcess - def self.included(base) - # Executes a request simulating GET HTTP method and set/volley the response - def get(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "GET") - end - - # Executes a request simulating POST HTTP method and set/volley the response - def post(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "POST") - end - - # Executes a request simulating PUT HTTP method and set/volley the response - def put(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "PUT") - end - - # Executes a request simulating DELETE HTTP method and set/volley the response - def delete(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "DELETE") - end - - # Executes a request simulating HEAD HTTP method and set/volley the response - def head(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "HEAD") - end - end - - def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') - # Sanity check for required instance variables so we can give an - # understandable error message. - %w(@controller @request @response).each do |iv_name| - if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? - raise "#{iv_name} is nil: make sure you set it in your test's setup method." - end - end - - @request.recycle! - @response.recycle! - @controller.response_body = nil - @controller.formats = nil - @controller.params = nil - - @html_document = nil - @request.env['REQUEST_METHOD'] = http_method - - parameters ||= {} - @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters) - - @request.session = ActionController::TestSession.new(session) unless session.nil? - @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash - - @controller.request = @request - @controller.params.merge!(parameters) - build_request_uri(action, parameters) - Base.class_eval { include Testing } - @controller.process_with_new_base_test(@request, @response) - @response - end - - def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') - returning __send__(request_method, action, parameters, session, flash) do - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' - end - end - alias xhr :xml_http_request - def assigns(key = nil) assigns = {} @controller.instance_variable_names.each do |ivar| @@ -182,16 +42,6 @@ module ActionController #:nodoc: @response.redirect_url end - def build_request_uri(action, parameters) - unless @request.env['REQUEST_URI'] - options = @controller.__send__(:rewrite_options, parameters) - options.update(:only_path => true, :action => action) - - url = ActionController::UrlRewriter.new(@request, parameters) - @request.request_uri = url.rewrite(options) - end - end - def html_document xml = @response.content_type =~ /xml$/ @html_document ||= HTML::Document.new(@response.body, false, xml) @@ -249,7 +99,6 @@ module ActionController #:nodoc: temporary_routes = ActionController::Routing::RouteSet.new ActionController::Routing.module_eval { const_set :Routes, temporary_routes } - ActionController::Dispatcher.router = temporary_routes yield temporary_routes ensure @@ -257,7 +106,6 @@ module ActionController #:nodoc: ActionController::Routing.module_eval { remove_const :Routes } end ActionController::Routing.const_set(:Routes, real_routes) if real_routes - ActionController::Dispatcher.router = ActionController::Routing::Routes end end end diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb index b66a4c15ff..178e3477a6 100644 --- a/actionpack/lib/action_controller/testing/test_case.rb +++ b/actionpack/lib/action_controller/testing/test_case.rb @@ -1,7 +1,77 @@ require 'active_support/test_case' -require 'action_controller/testing/process' +require 'rack/session/abstract/id' module ActionController + class TestRequest < ActionDispatch::TestRequest #:nodoc: + def initialize(env = {}) + super + + self.session = TestSession.new + self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16)) + end + + def assign_parameters(controller_path, action, parameters = {}) + parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) + extra_keys = ActionController::Routing::Routes.extra_keys(parameters) + non_path_parameters = get? ? query_parameters : request_parameters + parameters.each do |key, value| + if value.is_a? Fixnum + value = value.to_s + elsif value.is_a? Array + value = ActionController::Routing::PathSegment::Result.new(value) + end + + if extra_keys.include?(key.to_sym) + non_path_parameters[key] = value + else + path_parameters[key.to_s] = value + end + end + + params = self.request_parameters.dup + + %w(controller action only_path).each do |k| + params.delete(k) + params.delete(k.to_sym) + end + + data = params.to_query + @env['CONTENT_LENGTH'] = data.length.to_s + @env['rack.input'] = StringIO.new(data) + end + + def recycle! + @formats = nil + @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } + @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } + @env['action_dispatch.request.query_parameters'] = {} + end + end + + class TestResponse < ActionDispatch::TestResponse + def recycle! + @status = 200 + @header = {} + @writer = lambda { |x| @body << x } + @block = nil + @length = 0 + @body = [] + @charset = nil + @content_type = nil + + @request = @template = nil + end + end + + class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc: + DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS + + def initialize(session = {}) + replace(session.stringify_keys) + @loaded = true + end + end + # Superclass for ActionController functional tests. Functional tests allow you to # test a single controller action per test method. This should not be confused with # integration tests (see ActionController::IntegrationTest), which are more like @@ -105,6 +175,73 @@ module ActionController class TestCase < ActiveSupport::TestCase include TestProcess + # Executes a request simulating GET HTTP method and set/volley the response + def get(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "GET") + end + + # Executes a request simulating POST HTTP method and set/volley the response + def post(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "POST") + end + + # Executes a request simulating PUT HTTP method and set/volley the response + def put(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "PUT") + end + + # Executes a request simulating DELETE HTTP method and set/volley the response + def delete(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "DELETE") + end + + # Executes a request simulating HEAD HTTP method and set/volley the response + def head(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "HEAD") + end + + def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) + @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + returning __send__(request_method, action, parameters, session, flash) do + @request.env.delete 'HTTP_X_REQUESTED_WITH' + @request.env.delete 'HTTP_ACCEPT' + end + end + alias xhr :xml_http_request + + def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') + # Sanity check for required instance variables so we can give an + # understandable error message. + %w(@controller @request @response).each do |iv_name| + if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? + raise "#{iv_name} is nil: make sure you set it in your test's setup method." + end + end + + @request.recycle! + @response.recycle! + @controller.response_body = nil + @controller.formats = nil + @controller.params = nil + + @html_document = nil + @request.env['REQUEST_METHOD'] = http_method + + parameters ||= {} + @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) + + @request.session = ActionController::TestSession.new(session) unless session.nil? + @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash + + @controller.request = @request + @controller.params.merge!(parameters) + build_request_uri(action, parameters) + Base.class_eval { include Testing } + @controller.process_with_new_base_test(@request, @response) + @response + end + include ActionDispatch::Assertions # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline @@ -186,5 +323,16 @@ module ActionController def rescue_action_in_public! @request.remote_addr = '208.77.188.166' # example.com end + + private + def build_request_uri(action, parameters) + unless @request.env['REQUEST_URI'] + options = @controller.__send__(:rewrite_options, parameters) + options.update(:only_path => true, :action => action) + + url = ActionController::UrlRewriter.new(@request, parameters) + @request.request_uri = url.rewrite(options) + end + end end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 884828a01a..38aaa6146e 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -21,36 +21,34 @@ # 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' - -begin - gem 'rack', '~> 1.1.pre' -rescue Gem::LoadError, ArgumentError - $:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-1.1.pre" -end - require 'rack' -$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-test" +module Rack + autoload :Test, 'rack/test' +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' 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 :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Static, 'action_dispatch/middleware/static' - autoload :HTML, 'action_controller/vendor/html-scanner' 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 :HTML, 'action_controller/vendor/html-scanner' + module Http autoload :Headers, 'action_dispatch/http/headers' end @@ -63,3 +61,7 @@ module ActionDispatch 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/response.rb b/actionpack/lib/action_dispatch/http/response.rb index e457450059..3e3b473178 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/module/delegation' module ActionDispatch # :nodoc: # Represents an HTTP response generated by a controller action. One can use - # an ActionController::Response object to retrieve the current state + # an ActionDispatch::Response object to retrieve the current state # of the response, or customize the response. An Response object can # either represent a "real" HTTP response (i.e. one that is meant to be sent # back to the web browser) or a test response (i.e. one that is generated @@ -18,14 +18,14 @@ module ActionDispatch # :nodoc: # Nevertheless, integration tests may want to inspect controller responses in # more detail, and that's when Response can be useful for application # developers. Integration test methods such as - # ActionController::Integration::Session#get and - # ActionController::Integration::Session#post return objects of type + # ActionDispatch::Integration::Session#get and + # ActionDispatch::Integration::Session#post return objects of type # TestResponse (which are of course also of type Response). # # For example, the following demo integration "test" prints the body of the # controller response to the console: # - # class DemoControllerTest < ActionController::IntegrationTest + # class DemoControllerTest < ActionDispatch::IntegrationTest # def test_print_root_path_to_console # get('/') # puts @response.body diff --git a/actionpack/lib/action_dispatch/http/utils.rb b/actionpack/lib/action_dispatch/http/utils.rb new file mode 100644 index 0000000000..e04a39935e --- /dev/null +++ b/actionpack/lib/action_dispatch/http/utils.rb @@ -0,0 +1,20 @@ +module ActionDispatch + module Utils + # TODO: Pull this into rack core + # http://github.com/halorgium/rack/commit/feaf071c1de743fbd10bc316830180a9af607278 + def parse_config(config) + 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 + end + module_function :parse_config + end +end diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index 0a2b4cf5f7..56d6da1706 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -1,40 +1,50 @@ module ActionDispatch class Callbacks - include ActiveSupport::Callbacks - define_callbacks :prepare, :before, :after + include ActiveSupport::NewCallbacks + + define_callbacks :call, :terminator => "result == false", :rescuable => true + define_callbacks :prepare, :scope => :name + + # Add a preparation callback. Preparation callbacks are run before every + # request in development mode, and before the first request in production mode. + # + # If a symbol with a block is given, the symbol is used as an identifier. + # That allows to_prepare to be called again with the same identifier to + # replace the existing callback. Passing an identifier is a suggested + # practice if the code adding a preparation block may be reloaded. + def self.to_prepare(*args, &block) + if args.first.is_a?(Symbol) && block_given? + define_method :"__#{args.first}", &block + set_callback(:prepare, :"__#{args.first}") + else + set_callback(:prepare, *args, &block) + end + end + + def self.before(*args, &block) + set_callback(:call, :before, *args, &block) + end + + def self.after(*args, &block) + set_callback(:call, :after, *args, &block) + end class << self # DEPRECATED - alias_method :prepare_dispatch, :prepare alias_method :before_dispatch, :before alias_method :after_dispatch, :after end - # Add a preparation callback. Preparation callbacks are run before every - # request in development mode, and before the first request in production - # mode. - # - # An optional identifier may be supplied for the callback. If provided, - # to_prepare may be called again with the same identifier to replace the - # existing callback. Passing an identifier is a suggested practice if the - # code adding a preparation block may be reloaded. - def self.to_prepare(identifier = nil, &block) - @prepare_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new - callback = ActiveSupport::Callbacks::Callback.new(:prepare, block, :identifier => identifier) - @prepare_callbacks.replace_or_append!(callback) - end - def initialize(app, prepare_each_request = false) @app, @prepare_each_request = app, prepare_each_request - run_callbacks :prepare + _run_prepare_callbacks end def call(env) - run_callbacks :before - run_callbacks :prepare if @prepare_each_request - @app.call(env) - ensure - run_callbacks :after, :enumerator => :reverse_each + _run_call_callbacks do + _run_prepare_callbacks if @prepare_each_request + @app.call(env) + end end end end diff --git a/actionpack/lib/action_dispatch/middleware/rescue.rb b/actionpack/lib/action_dispatch/middleware/rescue.rb index 1456825526..aee672112c 100644 --- a/actionpack/lib/action_dispatch/middleware/rescue.rb +++ b/actionpack/lib/action_dispatch/middleware/rescue.rb @@ -1,14 +1,26 @@ module ActionDispatch class Rescue - def initialize(app, rescuer) - @app, @rescuer = app, rescuer + def initialize(app, rescuers = {}, &block) + @app, @rescuers = app, {} + rescuers.each { |exception, rescuer| rescue_from(exception, rescuer) } + instance_eval(&block) if block_given? end def call(env) @app.call(env) rescue Exception => exception - env['action_dispatch.rescue.exception'] = exception - @rescuer.call(env) + if rescuer = @rescuers[exception.class.name] + env['action_dispatch.rescue.exception'] = exception + rescuer.call(env) + else + raise exception + end end + + protected + def rescue_from(exception, rescuer) + exception = exception.class.name if exception.is_a?(Exception) + @rescuers[exception.to_s] = rescuer + end end end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 03761b10bd..c5c06f74a2 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -2,6 +2,9 @@ require 'rack/utils' module ActionDispatch module Session + class SessionRestoreError < StandardError #:nodoc: + end + class AbstractStore ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze @@ -19,7 +22,7 @@ module ActionDispatch def session_id ActiveSupport::Deprecation.warn( - "ActionController::Session::AbstractStore::SessionHash#session_id " + + "ActionDispatch::Session::AbstractStore::SessionHash#session_id " + "has been deprecated. Please use request.session_options[:id] instead.", caller) @env[ENV_SESSION_OPTIONS_KEY][:id] end @@ -45,6 +48,7 @@ module ActionDispatch ActiveSupport::Deprecation.warn('use replace instead', caller) replace({}) else + load! unless @loaded super(hash.stringify_keys) end end @@ -54,13 +58,14 @@ module ActionDispatch ActiveSupport::Deprecation.warn('use clear instead', caller) clear else + load! unless @loaded super(key.to_s) end end def data ActiveSupport::Deprecation.warn( - "ActionController::Session::AbstractStore::SessionHash#data " + + "ActionDispatch::Session::AbstractStore::SessionHash#data " + "has been deprecated. Please use #to_hash instead.", caller) to_hash end @@ -96,7 +101,7 @@ module ActionDispatch # Note that the regexp does not allow $1 to end with a ':' $1.constantize rescue LoadError, NameError => const_error - raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" + raise ActionDispatch::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n" end retry diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 9cfd6956d0..bd552b458a 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -49,7 +49,18 @@ module ActionDispatch :expire_after => nil, :httponly => true }.freeze + + class OptionsHash < Hash + def initialize(by, env, default_options) + @session_data = env[CookieStore::ENV_SESSION_KEY] + default_options.each { |key, value| self[key] = value } + end + def [](key) + key == :id ? @session_data[:session_id] : super(key) + end + end + ENV_SESSION_KEY = "rack.session".freeze ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze HTTP_SET_COOKIE = "Set-Cookie".freeze @@ -90,8 +101,8 @@ module ActionDispatch def call(env) env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup - + env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) + status, headers, body = @app.call(env) session_data = env[ENV_SESSION_KEY] diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index 1d9efc2b36..be1d5a43a2 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -1,52 +1,47 @@ -require "active_support/core_ext/kernel/requires" -begin - require_library_or_gem 'memcache' +module ActionDispatch + module Session + class MemCacheStore < AbstractStore + def initialize(app, options = {}) + require 'memcache' - module ActionDispatch - module Session - class MemCacheStore < AbstractStore - def initialize(app, options = {}) - # Support old :expires option - options[:expire_after] ||= options[:expires] + # Support old :expires option + options[:expire_after] ||= options[:expires] - super + super - @default_options = { - :namespace => 'rack:session', - :memcache_server => 'localhost:11211' - }.merge(@default_options) + @default_options = { + :namespace => 'rack:session', + :memcache_server => 'localhost:11211' + }.merge(@default_options) - @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options) - unless @pool.servers.any? { |s| s.alive? } - raise "#{self} unable to find server during initialization." - end - @mutex = Mutex.new - - super + @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options) + unless @pool.servers.any? { |s| s.alive? } + raise "#{self} unable to find server during initialization." end + @mutex = Mutex.new - private - def get_session(env, sid) - sid ||= generate_sid - begin - session = @pool.get(sid) || {} - rescue MemCache::MemCacheError, Errno::ECONNREFUSED - session = {} - end - [sid, session] - end + super + end - def set_session(env, sid, session_data) - options = env['rack.session.options'] - expiry = options[:expire_after] || 0 - @pool.set(sid, session_data, expiry) - return true + private + def get_session(env, sid) + sid ||= generate_sid + begin + session = @pool.get(sid) || {} rescue MemCache::MemCacheError, Errno::ECONNREFUSED - return false + session = {} end - end + [sid, session] + end + + def set_session(env, sid, session_data) + options = env['rack.session.options'] + expiry = options[:expire_after] || 0 + @pool.set(sid, session_data, expiry) + return true + rescue MemCache::MemCacheError, Errno::ECONNREFUSED + return false + end end end -rescue LoadError - # MemCache wasn't available so neither can the store be end diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb new file mode 100644 index 0000000000..d7e88a54e4 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -0,0 +1,44 @@ +require 'rack/utils' + +module ActionDispatch + class Static + FILE_METHODS = %w(GET HEAD).freeze + + def initialize(app, root) + @app = app + @file_server = ::Rack::File.new(root) + end + + def call(env) + path = env['PATH_INFO'].chomp('/') + method = env['REQUEST_METHOD'] + + if FILE_METHODS.include?(method) + if file_exist?(path) + return @file_server.call(env) + else + cached_path = directory_exist?(path) ? "#{path}/index" : path + cached_path += ::ActionController::Base.page_cache_extension + + if file_exist?(cached_path) + env['PATH_INFO'] = cached_path + return @file_server.call(env) + end + end + end + + @app.call(env) + end + + private + def file_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.file?(full_path) && File.readable?(full_path) + end + + def directory_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.directory?(full_path) && File.readable?(full_path) + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb index bb2d8375bd..f8f6b424ca 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb @@ -15,12 +15,12 @@ show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';" hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"} %> - <a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %> + <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %> <% end %> <% traces.each do |name, trace| %> <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;"> - <pre><code><%= trace.join "\n" %></code></pre> + <pre><code><%=h trace.join "\n" %></code></pre> </div> <% end %> </div> diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 5cb0f48f82..2c4a3a356d 100644 --- a/actionpack/lib/action_controller/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -3,10 +3,10 @@ require 'uri' require 'active_support/test_case' require 'active_support/core_ext/object/metaclass' -require 'rack/mock_session' -require 'rack/test/cookie_jar' +# TODO: Remove circular dependency on ActionController +require 'action_controller/testing/process' -module ActionController +module ActionDispatch module Integration #:nodoc: module RequestHelpers # Performs a GET request with the given parameters. @@ -23,7 +23,7 @@ module ActionController # # This method returns an Response object, which one can use to # inspect the details of the response. Furthermore, if this method was - # called from an ActionController::IntegrationTest object, then that + # called from an ActionDispatch::IntegrationTest object, then that # object's <tt>@response</tt> instance variable will point to the same # response object. # @@ -168,8 +168,8 @@ module ActionController attr_accessor :request_count # Create and initialize a new Session instance. - def initialize(app = nil) - @app = app || ActionController::Dispatcher.new + def initialize(app) + @app = app reset! end @@ -193,11 +193,11 @@ module ActionController unless defined? @named_routes_configured # install the named routes in this session instance. klass = metaclass - Routing::Routes.install_helpers(klass) + ActionController::Routing::Routes.install_helpers(klass) # the helpers are made protected by default--we make them public for # easier access during testing and troubleshooting. - klass.module_eval { public *Routing::Routes.named_routes.helpers } + klass.module_eval { public *ActionController::Routing::Routes.named_routes.helpers } @named_routes_configured = true end end @@ -251,7 +251,7 @@ module ActionController end end - opts = { + env = { :method => method, :params => parameters, @@ -261,24 +261,24 @@ module ActionController "rack.url_scheme" => https? ? "https" : "http", "REQUEST_URI" => path, - "PATH_INFO" => path, "HTTP_HOST" => host, "REMOTE_ADDR" => remote_addr, "CONTENT_TYPE" => "application/x-www-form-urlencoded", "HTTP_ACCEPT" => accept } - env = Rack::MockRequest.env_for(path, opts) (rack_environment || {}).each do |key, value| env[key] = value end + session = Rack::Test::Session.new(@mock_session) + @controller = ActionController::Base.capture_instantiation do - @mock_session.request(URI.parse(path), env) + session.request(path, env) end @request_count += 1 - @request = ActionDispatch::Request.new(env) + @request = ActionDispatch::Request.new(session.last_request.env) @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response) @html_document = nil @@ -295,7 +295,7 @@ module ActionController "SERVER_PORT" => https? ? "443" : "80", "HTTPS" => https? ? "on" : "off" } - UrlRewriter.new(ActionDispatch::Request.new(env), {}) + ActionController::UrlRewriter.new(ActionDispatch::Request.new(env), {}) end end @@ -325,6 +325,10 @@ module ActionController end module Runner + def app + @app + end + # Reset the current session. This is useful for testing multiple sessions # in a single test case. def reset! @@ -354,7 +358,7 @@ module ActionController # can use this method to open multiple sessions that ought to be tested # simultaneously. def open_session(app = nil) - session = Integration::Session.new(app) + session = Integration::Session.new(app || self.app) # delegate the fixture accessors back to the test instance extras = Module.new { attr_accessor :delegate, :test_result } @@ -476,5 +480,21 @@ module ActionController # end class IntegrationTest < ActiveSupport::TestCase include Integration::Runner + + @@app = nil + + def self.app + # DEPRECATE Rails application fallback + # This should be set by the initializer + @@app || (defined?(Rails.application) && Rails.application.new) || nil + end + + def self.app=(app) + @@app = app + end + + def app + super || self.class.app + end end end diff --git a/actionpack/lib/action_controller/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index d88180087d..b1ed9d31f4 100644 --- a/actionpack/lib/action_controller/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,14 +1,14 @@ require 'active_support/testing/performance' require 'active_support/testing/default' -module ActionController +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 < ActionController::IntegrationTest + class PerformanceTest < ActionDispatch::IntegrationTest include ActiveSupport::Testing::Performance include ActiveSupport::Testing::Default end diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index c35982e075..6d019023ce 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -1,6 +1,6 @@ module ActionDispatch - # Integration test methods such as ActionController::Integration::Session#get - # and ActionController::Integration::Session#post return objects of class + # Integration test methods such as ActionDispatch::Integration::Session#get + # and ActionDispatch::Integration::Session#post return objects of class # TestResponse, which represent the HTTP response results of the requested # controller actions. # diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb deleted file mode 100644 index 371d015690..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack.rb +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2007, 2008, 2009 Christian Neukirchen <purl.org/net/chneukirchen> -# -# Rack is freely distributable under the terms of an MIT-style license. -# See COPYING or http://www.opensource.org/licenses/mit-license.php. - -path = File.expand_path(File.dirname(__FILE__)) -$:.unshift(path) unless $:.include?(path) - - -# The Rack main module, serving as a namespace for all core Rack -# modules and classes. -# -# All modules meant for use in your application are <tt>autoload</tt>ed here, -# so it should be enough just to <tt>require rack.rb</tt> in your code. - -module Rack - # The Rack protocol version number implemented. - VERSION = [1,0] - - # Return the Rack protocol version as a dotted string. - def self.version - VERSION.join(".") - end - - # Return the Rack release as a dotted string. - def self.release - "1.0" - end - - autoload :Builder, "rack/builder" - autoload :Cascade, "rack/cascade" - autoload :Chunked, "rack/chunked" - autoload :CommonLogger, "rack/commonlogger" - autoload :ConditionalGet, "rack/conditionalget" - autoload :ContentLength, "rack/content_length" - autoload :ContentType, "rack/content_type" - autoload :File, "rack/file" - autoload :Deflater, "rack/deflater" - autoload :Directory, "rack/directory" - autoload :ForwardRequest, "rack/recursive" - autoload :Handler, "rack/handler" - autoload :Head, "rack/head" - autoload :Lint, "rack/lint" - autoload :Lock, "rack/lock" - autoload :MethodOverride, "rack/methodoverride" - autoload :Mime, "rack/mime" - autoload :Recursive, "rack/recursive" - autoload :Reloader, "rack/reloader" - autoload :ShowExceptions, "rack/showexceptions" - autoload :ShowStatus, "rack/showstatus" - autoload :Static, "rack/static" - autoload :URLMap, "rack/urlmap" - autoload :Utils, "rack/utils" - - autoload :MockRequest, "rack/mock" - autoload :MockResponse, "rack/mock" - - autoload :Request, "rack/request" - autoload :Response, "rack/response" - - module Auth - autoload :Basic, "rack/auth/basic" - autoload :AbstractRequest, "rack/auth/abstract/request" - autoload :AbstractHandler, "rack/auth/abstract/handler" - autoload :OpenID, "rack/auth/openid" - module Digest - autoload :MD5, "rack/auth/digest/md5" - autoload :Nonce, "rack/auth/digest/nonce" - autoload :Params, "rack/auth/digest/params" - autoload :Request, "rack/auth/digest/request" - end - end - - module Session - autoload :Cookie, "rack/session/cookie" - autoload :Pool, "rack/session/pool" - autoload :Memcache, "rack/session/memcache" - end - - # *Adapters* connect Rack with third party web frameworks. - # - # Rack includes an adapter for Camping, see README for other - # frameworks supporting Rack in their code bases. - # - # Refer to the submodules for framework-specific calling details. - - module Adapter - autoload :Camping, "rack/adapter/camping" - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb deleted file mode 100644 index 63bc787f54..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/adapter/camping.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Rack - module Adapter - class Camping - def initialize(app) - @app = app - end - - def call(env) - env["PATH_INFO"] ||= "" - env["SCRIPT_NAME"] ||= "" - controller = @app.run(env['rack.input'], env) - h = controller.headers - h.each_pair do |k,v| - if v.kind_of? URI - h[k] = v.to_s - end - end - [controller.status, controller.headers, [controller.body.to_s]] - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb deleted file mode 100644 index 214df6299e..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - # Rack::Auth::AbstractHandler implements common authentication functionality. - # - # +realm+ should be set for all handlers. - - class AbstractHandler - - attr_accessor :realm - - def initialize(app, realm=nil, &authenticator) - @app, @realm, @authenticator = app, realm, authenticator - end - - - private - - def unauthorized(www_authenticate = challenge) - return [ 401, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0', - 'WWW-Authenticate' => www_authenticate.to_s }, - [] - ] - end - - def bad_request - return [ 400, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0' }, - [] - ] - end - - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb deleted file mode 100644 index 1d9ccec685..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/abstract/request.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - class AbstractRequest - - def initialize(env) - @env = env - end - - def provided? - !authorization_key.nil? - end - - def parts - @parts ||= @env[authorization_key].split(' ', 2) - end - - def scheme - @scheme ||= parts.first.downcase.to_sym - end - - def params - @params ||= parts.last - end - - - private - - AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] - - def authorization_key - @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb deleted file mode 100644 index 9557224648..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/basic.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/abstract/request' - -module Rack - module Auth - # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. - # - # Initialize with the Rack application that you want protecting, - # and a block that checks if a username and password pair are valid. - # - # See also: <tt>example/protectedlobster.rb</tt> - - class Basic < AbstractHandler - - def call(env) - auth = Basic::Request.new(env) - - return unauthorized unless auth.provided? - - return bad_request unless auth.basic? - - if valid?(auth) - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - - unauthorized - end - - - private - - def challenge - 'Basic realm="%s"' % realm - end - - def valid?(auth) - @authenticator.call(*auth.credentials) - end - - class Request < Auth::AbstractRequest - def basic? - :basic == scheme - end - - def credentials - @credentials ||= params.unpack("m*").first.split(/:/, 2) - end - - def username - credentials.first - end - end - - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb deleted file mode 100644 index e579dc9632..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/md5.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/digest/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of - # HTTP Digest Authentication, as per RFC 2617. - # - # Initialize with the [Rack] application that you want protecting, - # and a block that looks up a plaintext password for a given username. - # - # +opaque+ needs to be set to a constant base64/hexadecimal string. - # - class MD5 < AbstractHandler - - attr_accessor :opaque - - attr_writer :passwords_hashed - - def initialize(*args) - super - @passwords_hashed = nil - end - - def passwords_hashed? - !!@passwords_hashed - end - - def call(env) - auth = Request.new(env) - - unless auth.provided? - return unauthorized - end - - if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) - return bad_request - end - - if valid?(auth) - if auth.nonce.stale? - return unauthorized(challenge(:stale => true)) - else - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - end - - unauthorized - end - - - private - - QOP = 'auth'.freeze - - def params(hash = {}) - Params.new do |params| - params['realm'] = realm - params['nonce'] = Nonce.new.to_s - params['opaque'] = H(opaque) - params['qop'] = QOP - - hash.each { |k, v| params[k] = v } - end - end - - def challenge(hash = {}) - "Digest #{params(hash)}" - end - - def valid?(auth) - valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) - end - - def valid_qop?(auth) - QOP == auth.qop - end - - def valid_opaque?(auth) - H(opaque) == auth.opaque - end - - def valid_nonce?(auth) - auth.nonce.valid? - end - - def valid_digest?(auth) - digest(auth, @authenticator.call(auth.username)) == auth.response - end - - def md5(data) - ::Digest::MD5.hexdigest(data) - end - - alias :H :md5 - - def KD(secret, data) - H([secret, data] * ':') - end - - def A1(auth, password) - [ auth.username, auth.realm, password ] * ':' - end - - def A2(auth) - [ auth.method, auth.uri ] * ':' - end - - def digest(auth, password) - password_hash = passwords_hashed? ? password : H(A1(auth, password)) - - KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') - end - - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb deleted file mode 100644 index dbe109f29a..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::Nonce is the default nonce generator for the - # Rack::Auth::Digest::MD5 authentication handler. - # - # +private_key+ needs to set to a constant string. - # - # +time_limit+ can be optionally set to an integer (number of seconds), - # to limit the validity of the generated nonces. - - class Nonce - - class << self - attr_accessor :private_key, :time_limit - end - - def self.parse(string) - new(*string.unpack("m*").first.split(' ', 2)) - end - - def initialize(timestamp = Time.now, given_digest = nil) - @timestamp, @given_digest = timestamp.to_i, given_digest - end - - def to_s - [([ @timestamp, digest ] * ' ')].pack("m*").strip - end - - def digest - ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') - end - - def valid? - digest == @given_digest - end - - def stale? - !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit - end - - def fresh? - !stale? - end - - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb deleted file mode 100644 index 730e2efdc8..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/params.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - module Auth - module Digest - class Params < Hash - - def self.parse(str) - split_header_value(str).inject(new) do |header, param| - k, v = param.split('=', 2) - header[k] = dequote(v) - header - end - end - - def self.dequote(str) # From WEBrick::HTTPUtils - ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup - ret.gsub!(/\\(.)/, "\\1") - ret - end - - def self.split_header_value(str) - str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } - end - - def initialize - super - - yield self if block_given? - end - - def [](k) - super k.to_s - end - - def []=(k, v) - super k.to_s, v.to_s - end - - UNQUOTED = ['qop', 'nc', 'stale'] - - def to_s - inject([]) do |parts, (k, v)| - parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) - parts - end.join(', ') - end - - def quote(str) # From WEBrick::HTTPUtils - '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' - end - - end - end - end -end - diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb deleted file mode 100644 index a8aa3bf996..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/digest/request.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'rack/auth/abstract/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' - -module Rack - module Auth - module Digest - class Request < Auth::AbstractRequest - - def method - @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] - end - - def digest? - :digest == scheme - end - - def correct_uri? - (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri - end - - def nonce - @nonce ||= Nonce.parse(params['nonce']) - end - - def params - @params ||= Params.parse(parts.last) - end - - def method_missing(sym) - if params.has_key? key = sym.to_s - return params[key] - end - super - end - - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb deleted file mode 100644 index c5f6a5143e..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/auth/openid.rb +++ /dev/null @@ -1,480 +0,0 @@ -# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net - -gem 'ruby-openid', '~> 2' if defined? Gem -require 'rack/request' -require 'rack/utils' -require 'rack/auth/abstract/handler' -require 'uri' -require 'openid' #gem -require 'openid/extension' #gem -require 'openid/store/memory' #gem - -module Rack - class Request - def openid_request - @env['rack.auth.openid.request'] - end - - def openid_response - @env['rack.auth.openid.response'] - end - end - - module Auth - - # Rack::Auth::OpenID provides a simple method for setting up an OpenID - # Consumer. It requires the ruby-openid library from janrain to operate, - # as well as a rack method of session management. - # - # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. - # - # The OpenID specifications can be found at - # http://openid.net/specs/openid-authentication-1_1.html - # and - # http://openid.net/specs/openid-authentication-2_0.html. Documentation - # for published OpenID extensions and related topics can be found at - # http://openid.net/developers/specs/. - # - # It is recommended to read through the OpenID spec, as well as - # ruby-openid's documentation, to understand what exactly goes on. However - # a setup as simple as the presented examples is enough to provide - # Consumer functionality. - # - # This library strongly intends to utilize the OpenID 2.0 features of the - # ruby-openid library, which provides OpenID 1.0 compatiblity. - # - # NOTE: Due to the amount of data that this library stores in the - # session, Rack::Session::Cookie may fault. - - class OpenID - - class NoSession < RuntimeError; end - class BadExtension < RuntimeError; end - # Required for ruby-openid - ValidStatus = [:success, :setup_needed, :cancel, :failure] - - # = Arguments - # - # The first argument is the realm, identifying the site they are trusting - # with their identity. This is required, also treated as the trust_root - # in OpenID 1.x exchanges. - # - # The optional second argument is a hash of options. - # - # == Options - # - # <tt>:return_to</tt> defines the url to return to after the client - # authenticates with the openid service provider. This url should point - # to where Rack::Auth::OpenID is mounted. If <tt>:return_to</tt> is not - # provided, return_to will be the current url which allows flexibility - # with caveats. - # - # <tt>:session_key</tt> defines the key to the session hash in the env. - # It defaults to 'rack.session'. - # - # <tt>:openid_param</tt> defines at what key in the request parameters to - # find the identifier to resolve. As per the 2.0 spec, the default is - # 'openid_identifier'. - # - # <tt>:store</tt> defined what OpenID Store to use for persistant - # information. By default a Store::Memory will be used. - # - # <tt>:immediate</tt> as true will make initial requests to be of an - # immediate type. This is false by default. See OpenID specification - # documentation. - # - # <tt>:extensions</tt> should be a hash of openid extension - # implementations. The key should be the extension main module, the value - # should be an array of arguments for extension::Request.new. - # The hash is iterated over and passed to #add_extension for processing. - # Please see #add_extension for further documentation. - # - # == Examples - # - # simple_oid = OpenID.new('http://mysite.com/') - # - # return_oid = OpenID.new('http://mysite.com/', { - # :return_to => 'http://mysite.com/openid' - # }) - # - # complex_oid = OpenID.new('http://mysite.com/', - # :immediate => true, - # :extensions => { - # ::OpenID::SReg => [['email'],['nickname']] - # } - # ) - # - # = Advanced - # - # Most of the functionality of this library is encapsulated such that - # expansion and overriding functions isn't difficult nor tricky. - # Alternately, to avoid opening up singleton objects or subclassing, a - # wrapper rack middleware can be composed to act upon Auth::OpenID's - # responses. See #check and #finish for locations of pertinent data. - # - # == Responses - # - # To change the responses that Auth::OpenID returns, override the methods - # #redirect, #bad_request, #unauthorized, #access_denied, and - # #foreign_server_failure. - # - # Additionally #confirm_post_params is used when the URI would exceed - # length limits on a GET request when doing the initial verification - # request. - # - # == Processing - # - # To change methods of processing completed transactions, override the - # methods #success, #setup_needed, #cancel, and #failure. Please ensure - # the returned object is a rack compatible response. - # - # The first argument is an OpenID::Response, the second is a - # Rack::Request of the current request, the last is the hash used in - # ruby-openid handling, which can be found manually at - # env['rack.session'][:openid]. - # - # This is useful if you wanted to expand the processing done, such as - # setting up user accounts. - # - # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to - # def oid_app.success oid, request, session - # user = Models::User[oid.identity_url] - # user ||= Models::User.create_from_openid oid - # request['rack.session'][:user] = user.id - # redirect MyApp.site_home - # end - # - # site_map['/openid'] = oid_app - # map = Rack::URLMap.new site_map - # ... - - def initialize(realm, options={}) - realm = URI(realm) - raise ArgumentError, "Invalid realm: #{realm}" \ - unless realm.absolute? \ - and realm.fragment.nil? \ - and realm.scheme =~ /^https?$/ \ - and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ - realm.path = '/' if realm.path.empty? - @realm = realm.to_s - - if ruri = options[:return_to] - ruri = URI(ruri) - raise ArgumentError, "Invalid return_to: #{ruri}" \ - unless ruri.absolute? \ - and ruri.scheme =~ /^https?$/ \ - and ruri.fragment.nil? - raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ - unless self.within_realm?(ruri) - @return_to = ruri.to_s - end - - @session_key = options[:session_key] || 'rack.session' - @openid_param = options[:openid_param] || 'openid_identifier' - @store = options[:store] || ::OpenID::Store::Memory.new - @immediate = !!options[:immediate] - - @extensions = {} - if extensions = options.delete(:extensions) - extensions.each do |ext, args| - add_extension ext, *args - end - end - - # Undocumented, semi-experimental - @anonymous = !!options[:anonymous] - end - - attr_reader :realm, :return_to, :session_key, :openid_param, :store, - :immediate, :extensions - - # Sets up and uses session data at <tt>:openid</tt> within the session. - # Errors in this setup will raise a NoSession exception. - # - # If the parameter 'openid.mode' is set, which implies a followup from - # the openid server, processing is passed to #finish and the result is - # returned. However, if there is no appropriate openid information in the - # session, a 400 error is returned. - # - # If the parameter specified by <tt>options[:openid_param]</tt> is - # present, processing is passed to #check and the result is returned. - # - # If neither of these conditions are met, #unauthorized is called. - - def call(env) - env['rack.auth.openid'] = self - env_session = env[@session_key] - unless env_session and env_session.is_a?(Hash) - raise NoSession, 'No compatible session' - end - # let us work in our own namespace... - session = (env_session[:openid] ||= {}) - unless session and session.is_a?(Hash) - raise NoSession, 'Incompatible openid session' - end - - request = Rack::Request.new(env) - consumer = ::OpenID::Consumer.new(session, @store) - - if mode = request.GET['openid.mode'] - if session.key?(:openid_param) - finish(consumer, session, request) - else - bad_request - end - elsif request.GET[@openid_param] - check(consumer, session, request) - else - unauthorized - end - end - - # As the first part of OpenID consumer action, #check retrieves the data - # required for completion. - # - # If all parameters fit within the max length of a URI, a 303 redirect - # will be returned. Otherwise #confirm_post_params will be called. - # - # Any messages from OpenID's request are logged to env['rack.errors'] - # - # <tt>env['rack.auth.openid.request']</tt> is the openid checkid request - # instance. - # - # <tt>session[:openid_param]</tt> is set to the openid identifier - # provided by the user. - # - # <tt>session[:return_to]</tt> is set to the return_to uri given to the - # identity provider. - - def check(consumer, session, req) - oid = consumer.begin(req.GET[@openid_param], @anonymous) - req.env['rack.auth.openid.request'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - ## Extension support - extensions.each do |ext,args| - oid.add_extension(ext::Request.new(*args)) - end - - session[:openid_param] = req.GET[openid_param] - return_to_uri = return_to ? return_to : req.url - session[:return_to] = return_to_uri - immediate = session.key?(:setup_needed) ? false : immediate - - if oid.send_redirect?(realm, return_to_uri, immediate) - uri = oid.redirect_url(realm, return_to_uri, immediate) - redirect(uri) - else - confirm_post_params(oid, realm, return_to_uri, immediate) - end - rescue ::OpenID::DiscoveryFailure => e - # thrown from inside OpenID::Consumer#begin by yadis stuff - req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n") - return foreign_server_failure - end - - # This is the final portion of authentication. - # If successful, a redirect to the realm is be returned. - # Data gathered from extensions are stored in session[:openid] with the - # extension's namespace uri as the key. - # - # Any messages from OpenID's response are logged to env['rack.errors'] - # - # <tt>env['rack.auth.openid.response']</tt> will contain the openid - # response. - - def finish(consumer, session, req) - oid = consumer.complete(req.GET, req.url) - req.env['rack.auth.openid.response'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - raise unless ValidStatus.include?(oid.status) - __send__(oid.status, oid, req, session) - end - - # The first argument should be the main extension module. - # The extension module should contain the constants: - # * class Request, should have OpenID::Extension as an ancestor - # * class Response, should have OpenID::Extension as an ancestor - # * string NS_URI, which defining the namespace of the extension - # - # All trailing arguments will be passed to extension::Request.new in - # #check. - # The openid response will be passed to - # extension::Response#from_success_response, #get_extension_args will be - # called on the result to attain the gathered data. - # - # This method returns the key at which the response data will be found in - # the session, which is the namespace uri by default. - - def add_extension(ext, *args) - raise BadExtension unless valid_extension?(ext) - extensions[ext] = args - return ext::NS_URI - end - - # Checks the validitity, in the context of usage, of a submitted - # extension. - - def valid_extension?(ext) - if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } - raise ArgumentError, 'Extension is missing constants.' - elsif not ext::Response.respond_to?(:from_success_response) - raise ArgumentError, 'Response is missing required method.' - end - return true - rescue - return false - end - - # Checks the provided uri to ensure it'd be considered within the realm. - # is currently not compatible with wildcard realms. - - def within_realm? uri - uri = URI.parse(uri.to_s) - realm = URI.parse(self.realm) - return false unless uri.absolute? - return false unless uri.path[0, realm.path.size] == realm.path - return false unless uri.host == realm.host or realm.host[/^\*\./] - # for wildcard support, is awkward with URI limitations - realm_match = Regexp.escape(realm.host). - sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' - return false unless uri.host.match(realm_match) - return true - end - alias_method :include?, :within_realm? - - protected - - ### These methods define some of the boilerplate responses. - - # Returns an html form page for posting to an Identity Provider if the - # GET request would exceed the upper URI length limit. - - def confirm_post_params(oid, realm, return_to, immediate) - Rack::Response.new.finish do |r| - r.write '<html><head><title>Confirm...</title></head><body>' - r.write oid.form_markup(realm, return_to, immediate) - r.write '</body></html>' - end - end - - # Returns a 303 redirect with the destination of that provided by the - # argument. - - def redirect(uri) - [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain', - 'Location' => uri}, - [] ] - end - - # Returns an empty 400 response. - - def bad_request - [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, - [''] ] - end - - # Returns a basic unauthorized 401 response. - - def unauthorized - [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, - ['Unauthorized.'] ] - end - - # Returns a basic access denied 403 response. - - def access_denied - [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, - ['Access denied.'] ] - end - - # Returns a 503 response to be used if communication with the remote - # OpenID server fails. - - def foreign_server_failure - [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, - ['Foreign server failure.'] ] - end - - private - - ### These methods are called after a transaction is completed, depending - # on its outcome. These should all return a rack compatible response. - # You'd want to override these to provide additional functionality. - - # Called to complete processing on a successful transaction. - # Within the openid session, :openid_identity and :openid_identifier are - # set to the user friendly and the standard representation of the - # validated identity. All other data in the openid session is cleared. - - def success(oid, request, session) - session.clear - session[:openid_identity] = oid.display_identifier - session[:openid_identifier] = oid.identity_url - extensions.keys.each do |ext| - label = ext.name[/[^:]+$/].downcase - response = ext::Response.from_success_response(oid) - session[label] = response.data - end - redirect(realm) - end - - # Called if the Identity Provider indicates further setup by the user is - # required. - # The identifier is retrived from the openid session at :openid_param. - # And :setup_needed is set to true to prevent looping. - - def setup_needed(oid, request, session) - identifier = session[:openid_param] - session[:setup_needed] = true - redirect req.script_name + '?' + openid_param + '=' + identifier - end - - # Called if the user indicates they wish to cancel identification. - # Data within openid session is cleared. - - def cancel(oid, request, session) - session.clear - access_denied - end - - # Called if the Identity Provider indicates the user is unable to confirm - # their identity. Data within the openid session is left alone, in case - # of swarm auth attacks. - - def failure(oid, request, session) - unauthorized - end - end - - # A class developed out of the request to use OpenID as an authentication - # middleware. The request will be sent to the OpenID instance unless the - # block evaluates to true. For example in rackup, you can use it as such: - # - # use Rack::Session::Pool - # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| - # env['rack.session'][:authkey] == a_string - # end - # run RackApp - # - # Or simply: - # - # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth - - class OpenIDAuth < Rack::Auth::AbstractHandler - attr_reader :oid - def initialize(app, realm, options={}, &auth) - @oid = OpenID.new(realm, options) - super(app, &auth) - end - - def call(env) - to = auth.call(env) ? @app : @oid - to.call env - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb deleted file mode 100644 index 295235e56a..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/builder.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Rack - # Rack::Builder implements a small DSL to iteratively construct Rack - # applications. - # - # Example: - # - # app = Rack::Builder.new { - # use Rack::CommonLogger - # use Rack::ShowExceptions - # map "/lobster" do - # use Rack::Lint - # run Rack::Lobster.new - # end - # } - # - # Or - # - # app = Rack::Builder.app do - # use Rack::CommonLogger - # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } - # end - # - # +use+ adds a middleware to the stack, +run+ dispatches to an application. - # You can use +map+ to construct a Rack::URLMap in a convenient way. - - class Builder - def initialize(&block) - @ins = [] - instance_eval(&block) if block_given? - end - - def self.app(&block) - self.new(&block).to_app - end - - def use(middleware, *args, &block) - @ins << lambda { |app| middleware.new(app, *args, &block) } - end - - def run(app) - @ins << app #lambda { |nothing| app } - end - - def map(path, &block) - if @ins.last.kind_of? Hash - @ins.last[path] = self.class.new(&block).to_app - else - @ins << {} - map(path, &block) - end - end - - def to_app - @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last - inner_app = @ins.last - @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } - end - - def call(env) - to_app.call(env) - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb deleted file mode 100644 index a038aa1105..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/cascade.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Rack - # Rack::Cascade tries an request on several apps, and returns the - # first response that is not 404 (or in a list of configurable - # status codes). - - class Cascade - attr_reader :apps - - def initialize(apps, catch=404) - @apps = apps - @catch = [*catch] - end - - def call(env) - status = headers = body = nil - raise ArgumentError, "empty cascade" if @apps.empty? - @apps.each { |app| - begin - status, headers, body = app.call(env) - break unless @catch.include?(status.to_i) - end - } - [status, headers, body] - end - - def add app - @apps << app - end - - def include? app - @apps.include? app - end - - alias_method :<<, :add - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb deleted file mode 100644 index 280d89dd65..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/chunked.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that applies chunked transfer encoding to response bodies - # when the response does not include a Content-Length header. - class Chunked - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if env['HTTP_VERSION'] == 'HTTP/1.0' || - STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Content-Length'] || - headers['Transfer-Encoding'] - [status, headers.to_hash, body] - else - dup.chunk(status, headers, body) - end - end - - def chunk(status, headers, body) - @body = body - headers.delete('Content-Length') - headers['Transfer-Encoding'] = 'chunked' - [status, headers.to_hash, self] - end - - def each - term = "\r\n" - @body.each do |chunk| - size = bytesize(chunk) - next if size == 0 - yield [size.to_s(16), term, chunk, term].join - end - yield ["0", term, "", term].join - end - - def close - @body.close if @body.respond_to?(:close) - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb deleted file mode 100644 index 5e68ac626d..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/commonlogger.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Rack - # Rack::CommonLogger forwards every request to an +app+ given, and - # logs a line in the Apache common log format to the +logger+, or - # rack.errors by default. - - class CommonLogger - def initialize(app, logger=nil) - @app = app - @logger = logger - end - - def call(env) - dup._call(env) - end - - def _call(env) - @env = env - @logger ||= self - @time = Time.now - @status, @header, @body = @app.call(env) - [@status, @header, self] - end - - def close - @body.close if @body.respond_to? :close - end - - # By default, log to rack.errors. - def <<(str) - @env["rack.errors"].write(str) - @env["rack.errors"].flush - end - - def each - length = 0 - @body.each { |part| - length += part.size - yield part - } - - @now = Time.now - - # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common - # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - - # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % - @logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} % - [ - @env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-", - @env["REMOTE_USER"] || "-", - @now.strftime("%d/%b/%Y %H:%M:%S"), - @env["REQUEST_METHOD"], - @env["PATH_INFO"], - @env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"], - @env["HTTP_VERSION"], - @status.to_s[0..3], - (length.zero? ? "-" : length.to_s), - @now - @time - ] - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb deleted file mode 100644 index 046ebdb00a..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/conditionalget.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that enables conditional GET using If-None-Match and - # If-Modified-Since. The application should set either or both of the - # Last-Modified or Etag response headers according to RFC 2616. When - # either of the conditions is met, the response body is set to be zero - # length and the response status is set to 304 Not Modified. - # - # Applications that defer response body generation until the body's each - # message is received will avoid response body generation completely when - # a conditional GET matches. - # - # Adapted from Michael Klishin's Merb implementation: - # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb - class ConditionalGet - def initialize(app) - @app = app - end - - def call(env) - return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) - - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - if etag_matches?(env, headers) || modified_since?(env, headers) - status = 304 - headers.delete('Content-Type') - headers.delete('Content-Length') - body = [] - end - [status, headers, body] - end - - private - def etag_matches?(env, headers) - etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] - end - - def modified_since?(env, headers) - last_modified = headers['Last-Modified'] and - last_modified == env['HTTP_IF_MODIFIED_SINCE'] - end - end - -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb deleted file mode 100644 index 1e56d43853..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_length.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'rack/utils' - -module Rack - # Sets the Content-Length header on responses with fixed-length bodies. - class ContentLength - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && - !headers['Content-Length'] && - !headers['Transfer-Encoding'] && - (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) - - body = [body] if body.respond_to?(:to_str) # rack 0.4 compat - length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } - headers['Content-Length'] = length.to_s - end - - [status, headers, body] - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb deleted file mode 100644 index 0c1e1ca3e1..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/content_type.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'rack/utils' - -module Rack - - # Sets the Content-Type header on responses which don't have one. - # - # Builder Usage: - # use Rack::ContentType, "text/plain" - # - # When no content type argument is provided, "text/html" is assumed. - class ContentType - def initialize(app, content_type = "text/html") - @app, @content_type = app, content_type - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - headers['Content-Type'] ||= @content_type - [status, headers.to_hash, body] - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb deleted file mode 100644 index 14137a944d..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/deflater.rb +++ /dev/null @@ -1,96 +0,0 @@ -require "zlib" -require "stringio" -require "time" # for Time.httpdate -require 'rack/utils' - -module Rack - class Deflater - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - - # Skip compressing empty entity body responses and responses with - # no-transform set. - if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Cache-Control'].to_s =~ /\bno-transform\b/ - return [status, headers, body] - end - - request = Request.new(env) - - encoding = Utils.select_best_encoding(%w(gzip deflate identity), - request.accept_encoding) - - # Set the Vary HTTP header. - vary = headers["Vary"].to_s.split(",").map { |v| v.strip } - unless vary.include?("*") || vary.include?("Accept-Encoding") - headers["Vary"] = vary.push("Accept-Encoding").join(",") - end - - case encoding - when "gzip" - headers['Content-Encoding'] = "gzip" - headers.delete('Content-Length') - mtime = headers.key?("Last-Modified") ? - Time.httpdate(headers["Last-Modified"]) : Time.now - [status, headers, GzipStream.new(body, mtime)] - when "deflate" - headers['Content-Encoding'] = "deflate" - headers.delete('Content-Length') - [status, headers, DeflateStream.new(body)] - when "identity" - [status, headers, body] - when nil - message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." - [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] - end - end - - class GzipStream - def initialize(body, mtime) - @body = body - @mtime = mtime - end - - def each(&block) - @writer = block - gzip =::Zlib::GzipWriter.new(self) - gzip.mtime = @mtime - @body.each { |part| gzip << part } - @body.close if @body.respond_to?(:close) - gzip.close - @writer = nil - end - - def write(data) - @writer.call(data) - end - end - - class DeflateStream - DEFLATE_ARGS = [ - Zlib::DEFAULT_COMPRESSION, - # drop the zlib header which causes both Safari and IE to choke - -Zlib::MAX_WBITS, - Zlib::DEF_MEM_LEVEL, - Zlib::DEFAULT_STRATEGY - ] - - def initialize(body) - @body = body - end - - def each - deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) - @body.each { |part| yield deflater.deflate(part) } - @body.close if @body.respond_to?(:close) - yield deflater.finish - nil - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb deleted file mode 100644 index acdd3029d3..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/directory.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::Directory serves entries below the +root+ given, according to the - # path info of the Rack request. If a directory is found, the file's contents - # will be presented in an html based index. If a file is found, the env will - # be passed to the specified +app+. - # - # If +app+ is not specified, a Rack::File of the same +root+ will be used. - - class Directory - DIR_FILE = "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>" - DIR_PAGE = <<-PAGE -<html><head> - <title>%s</title> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <style type='text/css'> -table { width:100%%; } -.name { text-align:left; } -.size, .mtime { text-align:right; } -.type { width:11em; } -.mtime { width:15em; } - </style> -</head><body> -<h1>%s</h1> -<hr /> -<table> - <tr> - <th class='name'>Name</th> - <th class='size'>Size</th> - <th class='type'>Type</th> - <th class='mtime'>Last Modified</th> - </tr> -%s -</table> -<hr /> -</body></html> - PAGE - - attr_reader :files - attr_accessor :root, :path - - def initialize(root, app=nil) - @root = F.expand_path(root) - @app = app || Rack::File.new(@root) - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @env = env - @script_name = env['SCRIPT_NAME'] - @path_info = Utils.unescape(env['PATH_INFO']) - - if forbidden = check_forbidden - forbidden - else - @path = F.join(@root, @path_info) - list_path - end - end - - def check_forbidden - return unless @path_info.include? ".." - - body = "Forbidden\n" - size = Rack::Utils.bytesize(body) - return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] - end - - def list_directory - @files = [['../','Parent Directory','','','']] - glob = F.join(@path, '*') - - Dir[glob].sort.each do |node| - stat = stat(node) - next unless stat - basename = F.basename(node) - ext = F.extname(node) - - url = F.join(@script_name, @path_info, basename) - size = stat.size - type = stat.directory? ? 'directory' : Mime.mime_type(ext) - size = stat.directory? ? '-' : filesize_format(size) - mtime = stat.mtime.httpdate - url << '/' if stat.directory? - basename << '/' if stat.directory? - - @files << [ url, basename, size, type, mtime ] - end - - return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] - end - - def stat(node, max = 10) - F.stat(node) - rescue Errno::ENOENT, Errno::ELOOP - return nil - end - - # TODO: add correct response if not readable, not sure if 404 is the best - # option - def list_path - @stat = F.stat(@path) - - if @stat.readable? - return @app.call(@env) if @stat.file? - return list_directory if @stat.directory? - else - raise Errno::ENOENT, 'No such file or directory' - end - - rescue Errno::ENOENT, Errno::ELOOP - return entity_not_found - end - - def entity_not_found - body = "Entity not found: #{@path_info}\n" - size = Rack::Utils.bytesize(body) - return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] - end - - def each - show_path = @path.sub(/^#{@root}/,'') - files = @files.map{|f| DIR_FILE % f }*"\n" - page = DIR_PAGE % [ show_path, show_path , files ] - page.each_line{|l| yield l } - end - - # Stolen from Ramaze - - FILESIZE_FORMAT = [ - ['%.1fT', 1 << 40], - ['%.1fG', 1 << 30], - ['%.1fM', 1 << 20], - ['%.1fK', 1 << 10], - ] - - def filesize_format(int) - FILESIZE_FORMAT.each do |format, size| - return format % (int.to_f / size) if int >= size - end - - int.to_s + 'B' - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb deleted file mode 100644 index fe62bd6b86..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/file.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::File serves files below the +root+ given, according to the - # path info of the Rack request. - # - # Handlers can detect if bodies are a Rack::File, and use mechanisms - # like sendfile on the +path+. - - class File - attr_accessor :root - attr_accessor :path - - alias :to_path :path - - def initialize(root) - @root = root - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @path_info = Utils.unescape(env["PATH_INFO"]) - return forbidden if @path_info.include? ".." - - @path = F.join(@root, @path_info) - - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def forbidden - body = "Forbidden\n" - [403, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - # NOTE: - # We check via File::size? whether this file provides size info - # via stat (e.g. /proc files often don't), otherwise we have to - # figure it out by reading the whole file into memory. And while - # we're at it we also use this as body then. - - def serving - if size = F.size?(@path) - body = self - else - body = [F.read(@path)] - size = Utils.bytesize(body.first) - end - - [200, { - "Last-Modified" => F.mtime(@path).httpdate, - "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), - "Content-Length" => size.to_s - }, body] - end - - def not_found - body = "File not found: #{@path_info}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - def each - F.open(@path, "rb") { |file| - while part = file.read(8192) - yield part - end - } - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb deleted file mode 100644 index 5624a1e79d..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Rack - # *Handlers* connect web servers with Rack. - # - # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI - # and LiteSpeed. - # - # Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>. - # A second optional hash can be passed to include server-specific - # configuration. - module Handler - def self.get(server) - return unless server - server = server.to_s - - if klass = @handlers[server] - obj = Object - klass.split("::").each { |x| obj = obj.const_get(x) } - obj - else - try_require('rack/handler', server) - const_get(server) - end - end - - # Transforms server-name constants to their canonical form as filenames, - # then tries to require them but silences the LoadError if not found - # - # Naming convention: - # - # Foo # => 'foo' - # FooBar # => 'foo_bar.rb' - # FooBAR # => 'foobar.rb' - # FOObar # => 'foobar.rb' - # FOOBAR # => 'foobar.rb' - # FooBarBaz # => 'foo_bar_baz.rb' - def self.try_require(prefix, const_name) - file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. - gsub(/[A-Z]+[^A-Z]/, '_\&').downcase - - require(::File.join(prefix, file)) - rescue LoadError - end - - def self.register(server, klass) - @handlers ||= {} - @handlers[server] = klass - end - - autoload :CGI, "rack/handler/cgi" - autoload :FastCGI, "rack/handler/fastcgi" - autoload :Mongrel, "rack/handler/mongrel" - autoload :EventedMongrel, "rack/handler/evented_mongrel" - autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" - autoload :WEBrick, "rack/handler/webrick" - autoload :LSWS, "rack/handler/lsws" - autoload :SCGI, "rack/handler/scgi" - autoload :Thin, "rack/handler/thin" - - register 'cgi', 'Rack::Handler::CGI' - register 'fastcgi', 'Rack::Handler::FastCGI' - register 'mongrel', 'Rack::Handler::Mongrel' - register 'emongrel', 'Rack::Handler::EventedMongrel' - register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' - register 'webrick', 'Rack::Handler::WEBrick' - register 'lsws', 'Rack::Handler::LSWS' - register 'scgi', 'Rack::Handler::SCGI' - register 'thin', 'Rack::Handler::Thin' - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb deleted file mode 100644 index f45f3d735a..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/cgi.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'rack/content_length' - -module Rack - module Handler - class CGI - def self.run(app, options=nil) - serve app - end - - def self.serve(app) - app = ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => $stdin, - "rack.errors" => $stderr, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => true, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - - def self.send_headers(status, headers) - STDOUT.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - STDOUT.print "#{k}: #{v}\r\n" - } - } - STDOUT.print "\r\n" - STDOUT.flush - end - - def self.send_body(body) - body.each { |part| - STDOUT.print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb deleted file mode 100644 index 0f5cbf7293..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/evented_mongrel' - -module Rack - module Handler - class EventedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb deleted file mode 100644 index 11e1fcaa74..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/fastcgi.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'fcgi' -require 'socket' -require 'rack/content_length' -require 'rack/rewindable_input' - -class FCGI::Stream - alias _rack_read_without_buffer read - - def read(n, buffer=nil) - buf = _rack_read_without_buffer n - buffer.replace(buf.to_s) if buffer - buf - end -end - -module Rack - module Handler - class FastCGI - def self.run(app, options={}) - file = options[:File] and STDIN.reopen(UNIXServer.new(file)) - port = options[:Port] and STDIN.reopen(TCPServer.new(port)) - FCGI.each { |request| - serve request, app - } - end - - def self.serve(request, app) - app = Rack::ContentLength.new(app) - - env = request.env - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - rack_input = RewindableInput.new(request.in) - - env.update({"rack.version" => [1,0], - "rack.input" => rack_input, - "rack.errors" => request.err, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" - env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" - - begin - status, headers, body = app.call(env) - begin - send_headers request.out, status, headers - send_body request.out, body - ensure - body.close if body.respond_to? :close - end - ensure - rack_input.close - request.finish - end - end - - def self.send_headers(out, status, headers) - out.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - out.print "#{k}: #{v}\r\n" - } - } - out.print "\r\n" - out.flush - end - - def self.send_body(out, body) - body.each { |part| - out.print part - out.flush - } - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb deleted file mode 100644 index 7231336d7b..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/lsws.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'lsapi' -require 'rack/content_length' - -module Rack - module Handler - class LSWS - def self.run(app, options=nil) - while LSAPI.accept != nil - serve app - end - end - def self.serve(app) - app = Rack::ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new($stdin.read.to_s), - "rack.errors" => $stderr, - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - def self.send_headers(status, headers) - print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - print "#{k}: #{v}\r\n" - } - } - print "\r\n" - STDOUT.flush - end - def self.send_body(body) - body.each { |part| - print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb deleted file mode 100644 index 3a5ef32d4b..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/mongrel.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'mongrel' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class Mongrel < ::Mongrel::HttpHandler - def self.run(app, options={}) - server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080) - # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. - # Use is similar to #run, replacing the app argument with a hash of - # { path=>app, ... } or an instance of Rack::URLMap. - if options[:map] - if app.is_a? Hash - app.each do |path, appl| - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - elsif app.is_a? URLMap - app.instance_variable_get(:@mapping).each do |(host, path, appl)| - next if !host.nil? && !options[:Host].nil? && options[:Host] != host - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - else - raise ArgumentError, "first argument should be a Hash or URLMap" - end - else - server.register('/', Rack::Handler::Mongrel.new(app)) - end - yield server if block_given? - server.run.join - end - - def initialize(app) - @app = Rack::Chunked.new(Rack::ContentLength.new(app)) - end - - def process(request, response) - env = {}.replace(request.params) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => request.body || StringIO.new(""), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, # ??? - "rack.run_once" => false, - - "rack.url_scheme" => "http", - }) - env["QUERY_STRING"] ||= "" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - - status, headers, body = @app.call(env) - - begin - response.status = status.to_i - response.send_status(nil) - - headers.each { |k, vs| - vs.split("\n").each { |v| - response.header[k] = v - } - } - response.send_header - - body.each { |part| - response.write part - response.socket.flush - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb deleted file mode 100644 index 6c4932df95..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/scgi.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'scgi' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class SCGI < ::SCGI::Processor - attr_accessor :app - - def self.run(app, options=nil) - new(options.merge(:app=>app, - :host=>options[:Host], - :port=>options[:Port], - :socket=>options[:Socket])).listen - end - - def initialize(settings = {}) - @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) - @log = Object.new - def @log.info(*args); end - def @log.error(*args); end - super(settings) - end - - def process_request(request, input_body, socket) - env = {}.replace(request) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["PATH_INFO"] = env["REQUEST_PATH"] - env["QUERY_STRING"] ||= "" - env["SCRIPT_NAME"] = "" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(input_body), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - status, headers, body = app.call(env) - begin - socket.write("Status: #{status}\r\n") - headers.each do |k, vs| - vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} - end - socket.write("\r\n") - body.each {|s| socket.write(s)} - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb deleted file mode 100644 index 4bafd0b953..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/swiftiplied_mongrel' - -module Rack - module Handler - class SwiftipliedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb deleted file mode 100644 index 3d4fedff75..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/thin.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "thin" -require "rack/content_length" -require "rack/chunked" - -module Rack - module Handler - class Thin - def self.run(app, options={}) - app = Rack::Chunked.new(Rack::ContentLength.new(app)) - server = ::Thin::Server.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080, - app) - yield server if block_given? - server.start - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb deleted file mode 100644 index 2bdc83a9ff..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/handler/webrick.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'webrick' -require 'stringio' -require 'rack/content_length' - -module Rack - module Handler - class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet - def self.run(app, options={}) - server = ::WEBrick::HTTPServer.new(options) - server.mount "/", Rack::Handler::WEBrick, app - trap(:INT) { server.shutdown } - yield server if block_given? - server.start - end - - def initialize(server, app) - super server - @app = Rack::ContentLength.new(app) - end - - def service(req, res) - env = req.meta_vars - env.delete_if { |k, v| v.nil? } - - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(req.body.to_s), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["QUERY_STRING"] ||= "" - env["REQUEST_PATH"] ||= "/" - if env["PATH_INFO"] == "" - env.delete "PATH_INFO" - else - path, n = req.request_uri.path, env["SCRIPT_NAME"].length - env["PATH_INFO"] = path[n, path.length-n] - end - - status, headers, body = @app.call(env) - begin - res.status = status.to_i - headers.each { |k, vs| - if k.downcase == "set-cookie" - res.cookies.concat vs.split("\n") - else - vs.split("\n").each { |v| - res[k] = v - } - end - } - body.each { |part| - res.body << part - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb deleted file mode 100644 index deab822a99..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/head.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Rack - -class Head - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - - if env["REQUEST_METHOD"] == "HEAD" - [status, headers, []] - else - [status, headers, body] - end - end -end - -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb deleted file mode 100644 index bf2e9787a1..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lint.rb +++ /dev/null @@ -1,537 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Lint validates your application and the requests and - # responses according to the Rack spec. - - class Lint - def initialize(app) - @app = app - end - - # :stopdoc: - - class LintError < RuntimeError; end - module Assertion - def assert(message, &block) - unless block.call - raise LintError, message - end - end - end - include Assertion - - ## This specification aims to formalize the Rack protocol. You - ## can (and should) use Rack::Lint to enforce it. - ## - ## When you develop middleware, be sure to add a Lint before and - ## after to catch all mistakes. - - ## = Rack applications - - ## A Rack application is an Ruby object (not a class) that - ## responds to +call+. - def call(env=nil) - dup._call(env) - end - - def _call(env) - ## It takes exactly one argument, the *environment* - assert("No env given") { env } - check_env env - - env['rack.input'] = InputWrapper.new(env['rack.input']) - env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) - - ## and returns an Array of exactly three values: - status, headers, @body = @app.call(env) - ## The *status*, - check_status status - ## the *headers*, - check_headers headers - ## and the *body*. - check_content_type status, headers - check_content_length status, headers, env - [status, headers, self] - end - - ## == The Environment - def check_env(env) - ## The environment must be an true instance of Hash (no - ## subclassing allowed) that includes CGI-like headers. - ## The application is free to modify the environment. - assert("env #{env.inspect} is not a Hash, but #{env.class}") { - env.instance_of? Hash - } - - ## - ## The environment is required to include these variables - ## (adopted from PEP333), except when they'd be empty, but see - ## below. - - ## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as - ## "GET" or "POST". This cannot ever - ## be an empty string, and so is - ## always required. - - ## <tt>SCRIPT_NAME</tt>:: The initial portion of the request - ## URL's "path" that corresponds to the - ## application object, so that the - ## application knows its virtual - ## "location". This may be an empty - ## string, if the application corresponds - ## to the "root" of the server. - - ## <tt>PATH_INFO</tt>:: The remainder of the request URL's - ## "path", designating the virtual - ## "location" of the request's target - ## within the application. This may be an - ## empty string, if the request URL targets - ## the application root and does not have a - ## trailing slash. This value may be - ## percent-encoded when I originating from - ## a URL. - - ## <tt>QUERY_STRING</tt>:: The portion of the request URL that - ## follows the <tt>?</tt>, if any. May be - ## empty, but is always required! - - ## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required. - - ## <tt>HTTP_</tt> Variables:: Variables corresponding to the - ## client-supplied HTTP request - ## headers (i.e., variables whose - ## names begin with <tt>HTTP_</tt>). The - ## presence or absence of these - ## variables should correspond with - ## the presence or absence of the - ## appropriate HTTP header in the - ## request. - - ## In addition to this, the Rack environment must include these - ## Rack-specific variables: - - ## <tt>rack.version</tt>:: The Array [1,0], representing this version of Rack. - ## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL. - ## <tt>rack.input</tt>:: See below, the input stream. - ## <tt>rack.errors</tt>:: See below, the error stream. - ## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. - ## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. - ## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). - ## - - ## Additional environment specifications have approved to - ## standardized middleware APIs. None of these are required to - ## be implemented by the server. - - ## <tt>rack.session</tt>:: A hash like interface for storing request session data. - ## The store must implement: - if session = env['rack.session'] - ## store(key, value) (aliased as []=); - assert("session #{session.inspect} must respond to store and []=") { - session.respond_to?(:store) && session.respond_to?(:[]=) - } - - ## fetch(key, default = nil) (aliased as []); - assert("session #{session.inspect} must respond to fetch and []") { - session.respond_to?(:fetch) && session.respond_to?(:[]) - } - - ## delete(key); - assert("session #{session.inspect} must respond to delete") { - session.respond_to?(:delete) - } - - ## clear; - assert("session #{session.inspect} must respond to clear") { - session.respond_to?(:clear) - } - end - - ## The server or the application can store their own data in the - ## environment, too. The keys must contain at least one dot, - ## and should be prefixed uniquely. The prefix <tt>rack.</tt> - ## is reserved for use with the Rack core distribution and other - ## accepted specifications and must not be used otherwise. - ## - - %w[REQUEST_METHOD SERVER_NAME SERVER_PORT - QUERY_STRING - rack.version rack.input rack.errors - rack.multithread rack.multiprocess rack.run_once].each { |header| - assert("env missing required key #{header}") { env.include? header } - } - - ## The environment must not contain the keys - ## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt> - ## (use the versions without <tt>HTTP_</tt>). - %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| - assert("env contains #{header}, must use #{header[5,-1]}") { - not env.include? header - } - } - - ## The CGI keys (named without a period) must have String values. - env.each { |key, value| - next if key.include? "." # Skip extensions - assert("env variable #{key} has non-string value #{value.inspect}") { - value.instance_of? String - } - } - - ## - ## There are the following restrictions: - - ## * <tt>rack.version</tt> must be an array of Integers. - assert("rack.version must be an Array, was #{env["rack.version"].class}") { - env["rack.version"].instance_of? Array - } - ## * <tt>rack.url_scheme</tt> must either be +http+ or +https+. - assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { - %w[http https].include? env["rack.url_scheme"] - } - - ## * There must be a valid input stream in <tt>rack.input</tt>. - check_input env["rack.input"] - ## * There must be a valid error stream in <tt>rack.errors</tt>. - check_error env["rack.errors"] - - ## * The <tt>REQUEST_METHOD</tt> must be a valid token. - assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { - env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ - } - - ## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt> - assert("SCRIPT_NAME must start with /") { - !env.include?("SCRIPT_NAME") || - env["SCRIPT_NAME"] == "" || - env["SCRIPT_NAME"] =~ /\A\// - } - ## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt> - assert("PATH_INFO must start with /") { - !env.include?("PATH_INFO") || - env["PATH_INFO"] == "" || - env["PATH_INFO"] =~ /\A\// - } - ## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only. - assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { - !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ - } - - ## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be - ## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if - ## <tt>SCRIPT_NAME</tt> is empty. - assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { - env["SCRIPT_NAME"] || env["PATH_INFO"] - } - ## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty. - assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { - env["SCRIPT_NAME"] != "/" - } - end - - ## === The Input Stream - ## - ## The input stream is an IO-like object which contains the raw HTTP - ## POST data. If it is a file then it must be opened in binary mode. - def check_input(input) - ## The input stream must respond to +gets+, +each+, +read+ and +rewind+. - [:gets, :each, :read, :rewind].each { |method| - assert("rack.input #{input} does not respond to ##{method}") { - input.respond_to? method - } - } - end - - class InputWrapper - include Assertion - - def initialize(input) - @input = input - end - - def size - @input.size - end - - ## * +gets+ must be called without arguments and return a string, - ## or +nil+ on EOF. - def gets(*args) - assert("rack.input#gets called with arguments") { args.size == 0 } - v = @input.gets - assert("rack.input#gets didn't return a String") { - v.nil? or v.instance_of? String - } - v - end - - ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>. - ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must - ## be a String and may not be nil. If +length+ is given and not nil, then this method - ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, - ## then this method reads all data until EOF. - ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" - ## if +length+ is not given or is nil. - ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a - ## newly created String object. - def read(*args) - assert("rack.input#read called with too many arguments") { - args.size <= 2 - } - if args.size >= 1 - assert("rack.input#read called with non-integer and non-nil length") { - args.first.kind_of?(Integer) || args.first.nil? - } - assert("rack.input#read called with a negative length") { - args.first.nil? || args.first >= 0 - } - end - if args.size >= 2 - assert("rack.input#read called with non-String buffer") { - args[1].kind_of?(String) - } - end - - v = @input.read(*args) - - assert("rack.input#read didn't return nil or a String") { - v.nil? or v.instance_of? String - } - if args[0].nil? - assert("rack.input#read(nil) returned nil on EOF") { - !v.nil? - } - end - - v - end - - ## * +each+ must be called without arguments and only yield Strings. - def each(*args) - assert("rack.input#each called with arguments") { args.size == 0 } - @input.each { |line| - assert("rack.input#each didn't yield a String") { - line.instance_of? String - } - yield line - } - end - - ## * +rewind+ must be called without arguments. It rewinds the input - ## stream back to the beginning. It must not raise Errno::ESPIPE: - ## that is, it may not be a pipe or a socket. Therefore, handler - ## developers must buffer the input data into some rewindable object - ## if the underlying input stream is not rewindable. - def rewind(*args) - assert("rack.input#rewind called with arguments") { args.size == 0 } - assert("rack.input#rewind raised Errno::ESPIPE") { - begin - @input.rewind - true - rescue Errno::ESPIPE - false - end - } - end - - ## * +close+ must never be called on the input stream. - def close(*args) - assert("rack.input#close must not be called") { false } - end - end - - ## === The Error Stream - def check_error(error) - ## The error stream must respond to +puts+, +write+ and +flush+. - [:puts, :write, :flush].each { |method| - assert("rack.error #{error} does not respond to ##{method}") { - error.respond_to? method - } - } - end - - class ErrorWrapper - include Assertion - - def initialize(error) - @error = error - end - - ## * +puts+ must be called with a single argument that responds to +to_s+. - def puts(str) - @error.puts str - end - - ## * +write+ must be called with a single argument that is a String. - def write(str) - assert("rack.errors#write not called with a String") { str.instance_of? String } - @error.write str - end - - ## * +flush+ must be called without arguments and must be called - ## in order to make the error appear for sure. - def flush - @error.flush - end - - ## * +close+ must never be called on the error stream. - def close(*args) - assert("rack.errors#close must not be called") { false } - end - end - - ## == The Response - - ## === The Status - def check_status(status) - ## This is an HTTP status. When parsed as integer (+to_i+), it must be - ## greater than or equal to 100. - assert("Status must be >=100 seen as integer") { status.to_i >= 100 } - end - - ## === The Headers - def check_headers(header) - ## The header must respond to +each+, and yield values of key and value. - assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { - header.respond_to? :each - } - header.each { |key, value| - ## The header keys must be Strings. - assert("header key must be a string, was #{key.class}") { - key.instance_of? String - } - ## The header must not contain a +Status+ key, - assert("header must not contain Status") { key.downcase != "status" } - ## contain keys with <tt>:</tt> or newlines in their name, - assert("header names must not contain : or \\n") { key !~ /[:\n]/ } - ## contain keys names that end in <tt>-</tt> or <tt>_</tt>, - assert("header names must not end in - or _") { key !~ /[-_]\z/ } - ## but only contain keys that consist of - ## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter. - assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } - - ## The values of the header must be Strings, - assert("a header value must be a String, but the value of " + - "'#{key}' is a #{value.class}") { value.kind_of? String } - ## consisting of lines (for multiple header values, e.g. multiple - ## <tt>Set-Cookie</tt> values) seperated by "\n". - value.split("\n").each { |item| - ## The lines must not contain characters below 037. - assert("invalid header value #{key}: #{item.inspect}") { - item !~ /[\000-\037]/ - } - } - } - end - - ## === The Content-Type - def check_content_type(status, headers) - headers.each { |key, value| - ## There must be a <tt>Content-Type</tt>, except when the - ## +Status+ is 1xx, 204 or 304, in which case there must be none - ## given. - if key.downcase == "content-type" - assert("Content-Type header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - return - end - } - assert("No Content-Type header found") { - Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - end - - ## === The Content-Length - def check_content_length(status, headers, env) - headers.each { |key, value| - if key.downcase == 'content-length' - ## There must not be a <tt>Content-Length</tt> header when the - ## +Status+ is 1xx, 204 or 304. - assert("Content-Length header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - - bytes = 0 - string_body = true - - if @body.respond_to?(:to_ary) - @body.each { |part| - unless part.kind_of?(String) - string_body = false - break - end - - bytes += Rack::Utils.bytesize(part) - } - - if env["REQUEST_METHOD"] == "HEAD" - assert("Response body was given for HEAD request, but should be empty") { - bytes == 0 - } - else - if string_body - assert("Content-Length header was #{value}, but should be #{bytes}") { - value == bytes.to_s - } - end - end - end - - return - end - } - end - - ## === The Body - def each - @closed = false - ## The Body must respond to +each+ - @body.each { |part| - ## and must only yield String values. - assert("Body yielded non-string value #{part.inspect}") { - part.instance_of? String - } - yield part - } - ## - ## The Body itself should not be an instance of String, as this will - ## break in Ruby 1.9. - ## - ## If the Body responds to +close+, it will be called after iteration. - # XXX howto: assert("Body has not been closed") { @closed } - - - ## - ## If the Body responds to +to_path+, it must return a String - ## identifying the location of a file whose contents are identical - ## to that produced by calling +each+; this may be used by the - ## server as an alternative, possibly more efficient way to - ## transport the response. - - if @body.respond_to?(:to_path) - assert("The file identified by body.to_path does not exist") { - ::File.exist? @body.to_path - } - end - - ## - ## The Body commonly is an Array of Strings, the application - ## instance itself, or a File-like object. - end - - def close - @closed = true - @body.close if @body.respond_to?(:close) - end - - # :startdoc: - - end -end - -## == Thanks -## Some parts of this specification are adopted from PEP333: Python -## Web Server Gateway Interface -## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank -## everyone involved in that effort. diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb deleted file mode 100644 index f63f419a49..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lobster.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'zlib' - -require 'rack/request' -require 'rack/response' - -module Rack - # Paste has a Pony, Rack has a Lobster! - class Lobster - LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 - P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 - t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ - I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) - - LambdaLobster = lambda { |env| - if env["QUERY_STRING"].include?("flip") - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?" - else - lobster = LobsterString - href = "?flip" - end - - content = ["<title>Lobstericious!</title>", - "<pre>", lobster, "</pre>", - "<a href='#{href}'>flip!</a>"] - length = content.inject(0) { |a,e| a+e.size }.to_s - [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] - } - - def call(env) - req = Request.new(env) - if req.GET["flip"] == "left" - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?flip=right" - elsif req.GET["flip"] == "crash" - raise "Lobster crashed" - else - lobster = LobsterString - href = "?flip=left" - end - - res = Response.new - res.write "<title>Lobstericious!</title>" - res.write "<pre>" - res.write lobster - res.write "</pre>" - res.write "<p><a href='#{href}'>flip!</a></p>" - res.write "<p><a href='?flip=crash'>crash!</a></p>" - res.finish - end - - end -end - -if $0 == __FILE__ - require 'rack' - require 'rack/showexceptions' - Rack::Handler::WEBrick.run \ - Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), - :Port => 9292 -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb deleted file mode 100644 index 93238528c4..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/lock.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Rack - class Lock - FLAG = 'rack.multithread'.freeze - - def initialize(app, lock = Mutex.new) - @app, @lock = app, lock - end - - def call(env) - old, env[FLAG] = env[FLAG], false - @lock.synchronize { @app.call(env) } - ensure - env[FLAG] = old - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb deleted file mode 100644 index 0eed29f471..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/methodoverride.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - class MethodOverride - HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) - - METHOD_OVERRIDE_PARAM_KEY = "_method".freeze - HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze - - def initialize(app) - @app = app - end - - def call(env) - if env["REQUEST_METHOD"] == "POST" - req = Request.new(env) - method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || - env[HTTP_METHOD_OVERRIDE_HEADER] - method = method.to_s.upcase - if HTTP_METHODS.include?(method) - env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] - env["REQUEST_METHOD"] = method - end - end - - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb deleted file mode 100644 index 5a6a73a97b..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mime.rb +++ /dev/null @@ -1,204 +0,0 @@ -module Rack - module Mime - # Returns String with mime type if found, otherwise use +fallback+. - # +ext+ should be filename extension in the '.ext' format that - # File.extname(file) returns. - # +fallback+ may be any object - # - # Also see the documentation for MIME_TYPES - # - # Usage: - # Rack::Mime.mime_type('.foo') - # - # This is a shortcut for: - # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') - - def mime_type(ext, fallback='application/octet-stream') - MIME_TYPES.fetch(ext, fallback) - end - module_function :mime_type - - # List of most common mime-types, selected various sources - # according to their usefulness in a webserving scope for Ruby - # users. - # - # To amend this list with your local mime.types list you can use: - # - # require 'webrick/httputils' - # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') - # Rack::Mime::MIME_TYPES.merge!(list) - # - # To add the list mongrel provides, use: - # - # require 'mongrel/handlers' - # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) - - MIME_TYPES = { - ".3gp" => "video/3gpp", - ".a" => "application/octet-stream", - ".ai" => "application/postscript", - ".aif" => "audio/x-aiff", - ".aiff" => "audio/x-aiff", - ".asc" => "application/pgp-signature", - ".asf" => "video/x-ms-asf", - ".asm" => "text/x-asm", - ".asx" => "video/x-ms-asf", - ".atom" => "application/atom+xml", - ".au" => "audio/basic", - ".avi" => "video/x-msvideo", - ".bat" => "application/x-msdownload", - ".bin" => "application/octet-stream", - ".bmp" => "image/bmp", - ".bz2" => "application/x-bzip2", - ".c" => "text/x-c", - ".cab" => "application/vnd.ms-cab-compressed", - ".cc" => "text/x-c", - ".chm" => "application/vnd.ms-htmlhelp", - ".class" => "application/octet-stream", - ".com" => "application/x-msdownload", - ".conf" => "text/plain", - ".cpp" => "text/x-c", - ".crt" => "application/x-x509-ca-cert", - ".css" => "text/css", - ".csv" => "text/csv", - ".cxx" => "text/x-c", - ".deb" => "application/x-debian-package", - ".der" => "application/x-x509-ca-cert", - ".diff" => "text/x-diff", - ".djv" => "image/vnd.djvu", - ".djvu" => "image/vnd.djvu", - ".dll" => "application/x-msdownload", - ".dmg" => "application/octet-stream", - ".doc" => "application/msword", - ".dot" => "application/msword", - ".dtd" => "application/xml-dtd", - ".dvi" => "application/x-dvi", - ".ear" => "application/java-archive", - ".eml" => "message/rfc822", - ".eps" => "application/postscript", - ".exe" => "application/x-msdownload", - ".f" => "text/x-fortran", - ".f77" => "text/x-fortran", - ".f90" => "text/x-fortran", - ".flv" => "video/x-flv", - ".for" => "text/x-fortran", - ".gem" => "application/octet-stream", - ".gemspec" => "text/x-script.ruby", - ".gif" => "image/gif", - ".gz" => "application/x-gzip", - ".h" => "text/x-c", - ".hh" => "text/x-c", - ".htm" => "text/html", - ".html" => "text/html", - ".ico" => "image/vnd.microsoft.icon", - ".ics" => "text/calendar", - ".ifb" => "text/calendar", - ".iso" => "application/octet-stream", - ".jar" => "application/java-archive", - ".java" => "text/x-java-source", - ".jnlp" => "application/x-java-jnlp-file", - ".jpeg" => "image/jpeg", - ".jpg" => "image/jpeg", - ".js" => "application/javascript", - ".json" => "application/json", - ".log" => "text/plain", - ".m3u" => "audio/x-mpegurl", - ".m4v" => "video/mp4", - ".man" => "text/troff", - ".mathml" => "application/mathml+xml", - ".mbox" => "application/mbox", - ".mdoc" => "text/troff", - ".me" => "text/troff", - ".mid" => "audio/midi", - ".midi" => "audio/midi", - ".mime" => "message/rfc822", - ".mml" => "application/mathml+xml", - ".mng" => "video/x-mng", - ".mov" => "video/quicktime", - ".mp3" => "audio/mpeg", - ".mp4" => "video/mp4", - ".mp4v" => "video/mp4", - ".mpeg" => "video/mpeg", - ".mpg" => "video/mpeg", - ".ms" => "text/troff", - ".msi" => "application/x-msdownload", - ".odp" => "application/vnd.oasis.opendocument.presentation", - ".ods" => "application/vnd.oasis.opendocument.spreadsheet", - ".odt" => "application/vnd.oasis.opendocument.text", - ".ogg" => "application/ogg", - ".p" => "text/x-pascal", - ".pas" => "text/x-pascal", - ".pbm" => "image/x-portable-bitmap", - ".pdf" => "application/pdf", - ".pem" => "application/x-x509-ca-cert", - ".pgm" => "image/x-portable-graymap", - ".pgp" => "application/pgp-encrypted", - ".pkg" => "application/octet-stream", - ".pl" => "text/x-script.perl", - ".pm" => "text/x-script.perl-module", - ".png" => "image/png", - ".pnm" => "image/x-portable-anymap", - ".ppm" => "image/x-portable-pixmap", - ".pps" => "application/vnd.ms-powerpoint", - ".ppt" => "application/vnd.ms-powerpoint", - ".ps" => "application/postscript", - ".psd" => "image/vnd.adobe.photoshop", - ".py" => "text/x-script.python", - ".qt" => "video/quicktime", - ".ra" => "audio/x-pn-realaudio", - ".rake" => "text/x-script.ruby", - ".ram" => "audio/x-pn-realaudio", - ".rar" => "application/x-rar-compressed", - ".rb" => "text/x-script.ruby", - ".rdf" => "application/rdf+xml", - ".roff" => "text/troff", - ".rpm" => "application/x-redhat-package-manager", - ".rss" => "application/rss+xml", - ".rtf" => "application/rtf", - ".ru" => "text/x-script.ruby", - ".s" => "text/x-asm", - ".sgm" => "text/sgml", - ".sgml" => "text/sgml", - ".sh" => "application/x-sh", - ".sig" => "application/pgp-signature", - ".snd" => "audio/basic", - ".so" => "application/octet-stream", - ".svg" => "image/svg+xml", - ".svgz" => "image/svg+xml", - ".swf" => "application/x-shockwave-flash", - ".t" => "text/troff", - ".tar" => "application/x-tar", - ".tbz" => "application/x-bzip-compressed-tar", - ".tcl" => "application/x-tcl", - ".tex" => "application/x-tex", - ".texi" => "application/x-texinfo", - ".texinfo" => "application/x-texinfo", - ".text" => "text/plain", - ".tif" => "image/tiff", - ".tiff" => "image/tiff", - ".torrent" => "application/x-bittorrent", - ".tr" => "text/troff", - ".txt" => "text/plain", - ".vcf" => "text/x-vcard", - ".vcs" => "text/x-vcalendar", - ".vrml" => "model/vrml", - ".war" => "application/java-archive", - ".wav" => "audio/x-wav", - ".wma" => "audio/x-ms-wma", - ".wmv" => "video/x-ms-wmv", - ".wmx" => "video/x-ms-wmx", - ".wrl" => "model/vrml", - ".wsdl" => "application/wsdl+xml", - ".xbm" => "image/x-xbitmap", - ".xhtml" => "application/xhtml+xml", - ".xls" => "application/vnd.ms-excel", - ".xml" => "application/xml", - ".xpm" => "image/x-xpixmap", - ".xsl" => "application/xml", - ".xslt" => "application/xslt+xml", - ".yaml" => "text/yaml", - ".yml" => "text/yaml", - ".zip" => "application/zip", - } - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb deleted file mode 100644 index fdefb0340a..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/mock.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'uri' -require 'stringio' -require 'rack/lint' -require 'rack/utils' -require 'rack/response' - -module Rack - # Rack::MockRequest helps testing your Rack application without - # actually using HTTP. - # - # After performing a request on a URL with get/post/put/delete, it - # returns a MockResponse with useful helper methods for effective - # testing. - # - # You can pass a hash with additional configuration to the - # get/post/put/delete. - # <tt>:input</tt>:: A String or IO-like to be used as rack.input. - # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors. - # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint. - - class MockRequest - class FatalWarning < RuntimeError - end - - class FatalWarner - def puts(warning) - raise FatalWarning, warning - end - - def write(warning) - raise FatalWarning, warning - end - - def flush - end - - def string - "" - end - end - - DEFAULT_ENV = { - "rack.version" => [1,0], - "rack.input" => StringIO.new, - "rack.errors" => StringIO.new, - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - } - - def initialize(app) - @app = app - end - - def get(uri, opts={}) request("GET", uri, opts) end - def post(uri, opts={}) request("POST", uri, opts) end - def put(uri, opts={}) request("PUT", uri, opts) end - def delete(uri, opts={}) request("DELETE", uri, opts) end - - def request(method="GET", uri="", opts={}) - env = self.class.env_for(uri, opts.merge(:method => method)) - - if opts[:lint] - app = Rack::Lint.new(@app) - else - app = @app - end - - errors = env["rack.errors"] - MockResponse.new(*(app.call(env) + [errors])) - end - - # Return the Rack environment used for a request to +uri+. - def self.env_for(uri="", opts={}) - uri = URI(uri) - uri.path = "/#{uri.path}" unless uri.path[0] == ?/ - - env = DEFAULT_ENV.dup - - env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" - env["SERVER_NAME"] = uri.host || "example.org" - env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" - env["QUERY_STRING"] = uri.query.to_s - env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path - env["rack.url_scheme"] = uri.scheme || "http" - env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" - - env["SCRIPT_NAME"] = opts[:script_name] || "" - - if opts[:fatal] - env["rack.errors"] = FatalWarner.new - else - env["rack.errors"] = StringIO.new - end - - if params = opts[:params] - if env["REQUEST_METHOD"] == "GET" - params = Utils.parse_nested_query(params) if params.is_a?(String) - params.update(Utils.parse_nested_query(env["QUERY_STRING"])) - env["QUERY_STRING"] = Utils.build_nested_query(params) - elsif !opts.has_key?(:input) - opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - if params.is_a?(Hash) - if data = Utils::Multipart.build_multipart(params) - opts[:input] = data - opts["CONTENT_LENGTH"] ||= data.length.to_s - opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}" - else - opts[:input] = Utils.build_nested_query(params) - end - else - opts[:input] = params - end - end - end - - opts[:input] ||= "" - if String === opts[:input] - env["rack.input"] = StringIO.new(opts[:input]) - else - env["rack.input"] = opts[:input] - end - - env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s - - opts.each { |field, value| - env[field] = value if String === field - } - - env - end - end - - # Rack::MockResponse provides useful helpers for testing your apps. - # Usually, you don't create the MockResponse on your own, but use - # MockRequest. - - class MockResponse - def initialize(status, headers, body, errors=StringIO.new("")) - @status = status.to_i - - @original_headers = headers - @headers = Rack::Utils::HeaderHash.new - headers.each { |field, values| - @headers[field] = values - @headers[field] = "" if values.empty? - } - - @body = "" - body.each { |part| @body << part } - - @errors = errors.string if errors.respond_to?(:string) - end - - # Status - attr_reader :status - - # Headers - attr_reader :headers, :original_headers - - def [](field) - headers[field] - end - - - # Body - attr_reader :body - - def =~(other) - @body =~ other - end - - def match(other) - @body.match other - end - - - # Errors - attr_accessor :errors - - - include Response::Helpers - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb deleted file mode 100644 index bf8b965925..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/recursive.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'uri' - -module Rack - # Rack::ForwardRequest gets caught by Rack::Recursive and redirects - # the current request to the app at +url+. - # - # raise ForwardRequest.new("/not-found") - # - - class ForwardRequest < Exception - attr_reader :url, :env - - def initialize(url, env={}) - @url = URI(url) - @env = env - - @env["PATH_INFO"] = @url.path - @env["QUERY_STRING"] = @url.query if @url.query - @env["HTTP_HOST"] = @url.host if @url.host - @env["HTTP_PORT"] = @url.port if @url.port - @env["rack.url_scheme"] = @url.scheme if @url.scheme - - super "forwarding to #{url}" - end - end - - # Rack::Recursive allows applications called down the chain to - # include data from other applications (by using - # <tt>rack['rack.recursive.include'][...]</tt> or raise a - # ForwardRequest to redirect internally. - - class Recursive - def initialize(app) - @app = app - end - - def call(env) - @script_name = env["SCRIPT_NAME"] - @app.call(env.merge('rack.recursive.include' => method(:include))) - rescue ForwardRequest => req - call(env.merge(req.env)) - end - - def include(env, path) - unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || - path[@script_name.size].nil?) - raise ArgumentError, "can only include below #{@script_name}, not #{path}" - end - - env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, - "REQUEST_METHOD" => "GET", - "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", - "rack.input" => StringIO.new("")) - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb deleted file mode 100644 index aa2f060be5..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/reloader.rb +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com -# All files in this distribution are subject to the terms of the Ruby license. - -require 'pathname' - -module Rack - - # High performant source reloader - # - # This class acts as Rack middleware. - # - # What makes it especially suited for use in a production environment is that - # any file will only be checked once and there will only be made one system - # call stat(2). - # - # Please note that this will not reload files in the background, it does so - # only when actively called. - # - # It is performing a check/reload cycle at the start of every request, but - # also respects a cool down time, during which nothing will be done. - class Reloader - def initialize(app, cooldown = 10, backend = Stat) - @app = app - @cooldown = cooldown - @last = (Time.now - cooldown) - @cache = {} - @mtimes = {} - - extend backend - end - - def call(env) - if @cooldown and Time.now > @last + @cooldown - if Thread.list.size > 1 - Thread.exclusive{ reload! } - else - reload! - end - - @last = Time.now - end - - @app.call(env) - end - - def reload!(stderr = $stderr) - rotation do |file, mtime| - previous_mtime = @mtimes[file] ||= mtime - safe_load(file, mtime, stderr) if mtime > previous_mtime - end - end - - # A safe Kernel::load, issuing the hooks depending on the results - def safe_load(file, mtime, stderr = $stderr) - load(file) - stderr.puts "#{self.class}: reloaded `#{file}'" - file - rescue LoadError, SyntaxError => ex - stderr.puts ex - ensure - @mtimes[file] = mtime - end - - module Stat - def rotation - files = [$0, *$LOADED_FEATURES].uniq - paths = ['./', *$LOAD_PATH].uniq - - files.map{|file| - next if file =~ /\.(so|bundle)$/ # cannot reload compiled files - - found, stat = figure_path(file, paths) - next unless found and stat and mtime = stat.mtime - - @cache[file] = found - - yield(found, mtime) - }.compact - end - - # Takes a relative or absolute +file+ name, a couple possible +paths+ that - # the +file+ might reside in. Returns the full path and File::Stat for the - # path. - def figure_path(file, paths) - found = @cache[file] - found = file if !found and Pathname.new(file).absolute? - found, stat = safe_stat(found) - return found, stat if found - - paths.each do |possible_path| - path = ::File.join(possible_path, file) - found, stat = safe_stat(path) - return ::File.expand_path(found), stat if found - end - end - - def safe_stat(file) - return unless file - stat = ::File.stat(file) - return file, stat if stat.file? - rescue Errno::ENOENT, Errno::ENOTDIR - @cache.delete(file) and false - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb deleted file mode 100644 index 0bff7af038..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/request.rb +++ /dev/null @@ -1,254 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Request provides a convenient interface to a Rack - # environment. It is stateless, the environment +env+ passed to the - # constructor will be directly modified. - # - # req = Rack::Request.new(env) - # req.post? - # req.params["data"] - # - # The environment hash passed will store a reference to the Request object - # instantiated so that it will only instantiate if an instance of the Request - # object doesn't already exist. - - class Request - # The environment of the request. - attr_reader :env - - def self.new(env, *args) - if self == Rack::Request - env["rack.request"] ||= super - else - super - end - end - - def initialize(env) - @env = env - end - - def body; @env["rack.input"] end - def scheme; @env["rack.url_scheme"] end - def script_name; @env["SCRIPT_NAME"].to_s end - def path_info; @env["PATH_INFO"].to_s end - def port; @env["SERVER_PORT"].to_i end - def request_method; @env["REQUEST_METHOD"] end - def query_string; @env["QUERY_STRING"].to_s end - def content_length; @env['CONTENT_LENGTH'] end - def content_type; @env['CONTENT_TYPE'] end - def session; @env['rack.session'] ||= {} end - def session_options; @env['rack.session.options'] ||= {} end - - # The media type (type/subtype) portion of the CONTENT_TYPE header - # without any media type parameters. e.g., when CONTENT_TYPE is - # "text/plain;charset=utf-8", the media-type is "text/plain". - # - # For more information on the use of media types in HTTP, see: - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 - def media_type - content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase - end - - # The media type parameters provided in CONTENT_TYPE as a Hash, or - # an empty Hash if no CONTENT_TYPE or media-type parameters were - # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", - # this method responds with the following Hash: - # { 'charset' => 'utf-8' } - def media_type_params - return {} if content_type.nil? - content_type.split(/\s*[;,]\s*/)[1..-1]. - collect { |s| s.split('=', 2) }. - inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } - end - - # The character set of the request body if a "charset" media type - # parameter was given, or nil if no "charset" was specified. Note - # that, per RFC2616, text/* media types that specify no explicit - # charset are to be considered ISO-8859-1. - def content_charset - media_type_params['charset'] - end - - def host - # Remove port number. - (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') - end - - def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end - def path_info=(s); @env["PATH_INFO"] = s.to_s end - - def get?; request_method == "GET" end - def post?; request_method == "POST" end - def put?; request_method == "PUT" end - def delete?; request_method == "DELETE" end - def head?; request_method == "HEAD" end - - # The set of form-data media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for form-data / param parsing. - FORM_DATA_MEDIA_TYPES = [ - nil, - 'application/x-www-form-urlencoded', - 'multipart/form-data' - ] - - # The set of media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for param parsing like soap attachments or generic multiparts - PARSEABLE_DATA_MEDIA_TYPES = [ - 'multipart/related', - 'multipart/mixed' - ] - - # Determine whether the request body contains form-data by checking - # the request media_type against registered form-data media-types: - # "application/x-www-form-urlencoded" and "multipart/form-data". The - # list of form-data media types can be modified through the - # +FORM_DATA_MEDIA_TYPES+ array. - def form_data? - FORM_DATA_MEDIA_TYPES.include?(media_type) - end - - # Determine whether the request body contains data by checking - # the request media_type against registered parse-data media-types - def parseable_data? - PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) - end - - # Returns the data recieved in the query string. - def GET - if @env["rack.request.query_string"] == query_string - @env["rack.request.query_hash"] - else - @env["rack.request.query_string"] = query_string - @env["rack.request.query_hash"] = - Utils.parse_nested_query(query_string) - end - end - - # Returns the data recieved in the request body. - # - # This method support both application/x-www-form-urlencoded and - # multipart/form-data. - def POST - if @env["rack.request.form_input"].eql? @env["rack.input"] - @env["rack.request.form_hash"] - elsif form_data? || parseable_data? - @env["rack.request.form_input"] = @env["rack.input"] - unless @env["rack.request.form_hash"] = - Utils::Multipart.parse_multipart(env) - form_vars = @env["rack.input"].read - - # Fix for Safari Ajax postings that always append \0 - form_vars.sub!(/\0\z/, '') - - @env["rack.request.form_vars"] = form_vars - @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) - - @env["rack.input"].rewind - end - @env["rack.request.form_hash"] - else - {} - end - end - - # The union of GET and POST data. - def params - self.put? ? self.GET : self.GET.update(self.POST) - rescue EOFError => e - self.GET - end - - # shortcut for request.params[key] - def [](key) - params[key.to_s] - end - - # shortcut for request.params[key] = value - def []=(key, value) - params[key.to_s] = value - end - - # like Hash#values_at - def values_at(*keys) - keys.map{|key| params[key] } - end - - # the referer of the client or '/' - def referer - @env['HTTP_REFERER'] || '/' - end - alias referrer referer - - - def cookies - return {} unless @env["HTTP_COOKIE"] - - if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] - else - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - # According to RFC 2109: - # If multiple cookies satisfy the criteria above, they are ordered in - # the Cookie header such that those with more specific Path attributes - # precede those with less specific. Ordering with respect to other - # attributes (e.g., Domain) is unspecified. - @env["rack.request.cookie_hash"] = - Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| - h[k] = Array === v ? v.first : v - h - } - end - end - - def xhr? - @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" - end - - # Tries to return a remake of the original request URL as a string. - def url - url = scheme + "://" - url << host - - if scheme == "https" && port != 443 || - scheme == "http" && port != 80 - url << ":#{port}" - end - - url << fullpath - - url - end - - def path - script_name + path_info - end - - def fullpath - query_string.empty? ? path : "#{path}?#{query_string}" - end - - def accept_encoding - @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| - m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick - - if m - [m[1], (m[2] || 1.0).to_f] - else - raise "Invalid value for Accept-Encoding: #{part.inspect}" - end - end - end - - def ip - if addr = @env['HTTP_X_FORWARDED_FOR'] - addr.split(',').last.strip - else - @env['REMOTE_ADDR'] - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb deleted file mode 100644 index 28b4d8302f..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/response.rb +++ /dev/null @@ -1,183 +0,0 @@ -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::Response provides a convenient interface to create a Rack - # response. - # - # It allows setting of headers and cookies, and provides useful - # defaults (a OK response containing HTML). - # - # You can use Response#write to iteratively generate your response, - # but note that this is buffered by Rack::Response until you call - # +finish+. +finish+ however can take a block inside which calls to - # +write+ are syncronous with the Rack response. - # - # Your application's +call+ should end returning Response#finish. - - class Response - attr_accessor :length - - def initialize(body=[], status=200, header={}, &block) - @status = status - @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. - merge(header)) - - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - - @body = [] - - if body.respond_to? :to_str - write body.to_str - elsif body.respond_to?(:each) - body.each { |part| - write part.to_s - } - else - raise TypeError, "stringable or iterable required" - end - - yield self if block_given? - end - - attr_reader :header - attr_accessor :status, :body - - def [](key) - header[key] - end - - def []=(key, value) - header[key] = value - end - - def set_cookie(key, value) - case value - when Hash - domain = "; domain=" + value[:domain] if value[:domain] - path = "; path=" + value[:path] if value[:path] - # According to RFC 2109, we need dashes here. - # N.B.: cgi.rb uses spaces... - expires = "; expires=" + value[:expires].clone.gmtime. - strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] - secure = "; secure" if value[:secure] - httponly = "; HttpOnly" if value[:httponly] - value = value[:value] - end - value = [value] unless Array === value - cookie = Utils.escape(key) + "=" + - value.map { |v| Utils.escape v }.join("&") + - "#{domain}#{path}#{expires}#{secure}#{httponly}" - - case self["Set-Cookie"] - when Array - self["Set-Cookie"] << cookie - when String - self["Set-Cookie"] = [self["Set-Cookie"], cookie] - when nil - self["Set-Cookie"] = cookie - end - end - - def delete_cookie(key, value={}) - unless Array === self["Set-Cookie"] - self["Set-Cookie"] = [self["Set-Cookie"]].compact - end - - self["Set-Cookie"].reject! { |cookie| - cookie =~ /\A#{Utils.escape(key)}=/ - } - - set_cookie(key, - {:value => '', :path => nil, :domain => nil, - :expires => Time.at(0) }.merge(value)) - end - - def redirect(target, status=302) - self.status = status - self["Location"] = target - end - - def finish(&block) - @block = block - - if [204, 304].include?(status.to_i) - header.delete "Content-Type" - [status.to_i, header.to_hash, []] - else - [status.to_i, header.to_hash, self] - end - end - alias to_a finish # For *response - - def each(&callback) - @body.each(&callback) - @writer = callback - @block.call(self) if @block - end - - # Append to body and update Content-Length. - # - # NOTE: Do not mix #write and direct #body access! - # - def write(str) - s = str.to_s - @length += Rack::Utils.bytesize(s) - @writer.call s - - header["Content-Length"] = @length.to_s - str - end - - def close - body.close if body.respond_to?(:close) - end - - def empty? - @block == nil && @body.empty? - end - - alias headers header - - module Helpers - def invalid?; @status < 100 || @status >= 600; end - - def informational?; @status >= 100 && @status < 200; end - def successful?; @status >= 200 && @status < 300; end - def redirection?; @status >= 300 && @status < 400; end - def client_error?; @status >= 400 && @status < 500; end - def server_error?; @status >= 500 && @status < 600; end - - def ok?; @status == 200; end - def forbidden?; @status == 403; end - def not_found?; @status == 404; end - - def redirect?; [301, 302, 303, 307].include? @status; end - def empty?; [201, 204, 304].include? @status; end - - # Headers - attr_reader :headers, :original_headers - - def include?(header) - !!headers[header] - end - - def content_type - headers["Content-Type"] - end - - def content_length - cl = headers["Content-Length"] - cl ? cl.to_i : cl - end - - def location - headers["Location"] - end - end - - include Helpers - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb deleted file mode 100644 index 9e9b21ff99..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/rewindable_input.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'tempfile' - -module Rack - # Class which can make any IO object rewindable, including non-rewindable ones. It does - # this by buffering the data into a tempfile, which is rewindable. - # - # rack.input is required to be rewindable, so if your input stream IO is non-rewindable - # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class - # to easily make it rewindable. - # - # Don't forget to call #close when you're done. This frees up temporary resources that - # RewindableInput uses, though it does *not* close the original IO object. - class RewindableInput - def initialize(io) - @io = io - @rewindable_io = nil - @unlinked = false - end - - def gets - make_rewindable unless @rewindable_io - @rewindable_io.gets - end - - def read(*args) - make_rewindable unless @rewindable_io - @rewindable_io.read(*args) - end - - def each(&block) - make_rewindable unless @rewindable_io - @rewindable_io.each(&block) - end - - def rewind - make_rewindable unless @rewindable_io - @rewindable_io.rewind - end - - # Closes this RewindableInput object without closing the originally - # wrapped IO oject. Cleans up any temporary resources that this RewindableInput - # has created. - # - # This method may be called multiple times. It does nothing on subsequent calls. - def close - if @rewindable_io - if @unlinked - @rewindable_io.close - else - @rewindable_io.close! - end - @rewindable_io = nil - end - end - - private - - # Ruby's Tempfile class has a bug. Subclass it and fix it. - class Tempfile < ::Tempfile - def _close - @tmpfile.close if @tmpfile - @data[1] = nil if @data - @tmpfile = nil - end - end - - def make_rewindable - # Buffer all data into a tempfile. Since this tempfile is private to this - # RewindableInput object, we chmod it so that nobody else can read or write - # it. On POSIX filesystems we also unlink the file so that it doesn't - # even have a file entry on the filesystem anymore, though we can still - # access it because we have the file handle open. - @rewindable_io = Tempfile.new('RackRewindableInput') - @rewindable_io.chmod(0000) - if filesystem_has_posix_semantics? - @rewindable_io.unlink - @unlinked = true - end - - buffer = "" - while @io.read(1024 * 4, buffer) - entire_buffer_written_out = false - while !entire_buffer_written_out - written = @rewindable_io.write(buffer) - entire_buffer_written_out = written == buffer.size - if !entire_buffer_written_out - buffer.slice!(0 .. written - 1) - end - end - end - @rewindable_io.rewind - end - - def filesystem_has_posix_semantics? - RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb deleted file mode 100644 index 218144c17f..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb +++ /dev/null @@ -1,142 +0,0 @@ -# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net -# bugrep: Andreas Zehnder - -require 'time' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - module Abstract - - # ID sets up a basic framework for implementing an id based sessioning - # service. Cookies sent to the client for maintaining sessions will only - # contain an id reference. Only #get_session and #set_session are - # required to be overwritten. - # - # All parameters are optional. - # * :key determines the name of the cookie, by default it is - # 'rack.session' - # * :path, :domain, :expire_after, :secure, and :httponly set the related - # cookie options as by Rack::Response#add_cookie - # * :defer will not set a cookie in the response. - # * :renew (implementation dependent) will prompt the generation of a new - # session id, and migration of data to be referenced at the new id. If - # :defer is set, it will be overridden and the cookie will be set. - # * :sidbits sets the number of bits in length that a generated session - # id will be. - # - # These options can be set on a per request basis, at the location of - # env['rack.session.options']. Additionally the id of the session can be - # found within the options hash at the key :id. It is highly not - # recommended to change its value. - # - # Is Rack::Utils::Context compatible. - - class ID - DEFAULT_OPTIONS = { - :path => '/', - :domain => nil, - :expire_after => nil, - :secure => false, - :httponly => true, - :defer => false, - :renew => false, - :sidbits => 128 - } - - attr_reader :key, :default_options - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @default_options = self.class::DEFAULT_OPTIONS.merge(options) - end - - def call(env) - context(env) - end - - def context(env, app=@app) - load_session(env) - status, headers, body = app.call(env) - commit_session(env, status, headers, body) - end - - private - - # Generate a new session id using Ruby #rand. The size of the - # session id is controlled by the :sidbits option. - # Monkey patch this to use custom methods for session id generation. - - def generate_sid - "%0#{@default_options[:sidbits] / 4}x" % - rand(2**@default_options[:sidbits] - 1) - end - - # Extracts the session id from provided cookies and passes it and the - # environment to #get_session. It then sets the resulting session into - # 'rack.session', and places options and session metadata into - # 'rack.session.options'. - - def load_session(env) - request = Rack::Request.new(env) - session_id = request.cookies[@key] - - begin - session_id, session = get_session(env, session_id) - env['rack.session'] = session - rescue - env['rack.session'] = Hash.new - end - - env['rack.session.options'] = @default_options. - merge(:id => session_id) - end - - # Acquires the session from the environment and the session id from - # the session options and passes them to #set_session. If successful - # and the :defer option is not true, a cookie will be added to the - # response with the session's id. - - def commit_session(env, status, headers, body) - session = env['rack.session'] - options = env['rack.session.options'] - session_id = options[:id] - - if not session_id = set_session(env, session_id, session, options) - env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") - [status, headers, body] - elsif options[:defer] and not options[:renew] - env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE - [status, headers, body] - else - cookie = Hash.new - cookie[:value] = session_id - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - # All thread safety and session retrival proceedures should occur here. - # Should return [session_id, session]. - # If nil is provided as the session id, generation of a new valid id - # should occur within. - - def get_session(env, sid) - raise '#get_session not implemented.' - end - - # All thread safety and session storage proceedures should occur here. - # Should return true or false dependant on whether or not the session - # was saved or not. - def set_session(env, sid, session, options) - raise '#set_session not implemented.' - end - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb deleted file mode 100644 index eace9bd0c6..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/cookie.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'openssl' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - # Rack::Session::Cookie provides simple cookie based session management. - # The session is a Ruby Hash stored as base64 encoded marshalled data - # set to :key (default: rack.session). - # When the secret key is set, cookie data is checked for data integrity. - # - # Example: - # - # use Rack::Session::Cookie, :key => 'rack.session', - # :domain => 'foo.com', - # :path => '/', - # :expire_after => 2592000, - # :secret => 'change_me' - # - # All parameters are optional. - - class Cookie - - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @secret = options[:secret] - @default_options = {:domain => nil, - :path => "/", - :expire_after => nil}.merge(options) - end - - def call(env) - load_session(env) - status, headers, body = @app.call(env) - commit_session(env, status, headers, body) - end - - private - - def load_session(env) - request = Rack::Request.new(env) - session_data = request.cookies[@key] - - if @secret && session_data - session_data, digest = session_data.split("--") - session_data = nil unless digest == generate_hmac(session_data) - end - - begin - session_data = session_data.unpack("m*").first - session_data = Marshal.load(session_data) - env["rack.session"] = session_data - rescue - env["rack.session"] = Hash.new - end - - env["rack.session.options"] = @default_options.dup - end - - def commit_session(env, status, headers, body) - session_data = Marshal.dump(env["rack.session"]) - session_data = [session_data].pack("m*") - - if @secret - session_data = "#{session_data}--#{generate_hmac(session_data)}" - end - - if session_data.size > (4096 - @key.size) - env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") - [status, headers, body] - else - options = env["rack.session.options"] - cookie = Hash.new - cookie[:value] = session_data - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - def generate_hmac(data) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) - end - - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb deleted file mode 100644 index 4a65cbf35d..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/memcache.rb +++ /dev/null @@ -1,109 +0,0 @@ -# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net - -require 'rack/session/abstract/id' -require 'memcache' - -module Rack - module Session - # Rack::Session::Memcache provides simple cookie based session management. - # Session data is stored in memcached. The corresponding session key is - # maintained in the cookie. - # You may treat Session::Memcache as you would Session::Pool with the - # following caveats. - # - # * Setting :expire_after to 0 would note to the Memcache server to hang - # onto the session data until it would drop it according to it's own - # specifications. However, the cookie sent to the client would expire - # immediately. - # - # Note that memcache does drop data before it may be listed to expire. For - # a full description of behaviour, please see memcache's documentation. - - class Memcache < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ - :namespace => 'rack:session', - :memcache_server => 'localhost:11211' - - def initialize(app, options={}) - super - - @mutex = Mutex.new - @pool = MemCache. - new @default_options[:memcache_server], @default_options - raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} - end - - def generate_sid - loop do - sid = super - break sid unless @pool.get(sid, true) - end - end - - def get_session(env, sid) - session = @pool.get(sid) if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - ret = @pool.add sid, session - raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return [ nil, {} ] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - expiry = options[:expire_after] - expiry = expiry.nil? ? 0 : expiry + 1 - - @mutex.lock if env['rack.multithread'] - session = @pool.get(session_id) || {} - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.add session_id, 0 # so we don't worry about cache miss on #set - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.set session_id, session, expiry - return session_id - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return false - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb deleted file mode 100644 index f6f87408bb..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/pool.rb +++ /dev/null @@ -1,100 +0,0 @@ -# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net -# THANKS: -# apeiros, for session id generation, expiry setup, and threadiness -# sergio, threadiness and bugreps - -require 'rack/session/abstract/id' -require 'thread' - -module Rack - module Session - # Rack::Session::Pool provides simple cookie based session management. - # Session data is stored in a hash held by @pool. - # In the context of a multithreaded environment, sessions being - # committed to the pool is done in a merging manner. - # - # The :drop option is available in rack.session.options if you with to - # explicitly remove the session from the session cache. - # - # Example: - # myapp = MyRackApp.new - # sessioned = Rack::Session::Pool.new(myapp, - # :domain => 'foo.com', - # :expire_after => 2592000 - # ) - # Rack::Handler::WEBrick.run sessioned - - class Pool < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false - - def initialize(app, options={}) - super - @pool = Hash.new - @mutex = Mutex.new - end - - def generate_sid - loop do - sid = super - break sid unless @pool.key? sid - end - end - - def get_session(env, sid) - session = @pool[sid] if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - @pool.store sid, session - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - @mutex.lock if env['rack.multithread'] - session = @pool[session_id] - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.store session_id, 0 - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.store session_id, session - return session_id - rescue - warn "#{new_session.inspect} has been lost." - warn $!.inspect - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb deleted file mode 100644 index 697bc41fdb..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showexceptions.rb +++ /dev/null @@ -1,349 +0,0 @@ -require 'ostruct' -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowExceptions catches all exceptions raised from the app it - # wraps. It shows a useful backtrace with the sourcefile and - # clickable context, the whole Rack environment and the request - # data. - # - # Be careful when you use this on public-facing sites as it could - # reveal information helpful to attackers. - - class ShowExceptions - CONTEXT = 7 - - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - @app.call(env) - rescue StandardError, LoadError, SyntaxError => e - backtrace = pretty(env, e) - [500, - {"Content-Type" => "text/html", - "Content-Length" => backtrace.join.size.to_s}, - backtrace] - end - - def pretty(env, exception) - req = Rack::Request.new(env) - path = (req.script_name + req.path_info).squeeze("/") - - frames = exception.backtrace.map { |line| - frame = OpenStruct.new - if line =~ /(.*?):(\d+)(:in `(.*)')?/ - frame.filename = $1 - frame.lineno = $2.to_i - frame.function = $4 - - begin - lineno = frame.lineno-1 - lines = ::File.readlines(frame.filename) - frame.pre_context_lineno = [lineno-CONTEXT, 0].max - frame.pre_context = lines[frame.pre_context_lineno...lineno] - frame.context_line = lines[lineno].chomp - frame.post_context_lineno = [lineno+CONTEXT, lines.size].min - frame.post_context = lines[lineno+1..frame.post_context_lineno] - rescue - end - - frame - else - nil - end - }.compact - - env["rack.errors"].puts "#{exception.class}: #{exception.message}" - env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } - env["rack.errors"].flush - - [@template.result(binding)] - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django <djangoproject.com> -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> -<html lang="en"> -<head> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <meta name="robots" content="NONE,NOARCHIVE" /> - <title><%=h exception.class %> at <%=h path %></title> - <style type="text/css"> - html * { padding:0; margin:0; } - body * { padding:10px 20px; } - body * * { padding:0; } - body { font:small sans-serif; } - body>div { border-bottom:1px solid #ddd; } - h1 { font-weight:normal; } - h2 { margin-bottom:.8em; } - h2 span { font-size:80%; color:#666; font-weight:normal; } - h3 { margin:1em 0 .5em 0; } - h4 { margin:0 0 .5em 0; font-weight: normal; } - table { - border:1px solid #ccc; border-collapse: collapse; background:white; } - tbody td, tbody th { vertical-align:top; padding:2px 3px; } - thead th { - padding:1px 6px 1px 3px; background:#fefefe; text-align:left; - font-weight:normal; font-size:11px; border:1px solid #ddd; } - tbody th { text-align:right; color:#666; padding-right:.5em; } - table.vars { margin:5px 0 2px 40px; } - table.vars td, table.req td { font-family:monospace; } - table td.code { width:100%;} - table td.code div { overflow:hidden; } - table.source th { color:#666; } - table.source td { - font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } - ul.traceback { list-style-type:none; } - ul.traceback li.frame { margin-bottom:1em; } - div.context { margin: 10px 0; } - div.context ol { - padding-left:30px; margin:0 10px; list-style-position: inside; } - div.context ol li { - font-family:monospace; white-space:pre; color:#666; cursor:pointer; } - div.context ol.context-line li { color:black; background-color:#ccc; } - div.context ol.context-line li span { float: right; } - div.commands { margin-left: 40px; } - div.commands a { color:black; text-decoration:none; } - #summary { background: #ffc; } - #summary h2 { font-weight: normal; color: #666; } - #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; } - #summary ul#quicklinks li { float: left; padding: 0 1em; } - #summary ul#quicklinks>li+li { border-left: 1px #666 solid; } - #explanation { background:#eee; } - #template, #template-not-exist { background:#f6f6f6; } - #template-not-exist ul { margin: 0 0 0 20px; } - #traceback { background:#eee; } - #requestinfo { background:#f6f6f6; padding-left:120px; } - #summary table { border:none; background:transparent; } - #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; } - #requestinfo h3 { margin-bottom:-1em; } - .error { background: #ffc; } - .specific { color:#cc3300; font-weight:bold; } - </style> - <script type="text/javascript"> - //<!-- - function getElementsByClassName(oElm, strTagName, strClassName){ - // Written by Jonathan Snook, http://www.snook.ca/jon; - // Add-ons by Robert Nyman, http://www.robertnyman.com - var arrElements = (strTagName == "*" && document.all)? document.all : - oElm.getElementsByTagName(strTagName); - var arrReturnElements = new Array(); - strClassName = strClassName.replace(/\-/g, "\\-"); - var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)"); - var oElement; - for(var i=0; i<arrElements.length; i++){ - oElement = arrElements[i]; - if(oRegExp.test(oElement.className)){ - arrReturnElements.push(oElement); - } - } - return (arrReturnElements) - } - function hideAll(elems) { - for (var e = 0; e < elems.length; e++) { - elems[e].style.display = 'none'; - } - } - window.onload = function() { - hideAll(getElementsByClassName(document, 'table', 'vars')); - hideAll(getElementsByClassName(document, 'ol', 'pre-context')); - hideAll(getElementsByClassName(document, 'ol', 'post-context')); - } - function toggle() { - for (var i = 0; i < arguments.length; i++) { - var e = document.getElementById(arguments[i]); - if (e) { - e.style.display = e.style.display == 'none' ? 'block' : 'none'; - } - } - return false; - } - function varToggle(link, id) { - toggle('v' + id); - var s = link.getElementsByTagName('span')[0]; - var uarr = String.fromCharCode(0x25b6); - var darr = String.fromCharCode(0x25bc); - s.innerHTML = s.innerHTML == uarr ? darr : uarr; - return false; - } - //--> - </script> -</head> -<body> - -<div id="summary"> - <h1><%=h exception.class %> at <%=h path %></h1> - <h2><%=h exception.message %></h2> - <table><tr> - <th>Ruby</th> - <td><code><%=h frames.first.filename %></code>: in <code><%=h frames.first.function %></code>, line <%=h frames.first.lineno %></td> - </tr><tr> - <th>Web</th> - <td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td> - </tr></table> - - <h3>Jump to:</h3> - <ul id="quicklinks"> - <li><a href="#get-info">GET</a></li> - <li><a href="#post-info">POST</a></li> - <li><a href="#cookie-info">Cookies</a></li> - <li><a href="#env-info">ENV</a></li> - </ul> -</div> - -<div id="traceback"> - <h2>Traceback <span>(innermost first)</span></h2> - <ul class="traceback"> -<% frames.each { |frame| %> - <li class="frame"> - <code><%=h frame.filename %></code>: in <code><%=h frame.function %></code> - - <% if frame.context_line %> - <div class="context" id="c<%=h frame.object_id %>"> - <% if frame.pre_context %> - <ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>"> - <% frame.pre_context.each { |line| %> - <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li> - <% } %> - </ol> - <% end %> - - <ol start="<%=h frame.lineno %>" class="context-line"> - <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol> - - <% if frame.post_context %> - <ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>"> - <% frame.post_context.each { |line| %> - <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li> - <% } %> - </ol> - <% end %> - </div> - <% end %> - </li> -<% } %> - </ul> -</div> - -<div id="requestinfo"> - <h2>Request information</h2> - - <h3 id="get-info">GET</h3> - <% unless req.GET.empty? %> - <table class="req"> - <thead> - <tr> - <th>Variable</th> - <th>Value</th> - </tr> - </thead> - <tbody> - <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> - <tr> - <td><%=h key %></td> - <td class="code"><div><%=h val.inspect %></div></td> - </tr> - <% } %> - </tbody> - </table> - <% else %> - <p>No GET data.</p> - <% end %> - - <h3 id="post-info">POST</h3> - <% unless req.POST.empty? %> - <table class="req"> - <thead> - <tr> - <th>Variable</th> - <th>Value</th> - </tr> - </thead> - <tbody> - <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> - <tr> - <td><%=h key %></td> - <td class="code"><div><%=h val.inspect %></div></td> - </tr> - <% } %> - </tbody> - </table> - <% else %> - <p>No POST data.</p> - <% end %> - - - <h3 id="cookie-info">COOKIES</h3> - <% unless req.cookies.empty? %> - <table class="req"> - <thead> - <tr> - <th>Variable</th> - <th>Value</th> - </tr> - </thead> - <tbody> - <% req.cookies.each { |key, val| %> - <tr> - <td><%=h key %></td> - <td class="code"><div><%=h val.inspect %></div></td> - </tr> - <% } %> - </tbody> - </table> - <% else %> - <p>No cookie data.</p> - <% end %> - - <h3 id="env-info">Rack ENV</h3> - <table class="req"> - <thead> - <tr> - <th>Variable</th> - <th>Value</th> - </tr> - </thead> - <tbody> - <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> - <tr> - <td><%=h key %></td> - <td class="code"><div><%=h val %></div></td> - </tr> - <% } %> - </tbody> - </table> - -</div> - -<div id="explanation"> - <p> - You're seeing this error because you use <code>Rack::ShowExceptions</code>. - </p> -</div> - -</body> -</html> -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb deleted file mode 100644 index 28258c7c89..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/showstatus.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowStatus catches all empty responses the app it wraps and - # replaces them with a site explaining the error. - # - # Additional details can be put into <tt>rack.showstatus.detail</tt> - # and will be shown as HTML. If such details exist, the error page - # is always rendered, even if the reply was not empty. - - class ShowStatus - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - empty = headers['Content-Length'].to_i <= 0 - - # client or server error, or explicit message - if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] - req = Rack::Request.new(env) - message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s - detail = env["rack.showstatus.detail"] || message - body = @template.result(binding) - size = Rack::Utils.bytesize(body) - [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] - else - [status, headers, body] - end - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django <djangoproject.com> -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> -<html lang="en"> -<head> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <title><%=h message %> at <%=h req.script_name + req.path_info %></title> - <meta name="robots" content="NONE,NOARCHIVE" /> - <style type="text/css"> - html * { padding:0; margin:0; } - body * { padding:10px 20px; } - body * * { padding:0; } - body { font:small sans-serif; background:#eee; } - body>div { border-bottom:1px solid #ddd; } - h1 { font-weight:normal; margin-bottom:.4em; } - h1 span { font-size:60%; color:#666; font-weight:normal; } - table { border:none; border-collapse: collapse; width:100%; } - td, th { vertical-align:top; padding:2px 3px; } - th { width:12em; text-align:right; color:#666; padding-right:.5em; } - #info { background:#f6f6f6; } - #info ol { margin: 0.5em 4em; } - #info ol li { font-family: monospace; } - #summary { background: #ffc; } - #explanation { background:#eee; border-bottom: 0px none; } - </style> -</head> -<body> - <div id="summary"> - <h1><%=h message %> <span>(<%= status.to_i %>)</span></h1> - <table class="meta"> - <tr> - <th>Request Method:</th> - <td><%=h req.request_method %></td> - </tr> - <tr> - <th>Request URL:</th> - <td><%=h req.url %></td> - </tr> - </table> - </div> - <div id="info"> - <p><%= detail %></p> - </div> - - <div id="explanation"> - <p> - You're seeing this error because you use <code>Rack::ShowStatus</code>. - </p> - </div> -</body> -</html> -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb deleted file mode 100644 index 168e8f83b2..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/static.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Rack - - # The Rack::Static middleware intercepts requests for static files - # (javascript files, images, stylesheets, etc) based on the url prefixes - # passed in the options, and serves them using a Rack::File object. This - # allows a Rack stack to serve both static and dynamic content. - # - # Examples: - # use Rack::Static, :urls => ["/media"] - # will serve all requests beginning with /media from the "media" folder - # located in the current directory (ie media/*). - # - # use Rack::Static, :urls => ["/css", "/images"], :root => "public" - # will serve all requests beginning with /css or /images from the folder - # "public" in the current directory (ie public/css/* and public/images/*) - - class Static - - def initialize(app, options={}) - @app = app - @urls = options[:urls] || ["/favicon.ico"] - root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) - end - - def call(env) - path = env["PATH_INFO"] - can_serve = @urls.any? { |url| path.index(url) == 0 } - - if can_serve - @file_server.call(env) - else - @app.call(env) - end - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb deleted file mode 100644 index fcf6616c58..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/urlmap.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - # Rack::URLMap takes a hash mapping urls or paths to apps, and - # dispatches accordingly. Support for HTTP/1.1 host names exists if - # the URLs start with <tt>http://</tt> or <tt>https://</tt>. - # - # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part - # relevant for dispatch is in the SCRIPT_NAME, and the rest in the - # PATH_INFO. This should be taken care of when you need to - # reconstruct the URL in order to create links. - # - # URLMap dispatches in such a way that the longest paths are tried - # first, since they are most specific. - - class URLMap - def initialize(map = {}) - remap(map) - end - - def remap(map) - @mapping = map.map { |location, app| - if location =~ %r{\Ahttps?://(.*?)(/.*)} - host, location = $1, $2 - else - host = nil - end - - unless location[0] == ?/ - raise ArgumentError, "paths need to start with /" - end - location = location.chomp('/') - - [host, location, app] - }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first - end - - def call(env) - path = env["PATH_INFO"].to_s.squeeze("/") - script_name = env['SCRIPT_NAME'] - hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') - @mapping.each { |host, location, app| - next unless (hHost == host || sName == host \ - || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) - next unless location == path[0, location.size] - next unless path[location.size] == nil || path[location.size] == ?/ - - return app.call( - env.merge( - 'SCRIPT_NAME' => (script_name + location), - 'PATH_INFO' => path[location.size..-1])) - } - [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] - end - end -end - diff --git a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb deleted file mode 100644 index 42e2e698f4..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/utils.rb +++ /dev/null @@ -1,516 +0,0 @@ -# -*- encoding: binary -*- - -require 'set' -require 'tempfile' - -module Rack - # Rack::Utils contains a grab-bag of useful methods for writing web - # applications adopted from all kinds of Ruby libraries. - - module Utils - # Performs URI escaping so that you can construct proper - # query strings faster. Use this rather than the cgi.rb - # version since it's faster. (Stolen from Camping). - def escape(s) - s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { - '%'+$1.unpack('H2'*$1.size).join('%').upcase - }.tr(' ', '+') - end - module_function :escape - - # Unescapes a URI escaped string. (Stolen from Camping). - def unescape(s) - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') - } - end - module_function :unescape - - # Stolen from Mongrel, with some small modifications: - # Parses a query string by breaking it up at the '&' - # and ';' characters. You can also use this to parse - # cookies by changing the characters used in the second - # parameter (which defaults to '&;'). - def parse_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - - if cur = params[k] - if cur.class == Array - params[k] << v - else - params[k] = [cur, v] - end - else - params[k] = v - end - end - - return params - end - module_function :parse_query - - def parse_nested_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - normalize_params(params, k, v) - end - - return params - end - module_function :parse_nested_query - - def normalize_params(params, name, v = nil) - name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - params[k] = v - elsif after == "[]" - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) - end - - return params - end - module_function :normalize_params - - def build_query(params) - params.map { |k, v| - if v.class == Array - build_query(v.map { |x| [k, x] }) - else - escape(k) + "=" + escape(v) - end - }.join("&") - end - module_function :build_query - - def build_nested_query(value, prefix = nil) - case value - when Array - value.map { |v| - build_nested_query(v, "#{prefix}[]") - }.join("&") - when Hash - value.map { |k, v| - build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) - }.join("&") - when String - raise ArgumentError, "value must be a Hash" if prefix.nil? - "#{prefix}=#{escape(value)}" - else - prefix - end - end - module_function :build_nested_query - - # Escape ampersands, brackets and quotes to their HTML/XML entities. - def escape_html(string) - string.to_s.gsub("&", "&"). - gsub("<", "<"). - gsub(">", ">"). - gsub("'", "'"). - gsub('"', """) - end - module_function :escape_html - - def select_best_encoding(available_encodings, accept_encoding) - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - - expanded_accept_encoding = - accept_encoding.map { |m, q| - if m == "*" - (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } - else - [[m, q]] - end - }.inject([]) { |mem, list| - mem + list - } - - encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } - - unless encoding_candidates.include?("identity") - encoding_candidates.push("identity") - end - - expanded_accept_encoding.find_all { |m, q| - q == 0.0 - }.each { |m, _| - encoding_candidates.delete(m) - } - - return (encoding_candidates & available_encodings)[0] - end - module_function :select_best_encoding - - # Return the bytesize of String; uses String#length under Ruby 1.8 and - # String#bytesize under 1.9. - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size - end - end - module_function :bytesize - - # Context allows the use of a compatible middleware at different points - # in a request handling stack. A compatible middleware must define - # #context which should take the arguments env and app. The first of which - # would be the request environment. The second of which would be the rack - # application that the request would be forwarded to. - class Context - attr_reader :for, :app - - def initialize(app_f, app_r) - raise 'running context does not respond to #context' unless app_f.respond_to? :context - @for, @app = app_f, app_r - end - - def call(env) - @for.context(env, @app) - end - - def recontext(app) - self.class.new(@for, app) - end - - def context(env, app=@app) - recontext(app).call(env) - end - end - - # A case-insensitive Hash that preserves the original case of a - # header when set. - class HeaderHash < Hash - def initialize(hash={}) - @names = {} - hash.each { |k, v| self[k] = v } - end - - def to_hash - inject({}) do |hash, (k,v)| - if v.respond_to? :to_ary - hash[k] = v.to_ary.join("\n") - else - hash[k] = v - end - hash - end - end - - def [](k) - super @names[k.downcase] - end - - def []=(k, v) - delete k - @names[k.downcase] = k - super k, v - end - - def delete(k) - super @names.delete(k.downcase) - end - - def include?(k) - @names.has_key? k.downcase - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - - def merge(other) - hash = dup - hash.merge! other - end - end - - # Every standard HTTP code mapped to the appropriate message. - # Stolen from Mongrel. - HTTP_STATUS_CODES = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - } - - # Responses with HTTP status codes that should not have an entity body - STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) - - # A multipart form data parser, adapted from IOWA. - # - # Usually, Rack::Request#POST takes care of calling this. - - module Multipart - class UploadedFile - # The filename, *not* including the path, of the "uploaded" file - attr_reader :original_filename - - # The content type of the "uploaded" file - attr_accessor :content_type - - def initialize(path, content_type = "text/plain", binary = false) - raise "#{path} file does not exist" unless ::File.exist?(path) - @content_type = content_type - @original_filename = ::File.basename(path) - @tempfile = Tempfile.new(@original_filename) - @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) - @tempfile.binmode if binary - FileUtils.copy_file(path, @tempfile.path) - end - - def path - @tempfile.path - end - alias_method :local_path, :path - - def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.__send__(method_name, *args, &block) - end - end - - EOL = "\r\n" - MULTIPART_BOUNDARY = "AaB03x" - - def self.parse_multipart(env) - unless env['CONTENT_TYPE'] =~ - %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n - nil - else - boundary = "--#{$1}" - - params = {} - buf = "" - content_length = env['CONTENT_LENGTH'].to_i - input = env['rack.input'] - input.rewind - - boundary_size = Utils.bytesize(boundary) + EOL.size - bufsize = 16384 - - content_length -= boundary_size - - read_buffer = '' - - status = input.read(boundary_size, read_buffer) - raise EOFError, "bad content body" unless status == boundary + EOL - - rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n - - loop { - head = nil - body = '' - filename = content_type = name = nil - - until head && buf =~ rx - if !head && i = buf.index(EOL+EOL) - head = buf.slice!(0, i+2) # First \r\n - buf.slice!(0, 2) # Second \r\n - - filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] - content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] - name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] - - if content_type || filename - body = Tempfile.new("RackMultipart") - body.binmode if body.respond_to?(:binmode) - end - - next - end - - # Save the read body part. - if head && (boundary_size+4 < buf.size) - body << buf.slice!(0, buf.size - (boundary_size+4)) - end - - c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) - raise EOFError, "bad content body" if c.nil? || c.empty? - buf << c - content_length -= c.size - end - - # Save the rest. - if i = buf.index(rx) - body << buf.slice!(0, i) - buf.slice!(0, boundary_size+2) - - content_length = -1 if $1 == "--" - end - - if filename == "" - # filename is blank which means no file has been selected - data = nil - elsif filename - body.rewind - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - filename =~ /^(?:.*[:\\\/])?(.*)/m - filename = $1 - - data = {:filename => filename, :type => content_type, - :name => name, :tempfile => body, :head => head} - elsif !filename && content_type - body.rewind - - # Generic multipart cases, not coming from a form - data = {:type => content_type, - :name => name, :tempfile => body, :head => head} - else - data = body - end - - Utils.normalize_params(params, name, data) unless data.nil? - - break if buf.empty? || content_length == -1 - } - - input.rewind - - params - end - end - - def self.build_multipart(params, first = true) - if first - unless params.is_a?(Hash) - raise ArgumentError, "value must be a Hash" - end - - multipart = false - query = lambda { |value| - case value - when Array - value.each(&query) - when Hash - value.values.each(&query) - when UploadedFile - multipart = true - end - } - params.values.each(&query) - return nil unless multipart - end - - flattened_params = Hash.new - - params.each do |key, value| - k = first ? key.to_s : "[#{key}]" - - case value - when Array - value.map { |v| - build_multipart(v, false).each { |subkey, subvalue| - flattened_params["#{k}[]#{subkey}"] = subvalue - } - } - when Hash - build_multipart(value, false).each { |subkey, subvalue| - flattened_params[k + subkey] = subvalue - } - else - flattened_params[k] = value - end - end - - if first - flattened_params.map { |name, file| - if file.respond_to?(:original_filename) - ::File.open(file.path, "rb") do |f| - f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r -Content-Type: #{file.content_type}\r -Content-Length: #{::File.stat(file.path).size}\r -\r -#{f.read}\r -EOF - end - else -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"\r -\r -#{file}\r -EOF - end - }.join + "--#{MULTIPART_BOUNDARY}--\r" - else - flattened_params - end - end - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb deleted file mode 100644 index eba6226538..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Rack - - class MockSession - attr_writer :cookie_jar - attr_reader :last_response - - def initialize(app, default_host = Rack::Test::DEFAULT_HOST) - @app = app - @default_host = default_host - end - - def clear_cookies - @cookie_jar = Rack::Test::CookieJar.new([], @default_host) - end - - def set_cookie(cookie, uri = nil) - cookie_jar.merge(cookie, uri) - end - - def request(uri, env) - env["HTTP_COOKIE"] ||= cookie_jar.for(uri) - @last_request = Rack::Request.new(env) - status, headers, body = @app.call(@last_request.env) - @last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush) - cookie_jar.merge(last_response.headers["Set-Cookie"], uri) - - @last_response - end - - # Return the last request issued in the session. Raises an error if no - # requests have been sent yet. - def last_request - raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request - @last_request - end - - # Return the last response received in the session. Raises an error if - # no requests have been sent yet. - def last_response - raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response - @last_response - end - - def cookie_jar - @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host) - end - - end - -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb deleted file mode 100644 index 70384b1d76..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb +++ /dev/null @@ -1,239 +0,0 @@ -unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/..")) - $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/..")) -end - -require "uri" -require "rack" -require "rack/mock_session" -require "rack/test/cookie_jar" -require "rack/test/mock_digest_request" -require "rack/test/utils" -require "rack/test/methods" -require "rack/test/uploaded_file" - -module Rack - module Test - - VERSION = "0.3.0" - - DEFAULT_HOST = "example.org" - MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1" - - # The common base class for exceptions raised by Rack::Test - class Error < StandardError; end - - class Session - extend Forwardable - include Rack::Test::Utils - - def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request - - # Initialize a new session for the given Rack app - def initialize(app, default_host = DEFAULT_HOST) - @headers = {} - @default_host = default_host - @rack_mock_session = Rack::MockSession.new(app, default_host) - end - - # Issue a GET request for the given URI with the given params and Rack - # environment. Stores the issues request object in #last_request and - # the app's response in #last_response. Yield #last_response to a block - # if given. - # - # Example: - # get "/" - def get(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "GET", :params => params)) - process_request(uri, env, &block) - end - - # Issue a POST request for the given URI. See #get - # - # Example: - # post "/signup", "name" => "Bryan" - def post(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "POST", :params => params)) - process_request(uri, env, &block) - end - - # Issue a PUT request for the given URI. See #get - # - # Example: - # put "/" - def put(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "PUT", :params => params)) - process_request(uri, env, &block) - end - - # Issue a DELETE request for the given URI. See #get - # - # Example: - # delete "/" - def delete(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "DELETE", :params => params)) - process_request(uri, env, &block) - end - - # Issue a HEAD request for the given URI. See #get - # - # Example: - # head "/" - def head(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "HEAD", :params => params)) - process_request(uri, env, &block) - end - - # Issue a request to the Rack app for the given URI and optional Rack - # environment. Stores the issues request object in #last_request and - # the app's response in #last_response. Yield #last_response to a block - # if given. - # - # Example: - # request "/" - def request(uri, env = {}, &block) - env = env_for(uri, env) - process_request(uri, env, &block) - end - - # Set a header to be included on all subsequent requests through the - # session. Use a value of nil to remove a previously configured header. - # - # Example: - # header "User-Agent", "Firefox" - def header(name, value) - if value.nil? - @headers.delete(name) - else - @headers[name] = value - end - end - - # Set the username and password for HTTP Basic authorization, to be - # included in subsequent requests in the HTTP_AUTHORIZATION header. - # - # Example: - # basic_authorize "bryan", "secret" - def basic_authorize(username, password) - encoded_login = ["#{username}:#{password}"].pack("m*") - header('HTTP_AUTHORIZATION', "Basic #{encoded_login}") - end - - alias_method :authorize, :basic_authorize - - def digest_authorize(username, password) - @digest_username = username - @digest_password = password - end - - # Rack::Test will not follow any redirects automatically. This method - # will follow the redirect returned in the last response. If the last - # response was not a redirect, an error will be raised. - def follow_redirect! - unless last_response.redirect? - raise Error.new("Last response was not a redirect. Cannot follow_redirect!") - end - - get(last_response["Location"]) - end - - private - - def env_for(path, env) - uri = URI.parse(path) - uri.host ||= @default_host - - env = default_env.merge(env) - - env.update("HTTPS" => "on") if URI::HTTPS === uri - env["X-Requested-With"] = "XMLHttpRequest" if env[:xhr] - - if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input) - env["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - - multipart = (Hash === env[:params]) && - env[:params].any? { |_, v| UploadedFile === v } - - if multipart - env[:input] = multipart_body(env.delete(:params)) - env["CONTENT_LENGTH"] ||= env[:input].length.to_s - env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}" - else - env[:input] = params_to_string(env.delete(:params)) - end - end - - params = env[:params] || {} - params.update(parse_query(uri.query)) - - uri.query = requestify(params) - - if env.has_key?(:cookie) - set_cookie(env.delete(:cookie), uri) - end - - Rack::MockRequest.env_for(uri.to_s, env) - end - - def process_request(uri, env) - uri = URI.parse(uri) - uri.host ||= @default_host - - @rack_mock_session.request(uri, env) - - if retry_with_digest_auth?(env) - auth_env = env.merge({ - "HTTP_AUTHORIZATION" => digest_auth_header, - "rack-test.digest_auth_retry" => true - }) - auth_env.delete('rack.request') - process_request(uri.path, auth_env) - else - yield last_response if block_given? - - last_response - end - end - - def digest_auth_header - challenge = last_response["WWW-Authenticate"].split(" ", 2).last - params = Rack::Auth::Digest::Params.parse(challenge) - - params.merge!({ - "username" => @digest_username, - "nc" => "00000001", - "cnonce" => "nonsensenonce", - "uri" => last_request.path_info, - "method" => last_request.env["REQUEST_METHOD"], - }) - - params["response"] = MockDigestRequest.new(params).response(@digest_password) - - "Digest #{params}" - end - - def retry_with_digest_auth?(env) - last_response.status == 401 && - digest_auth_configured? && - !env["rack-test.digest_auth_retry"] - end - - def digest_auth_configured? - @digest_username - end - - def default_env - { "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(@headers) - end - - def params_to_string(params) - case params - when Hash then requestify(params) - when nil then "" - else params - end - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb deleted file mode 100644 index d58c914c9b..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb +++ /dev/null @@ -1,169 +0,0 @@ -require "uri" -module Rack - module Test - - class Cookie - include Rack::Utils - - # :api: private - attr_reader :name, :value - - # :api: private - def initialize(raw, uri = nil, default_host = DEFAULT_HOST) - @default_host = default_host - uri ||= default_uri - - # separate the name / value pair from the cookie options - @name_value_raw, options = raw.split(/[;,] */n, 2) - - @name, @value = parse_query(@name_value_raw, ';').to_a.first - @options = parse_query(options, ';') - - @options["domain"] ||= (uri.host || default_host) - @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "") - end - - def replaces?(other) - [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path] - end - - # :api: private - def raw - @name_value_raw - end - - # :api: private - def empty? - @value.nil? || @value.empty? - end - - # :api: private - def domain - @options["domain"] - end - - def secure? - @options.has_key?("secure") - end - - # :api: private - def path - @options["path"].strip || "/" - end - - # :api: private - def expires - Time.parse(@options["expires"]) if @options["expires"] - end - - # :api: private - def expired? - expires && expires < Time.now - end - - # :api: private - def valid?(uri) - uri ||= default_uri - - if uri.host.nil? - uri.host = @default_host - end - - (!secure? || (secure? && uri.scheme == "https")) && - uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) && - uri.path =~ Regexp.new("^#{Regexp.escape(path)}") - end - - # :api: private - def matches?(uri) - ! expired? && valid?(uri) - end - - # :api: private - def <=>(other) - # Orders the cookies from least specific to most - [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse] - end - - protected - - def default_uri - URI.parse("//" + @default_host + "/") - end - - end - - class CookieJar - - # :api: private - def initialize(cookies = [], default_host = DEFAULT_HOST) - @default_host = default_host - @cookies = cookies - @cookies.sort! - end - - def [](name) - cookies = hash_for(nil) - # TODO: Should be case insensitive - cookies[name] && cookies[name].value - end - - def []=(name, value) - # TODO: needs proper escaping - merge("#{name}=#{value}") - end - - def merge(raw_cookies, uri = nil) - return unless raw_cookies - - raw_cookies.each_line do |raw_cookie| - cookie = Cookie.new(raw_cookie, uri, @default_host) - self << cookie if cookie.valid?(uri) - end - end - - def <<(new_cookie) - @cookies.reject! do |existing_cookie| - new_cookie.replaces?(existing_cookie) - end - - @cookies << new_cookie - @cookies.sort! - end - - # :api: private - def for(uri) - hash_for(uri).values.map { |c| c.raw }.join(';') - end - - def to_hash - cookies = {} - - hash_for(nil).each do |name, cookie| - cookies[name] = cookie.value - end - - return cookies - end - - protected - - def hash_for(uri = nil) - cookies = {} - - # The cookies are sorted by most specific first. So, we loop through - # all the cookies in order and add it to a hash by cookie name if - # the cookie can be sent to the current URI. It's added to the hash - # so that when we are done, the cookies will be unique by name and - # we'll have grabbed the most specific to the URI. - @cookies.each do |cookie| - cookies[cookie.name] = cookie if cookie.matches?(uri) - end - - return cookies - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb deleted file mode 100644 index a191fa23d8..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb +++ /dev/null @@ -1,45 +0,0 @@ -require "forwardable" - -module Rack - module Test - module Methods - extend Forwardable - - def rack_test_session - @_rack_test_session ||= Rack::Test::Session.new(app) - end - - def rack_mock_session - @_rack_mock_session ||= Rack::MockSession.new(app) - end - - METHODS = [ - :request, - - # HTTP verbs - :get, - :post, - :put, - :delete, - :head, - - # Redirects - :follow_redirect!, - - # Header-related features - :header, - :set_cookie, - :clear_cookies, - :authorize, - :basic_authorize, - :digest_authorize, - - # Expose the last request and response - :last_response, - :last_request - ] - - def_delegators :rack_test_session, *METHODS - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb deleted file mode 100644 index 81c398ba51..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - module Test - - class MockDigestRequest - def initialize(params) - @params = params - end - - def method_missing(sym) - if @params.has_key? k = sym.to_s - return @params[k] - end - - super - end - - def method - @params['method'] - end - - def response(password) - Rack::Auth::Digest::MD5.new(nil).send :digest, self, password - end - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb deleted file mode 100644 index 239302fbe4..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb +++ /dev/null @@ -1,36 +0,0 @@ -require "tempfile" - -module Rack - module Test - - class UploadedFile - # The filename, *not* including the path, of the "uploaded" file - attr_reader :original_filename - - # The content type of the "uploaded" file - attr_accessor :content_type - - def initialize(path, content_type = "text/plain", binary = false) - raise "#{path} file does not exist" unless ::File.exist?(path) - @content_type = content_type - @original_filename = ::File.basename(path) - @tempfile = Tempfile.new(@original_filename) - @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) - @tempfile.binmode if binary - FileUtils.copy_file(path, @tempfile.path) - end - - def path - @tempfile.path - end - - alias_method :local_path, :path - - def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.__send__(method_name, *args, &block) - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb deleted file mode 100644 index d25b849709..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb +++ /dev/null @@ -1,75 +0,0 @@ -module Rack - module Test - - module Utils - include Rack::Utils - - def requestify(value, prefix = nil) - case value - when Array - value.map do |v| - requestify(v, "#{prefix}[]") - end.join("&") - when Hash - value.map do |k, v| - requestify(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) - end.join("&") - else - "#{prefix}=#{escape(value)}" - end - end - - module_function :requestify - - def multipart_requestify(params, first=true) - p = Hash.new - - params.each do |key, value| - k = first ? key.to_s : "[#{key}]" - - if Hash === value - multipart_requestify(value, false).each do |subkey, subvalue| - p[k + subkey] = subvalue - end - else - p[k] = value - end - end - - return p - end - - module_function :multipart_requestify - - def multipart_body(params) - multipart_requestify(params).map do |key, value| - if value.respond_to?(:original_filename) - ::File.open(value.path, "rb") do |f| - f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) - - <<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{key}"; filename="#{escape(value.original_filename)}"\r -Content-Type: #{value.content_type}\r -Content-Length: #{::File.stat(value.path).size}\r -\r -#{f.read}\r -EOF - end - else -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{key}"\r -\r -#{value}\r -EOF - end - end.join("")+"--#{MULTIPART_BOUNDARY}--\r" - end - - module_function :multipart_body - - end - - end -end diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 70176a0ea4..3df4f2d6a3 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -21,11 +21,6 @@ # 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' -require 'active_support/core_ext/class/attribute_accessors' - require File.join(File.dirname(__FILE__), "action_pack") module ActionView @@ -36,14 +31,12 @@ module ActionView autoload :Base, 'action_view/base' autoload :Context, 'action_view/context' autoload :Helpers, 'action_view/helpers' - autoload :InlineTemplate, 'action_view/template/inline' 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 :Renderable, 'action_view/template/renderable' - autoload :RenderablePartial, 'action_view/template/partial' autoload :Template, 'action_view/template/template' autoload :TemplateError, 'action_view/template/error' autoload :TemplateHandler, 'action_view/template/handler' @@ -58,3 +51,8 @@ class ERB end 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/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index c71840d41f..95f00cda39 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -1,3 +1,4 @@ +require 'thread' require 'cgi' require 'action_view/helpers/url_helper' require 'action_view/helpers/tag_helper' @@ -286,7 +287,9 @@ module ActionView end javascript_src_tag(joined_javascript_name, options) else - ensure_javascript_sources!(expand_javascript_sources(sources, recursive)).collect { |source| javascript_src_tag(source, options) }.join("\n") + sources = expand_javascript_sources(sources, recursive) + ensure_javascript_sources!(sources) if cache + sources.collect { |source| javascript_src_tag(source, options) }.join("\n") end end @@ -435,7 +438,9 @@ module ActionView end stylesheet_tag(joined_stylesheet_name, options) else - ensure_stylesheet_sources!(expand_stylesheet_sources(sources, recursive)).collect { |source| stylesheet_tag(source, options) }.join("\n") + sources = expand_stylesheet_sources(sources, recursive) + ensure_stylesheet_sources!(sources) if cache + sources.collect { |source| stylesheet_tag(source, options) }.join("\n") end end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 332743d55b..8a7a870b99 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -26,8 +26,10 @@ module ActionView # 47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months - # 1 yr <-> 2 yrs minus 1 secs # => about 1 year - # 2 yrs <-> max time or date # => over [2..X] years + # 1 yr <-> 1 yr, 3 months # => about 1 year + # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year + # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years + # 2 yrs <-> max time or date # => (same rules as 1 yr) # # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds: # 0-4 secs # => less than 5 seconds @@ -43,17 +45,18 @@ module ActionView # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds - # distance_of_time_in_words(from_time, 3.years.from_now) # => over 3 years + # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years # distance_of_time_in_words(from_time, from_time + 60.hours) # => about 3 days # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year - # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years + # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years + # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years # # to_time = Time.now + 6.years + 19.days - # distance_of_time_in_words(from_time, to_time, true) # => over 6 years - # distance_of_time_in_words(to_time, from_time, true) # => over 6 years + # distance_of_time_in_words(from_time, to_time, true) # => about 6 years + # distance_of_time_in_words(to_time, from_time, true) # => about 6 years # distance_of_time_in_words(Time.now, Time.now) # => less than a minute # def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) @@ -81,12 +84,21 @@ module ActionView when 2..44 then locale.t :x_minutes, :count => distance_in_minutes when 45..89 then locale.t :about_x_hours, :count => 1 when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round - when 1440..2879 then locale.t :x_days, :count => 1 - when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round + when 1440..2529 then locale.t :x_days, :count => 1 + when 2530..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round when 43200..86399 then locale.t :about_x_months, :count => 1 - when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round - when 525600..1051199 then locale.t :about_x_years, :count => 1 - else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round + when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round + else + distance_in_years = distance_in_minutes / 525600 + minute_offset_for_leap_year = (distance_in_years / 4) * 1440 + remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600) + if remainder < 131400 + locale.t(:about_x_years, :count => distance_in_years) + elsif remainder < 394200 + locale.t(:over_x_years, :count => distance_in_years) + else + locale.t(:almost_x_years, :count => distance_in_years + 1) + end end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 81029102b1..32b9c4a7dd 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -449,6 +449,15 @@ module ActionView # <% end %> # <% end %> # + # Or a collection to be used: + # + # <% form_for @person, :url => { :action => "update" } do |person_form| %> + # ... + # <% person_form.fields_for :projects, @active_projects do |project_fields| %> + # Name: <%= project_fields.text_field :name %> + # <% end %> + # <% end %> + # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # @@ -1037,18 +1046,21 @@ module ActionView def fields_for_with_nested_attributes(association_name, args, block) name = "#{object_name}[#{association_name}_attributes]" - association = @object.send(association_name) - explicit_object = args.first.to_model if args.first.respond_to?(:to_model) + association = args.first.to_model if args.first.respond_to?(:to_model) + + if association.respond_to?(:new_record?) + association = [association] if @object.send(association_name).is_a?(Array) + elsif !association.is_a?(Array) + association = @object.send(association_name) + end if association.is_a?(Array) - children = explicit_object ? [explicit_object] : association explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) - - children.map do |child| + association.map do |child| fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block) end.join - else - fields_for_nested_model(name, explicit_object || association, args, block) + elsif association + fields_for_nested_model(name, association, args, block) end end diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 897a7cc348..397871b85e 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/big_decimal/conversions' require 'active_support/core_ext/float/rounding' module ActionView diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index ff5a2134ff..7fae0f6b8d 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -106,7 +106,7 @@ module ActionView # escape_once("<< Accept & Checkout") # # => "<< Accept & Checkout" def escape_once(html) - html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } + ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } end private diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index b07304e361..204d4d71e1 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/javascript_helper' +require 'active_support/core_ext/array/access' require 'active_support/core_ext/hash/keys' module ActionView diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index c82cd07ec2..84d94fd700 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -91,6 +91,9 @@ over_x_years: one: "over 1 year" other: "over {{count}} years" + almost_x_years: + one: "almost 1 year" + other: "almost {{count}} years" prompts: year: "Year" month: "Month" diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index b0b75918b7..0cab035ede 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -89,6 +89,7 @@ module ActionView def _render_text(text, layout, options) text = layout.render(self, options[:locals]) { text } if layout + text end # This is the API to render a ViewContext's template from a controller. @@ -105,7 +106,7 @@ module ActionView def _render_template(template, layout = nil, options = {}, partial = nil) logger && logger.info do - msg = "Rendering #{template.identifier}" + msg = "Rendering #{template.inspect}" msg << " (#{options[:status]})" if options[:status] msg end @@ -123,7 +124,7 @@ module ActionView if layout @_layout = layout.identifier - logger.info("Rendering template within #{layout.identifier}") if logger + logger.info("Rendering template within #{layout.inspect}") if logger content = layout.render(self, locals) {|*name| _layout_for(*name) } end content diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index 6e5093c5bd..aa21606f76 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -32,7 +32,7 @@ module ActionView def sub_template_message if @sub_templates "Trace of template inclusion: " + - @sub_templates.collect { |template| template.identifier }.join(", ") + @sub_templates.collect { |template| template.inspect }.join(", ") else "" end diff --git a/actionpack/lib/action_view/template/inline.rb b/actionpack/lib/action_view/template/inline.rb deleted file mode 100644 index 54efa543c8..0000000000 --- a/actionpack/lib/action_view/template/inline.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionView #:nodoc: - class InlineTemplate #:nodoc: - include Renderable - - attr_reader :source, :extension, :method_segment - - def initialize(source, type = nil) - @source = source - @extension = type - @method_segment = "inline_#{@source.hash.abs}" - end - - private - # Always recompile inline templates - def recompile? - true - end - end -end diff --git a/actionpack/lib/action_view/template/partial.rb b/actionpack/lib/action_view/template/partial.rb deleted file mode 100644 index 30dec1dc5b..0000000000 --- a/actionpack/lib/action_view/template/partial.rb +++ /dev/null @@ -1,18 +0,0 @@ -module ActionView - # NOTE: The template that this mixin is being included into is frozen - # so you cannot set or modify any instance variables - module RenderablePartial #:nodoc: - extend ActiveSupport::Memoizable - - def variable_name - name.sub(/\A_/, '').to_sym - end - memoize :variable_name - - def counter_name - "#{variable_name}_counter".to_sym - end - memoize :counter_name - - end -end diff --git a/actionpack/lib/action_view/template/renderable.rb b/actionpack/lib/action_view/template/renderable.rb deleted file mode 100644 index 7687578165..0000000000 --- a/actionpack/lib/action_view/template/renderable.rb +++ /dev/null @@ -1,93 +0,0 @@ -# encoding: utf-8 - -module ActionView - # NOTE: The template that this mixin is being included into is frozen - # so you cannot set or modify any instance variables - module Renderable #:nodoc: - extend ActiveSupport::Memoizable - - def render(view, locals) - compile(locals) - view.send(method_name(locals), locals) {|*args| yield(*args) } - end - - def load! - names = CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) - names.each do |name| - CompiledTemplates.class_eval do - remove_method(name) - end - end - super - end - - private - - def filename - 'compiled-template' - end - - def handler - Template.handler_class_for_extension(extension) - end - memoize :handler - - def compiled_source - handler.call(self) - end - memoize :compiled_source - - def method_name_without_locals - ['_run', extension, method_segment].compact.join('_') - end - memoize :method_name_without_locals - - def method_name(local_assigns) - if local_assigns && local_assigns.any? - method_name = method_name_without_locals.dup - method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}" - else - method_name = method_name_without_locals - end - method_name.to_sym - end - - # Compile and evaluate the template's code (if necessary) - def compile(local_assigns) - render_symbol = method_name(local_assigns) - - if !CompiledTemplates.method_defined?(render_symbol) || recompile? - compile!(render_symbol, local_assigns) - end - end - - private - def compile!(render_symbol, local_assigns) - locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join - - source = <<-end_src - def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} - ensure - self.output_buffer = old_output_buffer - end - end_src - - begin - ActionView::CompiledTemplates.module_eval(source, filename.to_s, 0) - rescue Exception => e # errors from template code - if logger = defined?(ActionController) && Base.logger - logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" - end - - raise ActionView::TemplateError.new(self, {}, e) - end - end - - def recompile? - false - end - end -end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 0b4c62d4d0..f5591ead09 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,9 +1,28 @@ require "pathname" +require "active_support/core_ext/class" require "action_view/template/template" module ActionView # Abstract superclass class Resolver + + class_inheritable_accessor(:registered_details) + self.registered_details = {} + + def self.register_detail(name, options = {}) + registered_details[name] = lambda do |val| + val ||= yield + val |= [nil] unless options[:allow_nil] == false + val + end + end + + register_detail(:locale) { [I18n.locale] } + register_detail(:formats) { Mime::SET.symbols } + register_detail(:handlers, :allow_nil => false) do + TemplateHandlers.extensions + end + def initialize(options = {}) @cache = options[:cache] @cached = {} @@ -11,15 +30,18 @@ module ActionView # Normalizes the arguments and passes it on to find_template def find(*args) - find_all_by_parts(*args).first + find_all(*args).first end - - def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) - details[:locales] = [I18n.locale] - name = name.to_s.gsub(handler_matcher, '').split("/") - find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) + + def find_all(name, details = {}, prefix = nil, partial = nil) + details = normalize_details(details) + name, prefix = normalize_name(name, prefix) + + cached([name, details, prefix, partial]) do + find_templates(name, details, prefix, partial) + end end - + private # This is what child classes implement. No defaults are needed @@ -28,29 +50,26 @@ module ActionView def find_templates(name, details, prefix, partial) raise NotImplementedError end - - def valid_handlers - @valid_handlers ||= TemplateHandlers.extensions - end - def handler_matcher - @handler_matcher ||= begin - e = valid_handlers.join('|') - /\.(?:#{e})$/ + def normalize_details(details) + details = details.dup + # TODO: Refactor this concern out of the resolver + details.delete(:formats) if details[:formats] == [:"*/*"] + registered_details.each do |k, v| + details[k] = v.call(details[k]) end + details end - def handler_glob - @handler_glob ||= begin - e = TemplateHandlers.extensions.map{|h| ".#{h}"}.join(",") - "{#{e}}" - end - end - - def formats_glob - @formats_glob ||= begin - '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' - end + # Support legacy foo.erb names even though we now ignore .erb + # 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('|') + name = name.to_s.gsub(/\.(?:#{handlers})$/, '') + + parts = name.split('/') + return parts.pop, [prefix, *parts].compact.join("/") end def cached(key) @@ -60,80 +79,49 @@ module ActionView end end - class FileSystemResolver < Resolver - - def self.cached_glob - @@cached_glob ||= {} - end - - def initialize(path, options = {}) - raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) - super(options) - @path = Pathname.new(path).expand_path - end + class PathResolver < Resolver - # TODO: This is the currently needed API. Make this suck less - # ==== <suck> - attr_reader :path + EXTENSION_ORDER = [:locale, :formats, :handlers] def to_s - path.to_s + @path.to_s end + alias to_path to_s - def to_str - path.to_s + def find_templates(name, details, prefix, partial) + path = build_path(name, details, prefix, partial) + query(path, EXTENSION_ORDER.map { |ext| details[ext] }) end - def ==(path) - to_str == path.to_str - end + private - def eql?(path) - to_str == path.to_str + def build_path(name, details, prefix, partial) + path = "" + path << "#{prefix}/" unless prefix.empty? + path << (partial ? "_#{name}" : name) + path end - # ==== </suck> - - def find_templates(name, details, prefix, partial, root = "#{@path}/") - if glob = details_to_glob(name, details, prefix, partial, root) - cached(glob) do - Dir[glob].map do |path| - next if File.directory?(path) - source = File.read(path) - identifier = Pathname.new(path).expand_path.to_s - Template.new(source, identifier, *path_to_details(path)) - end.compact - end + def query(path, exts) + query = "#{@path}/#{path}" + exts.each do |ext| + query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}' end - end - - private - # :api: plugin - def details_to_glob(name, details, prefix, partial, root) - self.class.cached_glob[[name, prefix, partial, details, root]] ||= begin - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - - extensions = "" - [:locales, :formats].each do |k| - extensions << if exts = details[k] - '{' + exts.map {|e| ".#{e},"}.join + '}' - else - k == :formats ? formats_glob : '' - end - end - - "#{root}#{path}#{extensions}#{handler_glob}" - end + Dir[query].map do |path| + next if File.directory?(path) + source = File.read(path) + identifier = Pathname.new(path).expand_path.to_s + + Template.new(source, identifier, *path_to_details(path)) + end.compact end - # TODO: fix me - # :api: plugin + # # TODO: fix me + # # :api: plugin def path_to_details(path) # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + if m = path.match(%r'(?:^|/)(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') partial = m[1] == '_' details = (m[2]||"").split('.').reject { |e| e.empty? } handler = Template.handler_class_for_extension(m[3]) @@ -146,13 +134,32 @@ module ActionView end end - class FileSystemResolverWithFallback < FileSystemResolver + class FileSystemResolver < PathResolver + def initialize(path, options = {}) + raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) + super(options) + @path = Pathname.new(path).expand_path + end + end - def find_templates(name, details, prefix, partial) - templates = super - return super(name, details, prefix, partial, '') if templates.empty? - templates + # OMG HAX + # TODO: remove hax + class FileSystemResolverWithFallback < Resolver + def initialize(path, options = {}) + super(options) + @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)] end + def find_templates(*args) + @paths.each do |p| + template = p.find_templates(*args) + return template unless template.empty? + end + [] + end + + def to_s + @paths.first.to_s + end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 7d6964e3e3..0f64c23649 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -8,7 +8,7 @@ module ActionView class Template extend TemplateHandlers attr_reader :source, :identifier, :handler, :mime_type, :formats, :details - + def initialize(source, identifier, handler, details) @source = source @identifier = identifier @@ -25,10 +25,12 @@ module ActionView @formats << :html if format == :js @details[:formats] = Array.wrap(format.to_sym) end - + def render(view, locals, &block) - method_name = compile(locals, view) - view.send(method_name, locals, &block) + ActiveSupport::Orchestra.instrument(:render_template, :identifier => identifier) do + method_name = compile(locals, view) + view.send(method_name, locals, &block) + end.result rescue Exception => e if e.is_a?(TemplateError) e.sub_template_of(self) @@ -37,7 +39,7 @@ module ActionView raise TemplateError.new(self, view.assigns, e) end end - + # TODO: Figure out how to abstract this def variable_name @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym @@ -47,76 +49,83 @@ module ActionView def counter_name @counter_name ||= "#{variable_name}_counter".to_sym end - + # TODO: kill hax def partial? @details[:partial] end - private + def inspect + if defined?(Rails.root) + identifier.sub("#{Rails.root}/", '') + else + identifier + end + end - def compile(locals, view) - method_name = build_method_name(locals) - - return method_name if view.respond_to?(method_name) - - locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join + private + def compile(locals, view) + method_name = build_method_name(locals) - code = @handler.call(self) - if code.sub!(/\A(#.*coding.*)\n/, '') - encoding_comment = $1 - elsif defined?(Encoding) && Encoding.respond_to?(:default_external) - encoding_comment = "#coding:#{Encoding.default_external}" - end + return method_name if view.respond_to?(method_name) - source = <<-end_src - def #{method_name}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{code} - ensure - self.output_buffer = old_output_buffer - end - end_src + locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join - if encoding_comment - source = "#{encoding_comment}\n#{source}" - line = -1 - else - line = 0 - end + code = @handler.call(self) + if code.sub!(/\A(#.*coding.*)\n/, '') + encoding_comment = $1 + elsif defined?(Encoding) && Encoding.respond_to?(:default_external) + encoding_comment = "#coding:#{Encoding.default_external}" + end - begin - ActionView::CompiledTemplates.module_eval(source, identifier, line) - method_name - rescue Exception => e # errors from template code - if logger = (view && view.logger) - logger.debug "ERROR: compiling #{method_name} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" + source = <<-end_src + def #{method_name}(local_assigns) + old_output_buffer = output_buffer;#{locals_code};#{code} + ensure + self.output_buffer = old_output_buffer + end + end_src + + if encoding_comment + source = "#{encoding_comment}\n#{source}" + line = -1 + else + line = 0 end - raise ActionView::TemplateError.new(self, {}, e) + begin + ActionView::CompiledTemplates.module_eval(source, identifier, line) + method_name + rescue Exception => e # errors from template code + if logger = (view && view.logger) + logger.debug "ERROR: compiling #{method_name} RAISED #{e}" + logger.debug "Function body: #{source}" + logger.debug "Backtrace: #{e.backtrace.join("\n")}" + end + + raise ActionView::TemplateError.new(self, {}, e) + end end - end - class LocalsKey - @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + class LocalsKey + @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } - def self.get(*locals) - @hash_keys[*locals] ||= new(klass, format, locale) - end + def self.get(*locals) + @hash_keys[*locals] ||= new(klass, format, locale) + end - attr_accessor :hash - def initialize(klass, format, locale) - @hash = locals.hash - end + attr_accessor :hash + def initialize(klass, format, locale) + @hash = locals.hash + end - alias_method :eql?, :equal? - end + alias_method :eql?, :equal? + end - def build_method_name(locals) - # TODO: is locals.keys.hash reliably the same? - @method_names[locals.keys.hash] ||= - "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") - end + def build_method_name(locals) + # TODO: is locals.keys.hash reliably the same? + @method_names[locals.keys.hash] ||= + "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + end end end diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index 9f12e5e0a8..f7d0df5ba0 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,6 +1,5 @@ module ActionView #:nodoc: class TextTemplate < String #:nodoc: - def initialize(string, content_type = Mime[:html]) super(string.to_s) @content_type = Mime[content_type] || content_type @@ -10,14 +9,28 @@ module ActionView #:nodoc: {:formats => [@content_type.to_sym]} end - def identifier() self end - - def render(*) self end - - def mime_type() @content_type end + def identifier + self + end + + def inspect + 'inline template' + end + + def render(*args) + self + end - def formats() [mime_type] end + def mime_type + @content_type + end - def partial?() false end + def formats + [mime_type] + end + + def partial? + false + end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index c2ccd1d3a5..441f462bc9 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,4 +1,5 @@ require 'active_support/test_case' +require 'action_controller/testing/test_case' module ActionView class Base @@ -23,12 +24,52 @@ module ActionView end class TestCase < ActiveSupport::TestCase + class TestController < ActionController::Base + attr_accessor :request, :response, :params + + def self.controller_path + '' + end + + def initialize + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @params = {} + end + end + include ActionDispatch::Assertions include ActionController::TestProcess include ActionView::Context + include ActionController::PolymorphicRoutes + include ActionController::RecordIdentifier + + include ActionView::Helpers + include ActionController::Helpers + class_inheritable_accessor :helper_class - @@helper_class = nil + attr_accessor :controller, :output_buffer, :rendered + + setup :setup_with_controller + def setup_with_controller + @controller = TestController.new + @output_buffer = '' + @rendered = '' + + self.class.send(:include_helper_modules!) + make_test_case_available_to_view! + end + + def render(options = {}, local_assigns = {}, &block) + @rendered << output = _view.render(options, local_assigns, &block) + output + end + + def protect_against_forgery? + false + end class << self def tests(helper_class) @@ -48,41 +89,75 @@ module ActionView rescue NameError nil end - end - include ActionView::Helpers - include ActionController::PolymorphicRoutes - include ActionController::RecordIdentifier - - setup :setup_with_helper_class - - def setup_with_helper_class - if helper_class && !self.class.ancestors.include?(helper_class) - self.class.send(:include, helper_class) + def helper_method(*methods) + # Almost a duplicate from ActionController::Helpers + methods.flatten.each do |method| + _helpers.module_eval <<-end_eval + def #{method}(*args, &block) # def current_user(*args, &block) + _test_case.send(%(#{method}), *args, &block) # test_case.send(%(current_user), *args, &block) + end # end + end_eval + end end - self.output_buffer = '' + private + def include_helper_modules! + helper(helper_class) if helper_class + include _helpers + end end - class TestController < ActionController::Base - attr_accessor :request, :response, :params + private + def make_test_case_available_to_view! + test_case_instance = self + _helpers.module_eval do + define_method(:_test_case) { test_case_instance } + private :_test_case + end + end - def initialize - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new + def _view + view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller) + view.class.send :include, _helpers + view + end - @params = {} + # Support the selector assertions + # + # Need to experiment if this priority is the best one: rendered => output_buffer + def response_from_page_or_rjs + HTML::Document.new(rendered.blank? ? output_buffer : rendered).root + end + + EXCLUDE_IVARS = %w{ + @output_buffer + @fixture_cache + @method_name + @_result + @loaded_fixtures + @test_passed + @view + } + + def _instance_variables + instance_variables - EXCLUDE_IVARS end - end - protected - attr_accessor :output_buffer + def _assigns + _instance_variables.inject({}) do |hash, var| + name = var[1..-1].to_sym + hash[name] = instance_variable_get(var) + hash + end + end - private def method_missing(selector, *args) - controller = TestController.new - return controller.__send__(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector) - super + if ActionController::Routing::Routes.named_routes.helpers.include?(selector) + @controller.__send__(selector, *args) + else + super + end end end end diff --git a/actionpack/lib/actionpack.rb b/actionpack/lib/actionpack.rb deleted file mode 100644 index 2fe2832f81..0000000000 --- a/actionpack/lib/actionpack.rb +++ /dev/null @@ -1 +0,0 @@ -require 'action_pack' diff --git a/actionpack/test/abstract_controller/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb index 7991436703..524381509d 100644 --- a/actionpack/test/abstract_controller/abstract_controller_test.rb +++ b/actionpack/test/abstract/abstract_controller_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module AbstractController module Testing @@ -148,10 +148,10 @@ module AbstractController private def self.layout(formats) begin - view_paths.find(name.underscore, {:formats => formats}, "layouts") + find_template(name.underscore, {:formats => formats}, :_prefix => "layouts") rescue ActionView::MissingTemplate begin - view_paths.find("application", {:formats => formats}, "layouts") + find_template("application", {:formats => formats}, :_prefix => "layouts") rescue ActionView::MissingTemplate end end diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index 8f62adce8c..0ce1dc506b 100644 --- a/actionpack/test/abstract_controller/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module AbstractController module Testing @@ -235,4 +235,4 @@ module AbstractController end end -end
\ No newline at end of file +end diff --git a/actionpack/test/abstract_controller/helper_test.rb b/actionpack/test/abstract/helper_test.rb index 34a10cecc9..5a363c9aa5 100644 --- a/actionpack/test/abstract_controller/helper_test.rb +++ b/actionpack/test/abstract/helper_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module AbstractController module Testing diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb index 995aac7fad..453d31826e 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract/layouts_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' require 'active_support/core_ext/class/removal' module AbstractControllerTests diff --git a/actionpack/test/abstract_controller/views/abstract_controller/testing/me3/formatted.html.erb b/actionpack/test/abstract/views/abstract_controller/testing/me3/formatted.html.erb index 785bf69191..785bf69191 100644 --- a/actionpack/test/abstract_controller/views/abstract_controller/testing/me3/formatted.html.erb +++ b/actionpack/test/abstract/views/abstract_controller/testing/me3/formatted.html.erb diff --git a/actionpack/test/abstract_controller/views/abstract_controller/testing/me3/index.erb b/actionpack/test/abstract/views/abstract_controller/testing/me3/index.erb index f079ad8204..f079ad8204 100644 --- a/actionpack/test/abstract_controller/views/abstract_controller/testing/me3/index.erb +++ b/actionpack/test/abstract/views/abstract_controller/testing/me3/index.erb diff --git a/actionpack/test/abstract_controller/views/abstract_controller/testing/me4/index.erb b/actionpack/test/abstract/views/abstract_controller/testing/me4/index.erb index 89dce12bdc..89dce12bdc 100644 --- a/actionpack/test/abstract_controller/views/abstract_controller/testing/me4/index.erb +++ b/actionpack/test/abstract/views/abstract_controller/testing/me4/index.erb diff --git a/actionpack/test/abstract_controller/views/abstract_controller/testing/me5/index.erb b/actionpack/test/abstract/views/abstract_controller/testing/me5/index.erb index 84d0b7417e..84d0b7417e 100644 --- a/actionpack/test/abstract_controller/views/abstract_controller/testing/me5/index.erb +++ b/actionpack/test/abstract/views/abstract_controller/testing/me5/index.erb diff --git a/actionpack/test/abstract_controller/views/action_with_ivars.erb b/actionpack/test/abstract/views/action_with_ivars.erb index 8d8ae22fd7..8d8ae22fd7 100644 --- a/actionpack/test/abstract_controller/views/action_with_ivars.erb +++ b/actionpack/test/abstract/views/action_with_ivars.erb diff --git a/actionpack/test/abstract_controller/views/helper_test.erb b/actionpack/test/abstract/views/helper_test.erb index 8ae45cc195..8ae45cc195 100644 --- a/actionpack/test/abstract_controller/views/helper_test.erb +++ b/actionpack/test/abstract/views/helper_test.erb diff --git a/actionpack/test/abstract_controller/views/index.erb b/actionpack/test/abstract/views/index.erb index cc1a8b8c85..cc1a8b8c85 100644 --- a/actionpack/test/abstract_controller/views/index.erb +++ b/actionpack/test/abstract/views/index.erb diff --git a/actionpack/test/abstract_controller/views/layouts/abstract_controller/testing/me4.erb b/actionpack/test/abstract/views/layouts/abstract_controller/testing/me4.erb index 172dd56569..172dd56569 100644 --- a/actionpack/test/abstract_controller/views/layouts/abstract_controller/testing/me4.erb +++ b/actionpack/test/abstract/views/layouts/abstract_controller/testing/me4.erb diff --git a/actionpack/test/abstract_controller/views/layouts/application.erb b/actionpack/test/abstract/views/layouts/application.erb index 27317140ad..27317140ad 100644 --- a/actionpack/test/abstract_controller/views/layouts/application.erb +++ b/actionpack/test/abstract/views/layouts/application.erb diff --git a/actionpack/test/abstract_controller/views/naked_render.erb b/actionpack/test/abstract/views/naked_render.erb index 1b3d03878b..1b3d03878b 100644 --- a/actionpack/test/abstract_controller/views/naked_render.erb +++ b/actionpack/test/abstract/views/naked_render.erb diff --git a/actionpack/test/abstract_controller/test_helper.rb b/actionpack/test/abstract_controller/test_helper.rb deleted file mode 100644 index ba4302d914..0000000000 --- a/actionpack/test/abstract_controller/test_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../../lib') -$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/../lib') - -require 'rubygems' -require 'test/unit' -require 'active_support' -require 'active_support/test_case' -require 'abstract_controller' -require 'action_view' -require 'action_view/base' -require 'action_dispatch' -require 'fixture_template' - -begin - require 'ruby-debug' - Debugger.settings[:autoeval] = true - Debugger.start -rescue LoadError - # Debugging disabled. `gem install ruby-debug` to enable. -end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 07ba37c51c..1214d608a4 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,29 +1,34 @@ $:.unshift(File.dirname(__FILE__) + '/../lib') $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') $:.unshift(File.dirname(__FILE__) + '/../../activemodel/lib') -$:.unshift(File.dirname(__FILE__) + '/lib') +$:.unshift(File.dirname(__FILE__) + '/lib') $:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') $:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') -ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp') +bundler = File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'environment') +require bundler if File.exist?("#{bundler}.rb") + +begin + %w( rack rack/test sqlite3 ).each { |lib| require lib } +rescue LoadError => e + abort e.message +end -ENV['new_base'] = "true" -$stderr.puts "Running old tests on new_base" +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 'action_controller/testing/process' require 'action_view/test_case' -require 'action_controller/testing/integration' require 'active_support/dependencies' -require 'active_model' - -$tags[:new_base] = true begin require 'ruby-debug' @@ -33,6 +38,8 @@ rescue LoadError # Debugging disabled. `gem install ruby-debug` to enable. end +require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late + ActiveSupport::Dependencies.hook! # Show backtraces for deprecated behavior for quicker cleanup. @@ -44,24 +51,127 @@ I18n.backend.store_translations 'pt-BR', {} ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +FIXTURES = Pathname.new(FIXTURE_LOAD_PATH) -module ActionView - class TestCase - setup do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' +module SetupOnce + extend ActiveSupport::Concern + + included do + cattr_accessor :setup_once_block + self.setup_once_block = nil + + setup :run_setup_once + end + + module ClassMethods + def setup_once(&block) + self.setup_once_block = block + end + end + + private + def run_setup_once + if self.setup_once_block + self.setup_once_block.call + self.setup_once_block = nil end end +end + +class ActiveSupport::TestCase + include SetupOnce + + # Hold off drawing routes until all the possible controller classes + # have been loaded. + setup_once do + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + end end end -module ActionController - Base.session = { - :key => '_testing_session', - :secret => '8273f16463985e2b3747dc25e30f2528' - } - Base.session_store = nil +class ActionController::IntegrationTest < ActiveSupport::TestCase + def self.build_app(routes = nil) + ActionDispatch::MiddlewareStack.new { |middleware| + middleware.use "ActionDispatch::ShowExceptions" + middleware.use "ActionDispatch::Callbacks" + middleware.use "ActionDispatch::ParamsParser" + middleware.use "Rack::Head" + }.build(routes || ActionController::Routing::Routes) + end + + self.app = build_app + + def with_routing(&block) + real_routes = ActionController::Routing::Routes + ActionController::Routing.module_eval { remove_const :Routes } + + temporary_routes = ActionController::Routing::RouteSet.new + self.class.app = self.class.build_app(temporary_routes) + 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 + self.class.app = self.class.build_app + end +end + +# Temporary base class +class Rack::TestCase < ActionController::IntegrationTest + setup do + ActionController::Base.session_options[:key] = "abc" + ActionController::Base.session_options[:secret] = ("*" * 30) + end + + def self.testing(klass = nil) + if klass + @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '') + else + @testing + end + end + + def get(thing, *args) + if thing.is_a?(Symbol) + super("#{self.class.testing}/#{thing}", *args) + else + super + end + end + + def assert_body(body) + assert_equal body, Array.wrap(response.body).join + end + + def assert_status(code) + assert_equal code, response.status + end + def assert_response(body, status = 200, headers = {}) + assert_body body + assert_status status + headers.each do |header, value| + assert_header header, value + end + end + + def assert_content_type(type) + assert_equal type, response.headers["Content-Type"] + end + + def assert_header(name, value) + assert_equal value, response.headers[name] + end +end + +class ::ApplicationController < ActionController::Base +end + +module ActionController class << Routing def possible_controllers @@possible_controllers ||= [] @@ -77,18 +187,12 @@ module ActionController super end end - + Base.view_paths = FIXTURE_LOAD_PATH - + class TestCase include TestProcess - setup do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - end - end - def assert_template(options = {}, message = nil) validate_request! diff --git a/actionpack/test/lib/active_record_unit.rb b/actionpack/test/active_record_unit.rb index 1ba308e9d7..9e0c66055d 100644 --- a/actionpack/test/lib/active_record_unit.rb +++ b/actionpack/test/active_record_unit.rb @@ -16,7 +16,7 @@ if defined?(ActiveRecord) && defined?(Fixtures) else $stderr.print 'Attempting to load Active Record... ' begin - PATH_TO_AR = "#{File.dirname(__FILE__)}/../../../activerecord/lib" + 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' @@ -72,13 +72,13 @@ class ActiveRecordTestConnector # Load actionpack sqlite tables def load_schema - File.read(File.dirname(__FILE__) + "/../fixtures/db_definitions/sqlite.sql").split(';').each do |sql| + File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql| ActiveRecord::Base.connection.execute(sql) unless sql.blank? end end def require_fixture_models - Dir.glob(File.dirname(__FILE__) + "/../fixtures/*.rb").each {|f| require f} + Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f} end end end diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index a46ce7a0aa..c6c079f88c 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -33,7 +33,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest def setup ActiveRecord::SessionStore.session_class.create_table! - reset_app! end def teardown @@ -120,21 +119,15 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest reset! - get '/set_session_value', :_session_id => session_id, :foo => "baz" - assert_response :success - assert_equal nil, cookies['_session_id'] - get '/get_session_value', :_session_id => session_id assert_response :success assert_equal 'foo: nil', response.body - assert_equal nil, cookies['_session_id'] + assert_not_equal session_id, cookies['_session_id'] end end def test_allows_session_fixation - with_test_route_set do - reset_with_fixation! - + with_test_route_set(:cookie_only => false) do get '/set_session_value' assert_response :success assert cookies['_session_id'] @@ -145,7 +138,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest session_id = cookies['_session_id'] assert session_id - reset_with_fixation! + reset! get '/set_session_value', :_session_id => session_id, :foo => "baz" assert_response :success @@ -159,23 +152,12 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end private - def reset_app! - app = ActiveRecord::SessionStore.new(ActionController::Dispatcher.new, :key => '_session_id') - @integration_session = open_session(app) - end - - def reset_with_fixation! - app = ActiveRecord::SessionStore.new(ActionController::Dispatcher.new, :key => '_session_id', :cookie_only => false) - @integration_session = open_session(app) - end - - def with_test_route_set + def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| - map.with_options :controller => "active_record_store_test/test" do |c| - c.connect "/:action" - end + map.connect "/:action", :controller => "active_record_store_test/test" end + @app = ActiveRecord::SessionStore.new(set, options.reverse_merge(:key => '_session_id')) yield end end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 453812c128..901cb940ea 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'action_controller/vendor/html-scanner' +require 'controller/fake_controllers' # a controller class to facilitate the tests class ActionPackAssertionsController < ActionController::Base diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb index 2d2a2745b0..c1cd22113d 100644 --- a/actionpack/test/controller/addresses_render_test.rb +++ b/actionpack/test/controller/addresses_render_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'logger' +require 'controller/fake_controllers' class Address def Address.count(conditions = nil, join = nil) @@ -15,13 +16,8 @@ class Address end end -class AddressesTestController < ActionController::Base - def self.controller_name; "addresses"; end - def self.controller_path; "addresses"; end -end - class AddressesTest < ActionController::TestCase - tests AddressesTestController + tests AddressesController def setup super diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 82c790bc19..69b0eb5e3e 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -46,14 +46,8 @@ end class PageCachingTest < ActionController::TestCase def setup super - ActionController::Base.perform_caching = true - ActionController::Routing::Routes.draw do |map| - map.main '', :controller => 'posts', :format => nil - map.formatted_posts 'posts.:format', :controller => 'posts' - map.resources :posts - map.connect ':controller/:action/:id' - end + ActionController::Base.perform_caching = true @request = ActionController::TestRequest.new @request.host = 'hostname.com' @@ -74,10 +68,16 @@ class PageCachingTest < ActionController::TestCase end def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route - @params[:format] = 'rss' - assert_equal '/posts.rss', @rewriter.rewrite(@params) - @params[:format] = nil - assert_equal '/', @rewriter.rewrite(@params) + with_routing do |set| + set.draw do |map| + map.main '', :controller => 'posts', :format => nil + map.formatted_posts 'posts.:format', :controller => 'posts' + end + @params[:format] = 'rss' + assert_equal '/posts.rss', @rewriter.rewrite(@params) + @params[:format] = nil + assert_equal '/', @rewriter.rewrite(@params) + end end def test_should_cache_get_with_ok_status @@ -441,8 +441,8 @@ class ActionCacheTest < ActionController::TestCase def test_correct_content_type_is_returned_for_cache_hit # run it twice to cache it the first time - get :index, :id => 'content-type.xml' - get :index, :id => 'content-type.xml' + get :index, :id => 'content-type', :format => 'xml' + get :index, :id => 'content-type', :format => 'xml' assert_equal 'application/xml', @response.content_type end @@ -625,15 +625,19 @@ class FragmentCachingTest < ActionController::TestCase def test_fragment_for_logging fragment_computed = false - @controller.class.expects(:benchmark).with('Cached fragment exists?: views/expensive') - @controller.class.expects(:benchmark).with('Cached fragment miss: views/expensive') - @controller.class.expects(:benchmark).with('Cached fragment hit: views/expensive').never + listener = [] + ActiveSupport::Orchestra.register listener buffer = 'generated till now -> ' @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } + assert_equal 1, listener.count { |e| e.name == :fragment_exist? } + assert_equal 1, listener.count { |e| e.name == :write_fragment } + assert fragment_computed assert_equal 'generated till now -> ', buffer + ensure + ActiveSupport::Orchestra.unregister listener end end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index c249788c67..e5ffe20ecc 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class ContentTypeController < ActionController::Base +class OldContentTypeController < ActionController::Base # :ported: def render_content_type_from_body response.content_type = Mime::RSS @@ -56,7 +56,7 @@ class ContentTypeController < ActionController::Base end class ContentTypeTest < ActionController::TestCase - tests ContentTypeController + tests OldContentTypeController def setup super @@ -73,11 +73,11 @@ class ContentTypeTest < ActionController::TestCase end def test_render_changed_charset_default - ContentTypeController.default_charset = "utf-16" + OldContentTypeController.default_charset = "utf-16" get :render_defaults assert_equal "utf-16", @response.charset assert_equal Mime::HTML, @response.content_type - ContentTypeController.default_charset = "utf-8" + OldContentTypeController.default_charset = "utf-8" end # :ported: @@ -109,12 +109,12 @@ class ContentTypeTest < ActionController::TestCase end def test_nil_default_for_rhtml - ContentTypeController.default_charset = nil + OldContentTypeController.default_charset = nil get :render_default_for_rhtml assert_equal Mime::HTML, @response.content_type assert_nil @response.charset, @response.headers.inspect ensure - ContentTypeController.default_charset = "utf-8" + OldContentTypeController.default_charset = "utf-8" end def test_default_for_rhtml @@ -143,8 +143,7 @@ class ContentTypeTest < ActionController::TestCase end class AcceptBasedContentTypeTest < ActionController::TestCase - - tests ContentTypeController + tests OldContentTypeController def setup super diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 9fae1fcf63..622d67287d 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -3,23 +3,23 @@ require 'abstract_unit' class DispatcherTest < Test::Unit::TestCase Dispatcher = ActionController::Dispatcher + class Foo + cattr_accessor :a, :b + end + def setup ENV['REQUEST_METHOD'] = 'GET' # Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks - ActionDispatch::Callbacks.instance_variable_set("@prepare_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - ActionDispatch::Callbacks.instance_variable_set("@before_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - ActionDispatch::Callbacks.instance_variable_set("@after_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + ActionDispatch::Callbacks.reset_callbacks(:prepare) + ActionDispatch::Callbacks.reset_callbacks(:call) - @old_router, Dispatcher.router = Dispatcher.router, mock() - Dispatcher.router.stubs(:call).returns([200, {}, 'response']) - Dispatcher.router.stubs(:reload) + ActionController::Routing::Routes.stubs(:call).returns([200, {}, 'response']) + ActionController::Routing::Routes.stubs(:reload) Dispatcher.stubs(:require_dependency) end def teardown - Dispatcher.router = @old_router - @dispatcher = nil ENV.delete 'REQUEST_METHOD' end @@ -29,27 +29,22 @@ class DispatcherTest < Test::Unit::TestCase end def test_reloads_routes_before_dispatch_if_in_loading_mode - Dispatcher.router.expects(:reload).once + ActionController::Routing::Routes.expects(:reload).once dispatch(false) end def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode - Dispatcher.router.expects(:reload).never + ActionController::Routing::Routes.expects(:reload).never ActiveSupport::Dependencies.expects(:clear).never dispatch end - # Stub out dispatch error logger - class << Dispatcher - def log_failsafe_exception(status, exception); end - end - def test_prepare_callbacks a = b = c = nil - Dispatcher.to_prepare { |*args| a = b = c = 1 } - Dispatcher.to_prepare { |*args| b = c = 2 } - Dispatcher.to_prepare { |*args| c = 3 } + ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 } + ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 } + ActionDispatch::Callbacks.to_prepare { |*args| c = 3 } # Ensure to_prepare callbacks are not run when defined assert_nil a || b || c @@ -68,25 +63,20 @@ class DispatcherTest < Test::Unit::TestCase end def test_to_prepare_with_identifier_replaces - a = b = nil - Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 } - Dispatcher.to_prepare(:unique_id) { |*args| a = 2 } + ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a, Foo.b = 1, 1 } + ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a = 2 } dispatch - assert_equal 2, a - assert_equal nil, b + assert_equal 2, Foo.a + assert_equal nil, Foo.b end private def dispatch(cache_classes = true) ActionController::Dispatcher.prepare_each_request = false Dispatcher.define_dispatcher_callbacks(cache_classes) - Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware| - middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb")) - middleware.instance_eval(File.read(middlewares)) - end - @dispatcher ||= Dispatcher.new + @dispatcher ||= ActionDispatch::Callbacks.new(ActionController::Routing::Routes) @dispatcher.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false}) end diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index f7864745eb..19232c6bc9 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -35,6 +35,7 @@ class FilterParamTest < ActionController::TestCase test_hashes = [[{},{},[]], [{'foo'=>nil},{'foo'=>nil},[]], [{'foo'=>'bar'},{'foo'=>'bar'},[]], + [{'foo'=>1},{'foo'=>1},[]], [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'], [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'], diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 197ba0c69c..508364d0b5 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'controller/fake_controllers' require 'action_controller/vendor/html-scanner' class SessionTest < Test::Unit::TestCase @@ -363,20 +364,22 @@ class IntegrationProcessTest < ActionController::IntegrationTest end end + def test_generate_url_with_controller + assert_equal 'http://www.example.com/foo', url_for(:controller => "foo") + end + private def with_test_route_set with_routing do |set| set.draw do |map| - map.with_options :controller => "IntegrationProcessTest::Integration" do |c| - c.connect "/:action" - end + map.connect "/:action", :controller => "integration_process_test/integration" end yield end end end -class MetalTest < ActionController::IntegrationTest +class MetalIntegrationTest < ActionController::IntegrationTest class Poller def self.call(env) if env["PATH_INFO"] =~ /^\/success/ @@ -388,7 +391,7 @@ class MetalTest < ActionController::IntegrationTest end def setup - @integration_session = ActionController::Integration::Session.new(Poller) + @app = Poller end def test_successful_get @@ -405,4 +408,8 @@ class MetalTest < ActionController::IntegrationTest assert_response :not_found assert_equal '', response.body end + + def test_generate_url_without_controller + assert_equal 'http://www.example.com/foo', url_for(:controller => "foo") + end end diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb index 98ffbc3813..2b5e8d8bde 100644 --- a/actionpack/test/controller/logging_test.rb +++ b/actionpack/test/controller/logging_test.rb @@ -12,11 +12,11 @@ class LoggingTest < ActionController::TestCase class MockLogger attr_reader :logged attr_accessor :level - + def initialize @level = Logger::DEBUG end - + def method_missing(method, *args, &blk) @logged ||= [] @logged << args.first @@ -31,25 +31,24 @@ class LoggingTest < ActionController::TestCase def test_logging_without_parameters get :show - assert_equal 2, logs.size + assert_equal 3, logs.size assert_nil logs.detect {|l| l =~ /Parameters/ } end def test_logging_with_parameters get :show, :id => '10' - assert_equal 3, logs.size + assert_equal 4, logs.size params = logs.detect {|l| l =~ /Parameters/ } assert_equal 'Parameters: {"id"=>"10"}', params end - + private + def set_logger + @controller.logger = MockLogger.new + end - def set_logger - @controller.logger = MockLogger.new - end - - def logs - @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip} - end + def logs + @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip} + end end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 44536ce54e..a79648396c 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -527,12 +527,6 @@ class RespondWithControllerTest < ActionController::TestCase super ActionController::Base.use_accept_header = true @request.host = "www.example.com" - - ActionController::Routing::Routes.draw do |map| - map.resources :customers - map.resources :quiz_stores, :has_many => :customers - map.connect ":controller/:action/:id" - end end def teardown @@ -593,53 +587,59 @@ class RespondWithControllerTest < ActionController::TestCase end def test_using_resource_for_post_with_html - 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? - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "New world!\n", @response.body - assert_nil @response.location + 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? + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "text/html", @response.content_type + assert_equal 200, @response.status + assert_equal "New world!\n", @response.body + assert_nil @response.location + end end def test_using_resource_for_post_with_xml - @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 - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location + 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 + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 422, @response.status + assert_equal errors.to_xml, @response.body + assert_nil @response.location + end end def test_using_resource_for_put_with_html - 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? - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location + 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? + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + put :using_resource + assert_equal "text/html", @response.content_type + assert_equal 200, @response.status + assert_equal "Edit world!\n", @response.body + assert_nil @response.location + end end def test_using_resource_for_put_with_xml @@ -660,11 +660,13 @@ class RespondWithControllerTest < ActionController::TestCase end def test_using_resource_for_delete_with_html - Customer.any_instance.stubs(:destroyed?).returns(true) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location + with_test_route_set do + Customer.any_instance.stubs(:destroyed?).returns(true) + delete :using_resource + assert_equal "text/html", @response.content_type + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers", @response.location + end end def test_using_resource_for_delete_with_xml @@ -685,21 +687,23 @@ class RespondWithControllerTest < ActionController::TestCase end def test_using_resource_with_parent_for_post - @request.accept = "application/xml" - - post :using_resource_with_parent - 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/quiz_stores/11/customers/13", @response.location - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location + with_test_route_set do + @request.accept = "application/xml" + + post :using_resource_with_parent + 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/quiz_stores/11/customers/13", @response.location + + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + post :using_resource + assert_equal "application/xml", @response.content_type + assert_equal 422, @response.status + assert_equal errors.to_xml, @response.body + assert_nil @response.location + end end def test_using_resource_with_collection @@ -726,6 +730,13 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "<name>david</name>", @response.body end + def test_block_inside_respond_with_is_rendered + @controller = InheritedRespondWithController.new + @request.accept = "application/json" + get :index + assert_equal "JSON", @response.body + end + def test_no_double_render_is_raised @request.accept = "text/html" assert_raise ActionView::MissingTemplate do @@ -766,6 +777,18 @@ class RespondWithControllerTest < ActionController::TestCase get :default_overwritten assert_equal 406, @response.status end + + private + 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" + end + yield + end + end end class AbstractPostController < ActionController::Base diff --git a/actionpack/test/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index 1b2e917ced..1f9bf7f0fb 100644 --- a/actionpack/test/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' # Tests the controller dispatching happy path module Dispatching @@ -26,7 +26,7 @@ module Dispatching class ContainedEmptyController < ActionController::Base ; end end - class BaseTest < SimpleRouteCase + class BaseTest < Rack::TestCase # :api: plugin test "simple dispatching" do get "/dispatching/simple/index" @@ -65,4 +65,4 @@ module Dispatching assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name end end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/new_base/content_negotiation_test.rb b/actionpack/test/controller/new_base/content_negotiation_test.rb new file mode 100644 index 0000000000..7b38a82f51 --- /dev/null +++ b/actionpack/test/controller/new_base/content_negotiation_test.rb @@ -0,0 +1,18 @@ +require 'abstract_unit' + +module ContentNegotiation + + # This has no layout and it works + class BasicController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new( + "content_negotiation/basic/hello.html.erb" => "Hello world <%= request.formats %>!" + )] + end + + class TestContentNegotiation < Rack::TestCase + test "A */* Accept header will return HTML" do + get "/content_negotiation/basic/hello", {}, "HTTP_ACCEPT" => "*/*" + assert_body "Hello world */*!" + end + end +end diff --git a/actionpack/test/new_base/content_type_test.rb b/actionpack/test/controller/new_base/content_type_test.rb index ceee508224..0ff5552b08 100644 --- a/actionpack/test/new_base/content_type_test.rb +++ b/actionpack/test/controller/new_base/content_type_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module ContentType class BaseController < ActionController::Base @@ -44,7 +44,7 @@ module ContentType end end - class ExplicitContentTypeTest < SimpleRouteCase + class ExplicitContentTypeTest < Rack::TestCase test "default response is HTML and UTF8" do get "/content_type/base" @@ -67,7 +67,7 @@ module ContentType end end - class ImpliedContentTypeTest < SimpleRouteCase + class ImpliedContentTypeTest < Rack::TestCase test "sets Content-Type as text/html when rendering *.html.erb" do get "/content_type/implied/i_am_html_erb" @@ -93,7 +93,7 @@ module ContentType end end - class ExplicitCharsetTest < SimpleRouteCase + class ExplicitCharsetTest < Rack::TestCase test "setting the charset of the response directly on the response object" do get "/content_type/charset/set_on_response_obj" diff --git a/actionpack/test/new_base/etag_test.rb b/actionpack/test/controller/new_base/etag_test.rb index 3a69e7dac4..51bfb2278a 100644 --- a/actionpack/test/new_base/etag_test.rb +++ b/actionpack/test/controller/new_base/etag_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module Etags class BasicController < ActionController::Base @@ -16,7 +16,7 @@ module Etags end end - class EtagTest < SimpleRouteCase + class EtagTest < Rack::TestCase describe "Rendering without any special etag options returns an etag that is an MD5 hash of its text" test "an action without a layout" do @@ -43,4 +43,4 @@ module Etags %("#{Digest::MD5.hexdigest(text)}") end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/metal_test.rb b/actionpack/test/controller/new_base/metal_test.rb index 2b7720863a..e1d46b906e 100644 --- a/actionpack/test/new_base/metal_test.rb +++ b/actionpack/test/controller/new_base/metal_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module MetalTest class MetalMiddleware < ActionController::Middleware @@ -41,4 +41,3 @@ module MetalTest end end end - diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb new file mode 100644 index 0000000000..ada0215b1a --- /dev/null +++ b/actionpack/test/controller/new_base/middleware_test.rb @@ -0,0 +1,77 @@ +require 'abstract_unit' + +module MiddlewareTest + class MyMiddleware + def initialize(app) + @app = app + end + + def call(env) + result = @app.call(env) + result[1]["Middleware-Test"] = "Success" + result[1]["Middleware-Order"] = "First" + result + end + end + + class ExclaimerMiddleware + def initialize(app) + @app = app + end + + def call(env) + result = @app.call(env) + result[1]["Middleware-Order"] << "!" + result + end + end + + class MyController < ActionController::Metal + use MyMiddleware + + middleware.insert_before MyMiddleware, ExclaimerMiddleware + + def index + self.response_body = "Hello World" + end + end + + class InheritedController < MyController + end + + module MiddlewareTests + extend ActiveSupport::Testing::Declarative + + test "middleware that is 'use'd is called as part of the Rack application" do + result = @app.call(env_for("/")) + assert_equal "Hello World", result[2] + assert_equal "Success", result[1]["Middleware-Test"] + end + + test "the middleware stack is exposed as 'middleware' in the controller" do + result = @app.call(env_for("/")) + assert_equal "First!", result[1]["Middleware-Order"] + end + end + + class TestMiddleware < ActiveSupport::TestCase + include MiddlewareTests + + def setup + @app = MyController.action(:index) + end + + def env_for(url) + Rack::MockRequest.env_for(url) + end + end + + class TestInheritedMiddleware < TestMiddleware + def setup + @app = InheritedController.action(:index) + end + + test "middleware inherits" do + end + end +end diff --git a/actionpack/test/new_base/render_action_test.rb b/actionpack/test/controller/new_base/render_action_test.rb index dfa7cc2141..ecd29c4530 100644 --- a/actionpack/test/new_base/render_action_test.rb +++ b/actionpack/test/controller/new_base/render_action_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderAction # This has no layout and it works @@ -45,7 +45,7 @@ module RenderAction end - class RenderActionTest < SimpleRouteCase + class RenderActionTest < Rack::TestCase test "rendering an action using :action => <String>" do get "/render_action/basic/hello_world" @@ -82,7 +82,7 @@ module RenderAction end end - class RenderLayoutTest < SimpleRouteCase + class RenderLayoutTest < Rack::TestCase describe "Both <controller_path>.html.erb and application.html.erb are missing" test "rendering with layout => true" do @@ -150,7 +150,7 @@ module RenderActionWithApplicationLayout end end - class LayoutTest < SimpleRouteCase + class LayoutTest < Rack::TestCase describe "Only application.html.erb is present and <controller_path>.html.erb is missing" test "rendering implicit application.html.erb as layout" do @@ -189,7 +189,7 @@ module RenderActionWithApplicationLayout end end - class TestLayout < SimpleRouteCase + class TestLayout < Rack::TestCase testing BasicController test "builder works with layouts" do @@ -228,7 +228,7 @@ module RenderActionWithControllerLayout end end - class ControllerLayoutTest < SimpleRouteCase + class ControllerLayoutTest < Rack::TestCase describe "Only <controller_path>.html.erb is present and application.html.erb is missing" test "render hello_world and implicitly use <controller_path>.html.erb as a layout." do @@ -286,7 +286,7 @@ module RenderActionWithBothLayouts end end - class ControllerLayoutTest < SimpleRouteCase + class ControllerLayoutTest < Rack::TestCase describe "Both <controller_path>.html.erb and application.html.erb are present" test "rendering implicitly use <controller_path>.html.erb over application.html.erb as a layout" do @@ -317,4 +317,4 @@ module RenderActionWithBothLayouts assert_status 200 end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index 769949be0c..8b2fdf8f96 100644 --- a/actionpack/test/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -1,110 +1,99 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderFile - class BasicController < ActionController::Base - self.view_paths = "." - + self.view_paths = File.dirname(__FILE__) + def index - render :file => File.join(File.dirname(__FILE__), *%w[.. fixtures test hello_world]) + render :file => File.join(File.dirname(__FILE__), *%w[.. .. fixtures test hello_world]) end - + def with_instance_variables @secret = 'in the sauce' - render :file => File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb') + render :file => File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar.erb') end - + def without_file_key - render File.join(File.dirname(__FILE__), *%w[.. fixtures test hello_world]) + render File.join(File.dirname(__FILE__), *%w[.. .. fixtures test hello_world]) end - + def without_file_key_with_instance_variable @secret = 'in the sauce' - render File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb') + render File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar.erb') end - + def relative_path @secret = 'in the sauce' - render :file => '../fixtures/test/render_file_with_ivar' + render :file => '../../fixtures/test/render_file_with_ivar' end - + def relative_path_with_dot @secret = 'in the sauce' - render :file => '../fixtures/test/dot.directory/render_file_with_ivar' + render :file => '../../fixtures/test/dot.directory/render_file_with_ivar' end - + def pathname @secret = 'in the sauce' - render :file => Pathname.new(File.dirname(__FILE__)).join(*%w[.. fixtures test dot.directory render_file_with_ivar.erb]) + render :file => Pathname.new(File.dirname(__FILE__)).join(*%w[.. .. fixtures test dot.directory render_file_with_ivar.erb]) end - + def with_locals - path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb') + path = File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_locals.erb') render :file => path, :locals => {:secret => 'in the sauce'} end - + def without_file_key_with_locals - path = File.expand_path('../fixtures/test/render_file_with_locals.erb') + path = FIXTURES.join('test/render_file_with_locals.erb').to_s render path, :locals => {:secret => 'in the sauce'} end end - - class TestBasic < SimpleRouteCase + + class TestBasic < Rack::TestCase testing RenderFile::BasicController - - def setup - @old_pwd = Dir.pwd - Dir.chdir(File.dirname(__FILE__)) - end - - def teardown - Dir.chdir(@old_pwd) - end - + test "rendering simple template" do get :index assert_response "Hello world!" end - + test "rendering template with ivar" do get :with_instance_variables assert_response "The secret is in the sauce\n" end - + test "rendering path without specifying the :file key" do get :without_file_key assert_response "Hello world!" end - + test "rendering path without specifying the :file key with ivar" do get :without_file_key_with_instance_variable assert_response "The secret is in the sauce\n" end - + test "rendering a relative path" do get :relative_path assert_response "The secret is in the sauce\n" end - + test "rendering a relative path with dot" do get :relative_path_with_dot assert_response "The secret is in the sauce\n" end - + test "rendering a Pathname" do get :pathname assert_response "The secret is in the sauce\n" end - + test "rendering file with locals" do get :with_locals assert_response "The secret is in the sauce\n" end - + test "rendering path without specifying the :file key with locals" do get :without_file_key_with_locals assert_response "The secret is in the sauce\n" end end - -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index fd96e1955f..90cc7933ff 100644 --- a/actionpack/test/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderImplicitAction class SimpleController < ::ApplicationController @@ -10,7 +10,7 @@ module RenderImplicitAction def hello_world() end end - class RenderImplicitActionTest < SimpleRouteCase + class RenderImplicitActionTest < Rack::TestCase test "render a simple action with new explicit call to render" do get "/render_implicit_action/simple/hello_world" @@ -25,4 +25,4 @@ module RenderImplicitAction assert_status 200 end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_layout_test.rb b/actionpack/test/controller/new_base/render_layout_test.rb index 933eef58e7..6a9668b81a 100644 --- a/actionpack/test/new_base/render_layout_test.rb +++ b/actionpack/test/controller/new_base/render_layout_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module ControllerLayouts class ImplicitController < ::ApplicationController @@ -36,7 +36,7 @@ module ControllerLayouts end end - class RenderLayoutTest < SimpleRouteCase + class RenderLayoutTest < Rack::TestCase test "rendering a normal template, but using the implicit layout" do get "/controller_layouts/implicit/index" @@ -58,7 +58,7 @@ module ControllerLayouts end - class LayoutOptionsTest < SimpleRouteCase + class LayoutOptionsTest < Rack::TestCase testing ControllerLayouts::ImplicitController test "rendering with :layout => false leaves out the implicit layout" do @@ -79,7 +79,7 @@ module ControllerLayouts end end - class MismatchFormatTest < SimpleRouteCase + class MismatchFormatTest < Rack::TestCase testing ControllerLayouts::MismatchFormatController test "if JS is selected, an HTML template is not also selected" do @@ -98,4 +98,4 @@ module ControllerLayouts end end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb index bbb98a0c01..8fddcbcd57 100644 --- a/actionpack/test/new_base/render_partial_test.rb +++ b/actionpack/test/controller/new_base/render_partial_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderPartial @@ -15,7 +15,7 @@ module RenderPartial end end - class TestPartial < SimpleRouteCase + class TestPartial < Rack::TestCase testing BasicController test "rendering a partial in ActionView doesn't pull the ivars again from the controller" do @@ -24,4 +24,4 @@ module RenderPartial end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_rjs_test.rb b/actionpack/test/controller/new_base/render_rjs_test.rb index 3d3e516905..8c47b38ab6 100644 --- a/actionpack/test/new_base/render_rjs_test.rb +++ b/actionpack/test/controller/new_base/render_rjs_test.rb @@ -1,9 +1,7 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderRjs - class BasicController < ActionController::Base - self.view_paths = [ActionView::FixtureResolver.new( "render_rjs/basic/index.js.rjs" => "page[:customer].replace_html render(:partial => 'customer')", "render_rjs/basic/index_html.js.rjs" => "page[:customer].replace_html :partial => 'customer'", @@ -23,9 +21,17 @@ module RenderRjs end end - class TestBasic < SimpleRouteCase + class TestBasic < Rack::TestCase testing BasicController + def setup + @old_locale = I18n.locale + end + + def teardown + I18n.locale = @old_locale + end + test "rendering a partial in an RJS template should pick the JS template over the HTML one" do get :index, "format" => "js" assert_response("$(\"customer\").update(\"JS Partial\");") @@ -40,6 +46,5 @@ module RenderRjs get :index_locale, "format" => "js" assert_response("$(\"customer\").update(\"Danish HTML Partial\");") end - end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 967cbd07b0..c81b951c0d 100644 --- a/actionpack/test/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderTemplate class WithoutLayoutController < ActionController::Base @@ -39,7 +39,7 @@ module RenderTemplate end end - class TestWithoutLayout < SimpleRouteCase + class TestWithoutLayout < Rack::TestCase testing RenderTemplate::WithoutLayoutController test "rendering a normal template with full path without layout" do @@ -107,7 +107,7 @@ module RenderTemplate end end - class TestWithLayout < SimpleRouteCase + class TestWithLayout < Rack::TestCase describe "Rendering with :template using implicit or explicit layout" test "rendering with implicit layout" do @@ -158,7 +158,7 @@ module RenderTemplate end end - class TestTemplateRenderWithForwardSlash < SimpleRouteCase + class TestTemplateRenderWithForwardSlash < Rack::TestCase test "rendering a normal template with full path starting with a leading slash" do get "/render_template/compatibility/without_layout/with_forward_slash" @@ -167,4 +167,4 @@ module RenderTemplate end end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb index 5783b4766a..d985d9f9ad 100644 --- a/actionpack/test/new_base/render_test.rb +++ b/actionpack/test/controller/new_base/render_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module Render class BlankRenderController < ActionController::Base @@ -35,7 +35,7 @@ module Render end end - class RenderTest < SimpleRouteCase + class RenderTest < Rack::TestCase test "render with blank" do get "/render/blank_render" @@ -50,7 +50,7 @@ module Render end end - class TestOnlyRenderPublicActions < SimpleRouteCase + class TestOnlyRenderPublicActions < Rack::TestCase describe "Only public methods on actual controllers are callable actions" test "raises an exception when a method of Object is called" do @@ -66,7 +66,7 @@ module Render end end - class TestVariousObjectsAvailableInView < SimpleRouteCase + class TestVariousObjectsAvailableInView < Rack::TestCase test "The request object is accessible in the view" do get "/render/blank_render/access_request" assert_body "The request: GET" @@ -82,4 +82,4 @@ module Render assert_body "Controller Name: blank_render" end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb index 84f77432c9..0e6f51c998 100644 --- a/actionpack/test/new_base/render_text_test.rb +++ b/actionpack/test/controller/new_base/render_text_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderText class SimpleController < ActionController::Base @@ -62,7 +62,7 @@ module RenderText end end - class RenderTextTest < SimpleRouteCase + class RenderTextTest < Rack::TestCase describe "Rendering text using render :text" test "rendering text from a action with default options renders the text with the layout" do @@ -134,4 +134,4 @@ module RenderText assert_status 200 end end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/render_xml_test.rb b/actionpack/test/controller/new_base/render_xml_test.rb index a3890ddfb2..d044738a78 100644 --- a/actionpack/test/new_base/render_xml_test.rb +++ b/actionpack/test/controller/new_base/render_xml_test.rb @@ -1,4 +1,4 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'abstract_unit' module RenderXml @@ -8,4 +8,4 @@ module RenderXml "render_xml/basic/with_render_erb" => "Hello world!" )] end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index bc850de733..491c98a0fd 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -2,23 +2,27 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' -class TestController < ActionController::Base - protect_from_forgery +class RenderJSTest < ActionController::TestCase + class TestController < ActionController::Base + protect_from_forgery - def render_vanilla_js_hello - render :js => "alert('hello')" - end - - def greeting - # let's just rely on the template + def self.controller_path + 'test' + end + + def render_vanilla_js_hello + render :js => "alert('hello')" + end + + def greeting + # let's just rely on the template + end + + def show_partial + render :partial => 'partial' + end end - - def show_partial - render :partial => 'partial' - end -end -class RenderTest < ActionController::TestCase tests TestController def test_render_vanilla_js @@ -26,14 +30,14 @@ class RenderTest < ActionController::TestCase assert_equal "alert('hello')", @response.body assert_equal "text/javascript", @response.content_type end - + def test_render_with_default_from_accept_header xhr :get, :greeting assert_equal "$(\"body\").visualEffect(\"highlight\");", @response.body end - + def test_should_render_js_partial xhr :get, :show_partial, :format => 'js' assert_equal 'partial js', @response.body end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index 233b2dfd89..3938fc7061 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -2,35 +2,39 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' -class TestController < ActionController::Base - protect_from_forgery - - def render_json_nil - render :json => nil - end +class RenderJsonTest < ActionController::TestCase + class TestController < ActionController::Base + protect_from_forgery - def render_json_hello_world - render :json => ActiveSupport::JSON.encode(:hello => 'world') - end + def self.controller_path + 'test' + end - def render_json_hello_world_with_callback - render :json => ActiveSupport::JSON.encode(:hello => 'world'), :callback => 'alert' - end + def render_json_nil + render :json => nil + end - def render_json_with_custom_content_type - render :json => ActiveSupport::JSON.encode(:hello => 'world'), :content_type => 'text/javascript' - end + def render_json_hello_world + render :json => ActiveSupport::JSON.encode(:hello => 'world') + end - def render_symbol_json - render :json => ActiveSupport::JSON.encode(:hello => 'world') - end + def render_json_hello_world_with_callback + render :json => ActiveSupport::JSON.encode(:hello => 'world'), :callback => 'alert' + end - def render_json_with_render_to_string - render :json => {:hello => render_to_string(:partial => 'partial')} - end -end + def render_json_with_custom_content_type + render :json => ActiveSupport::JSON.encode(:hello => 'world'), :content_type => 'text/javascript' + end + + def render_symbol_json + render :json => ActiveSupport::JSON.encode(:hello => 'world') + end + + def render_json_with_render_to_string + render :json => {:hello => render_to_string(:partial => 'partial')} + end + end -class RenderTest < ActionController::TestCase tests TestController def setup @@ -40,8 +44,8 @@ class RenderTest < ActionController::TestCase @controller.logger = Logger.new(nil) @request.host = "www.nextangle.com" - end - + end + def test_render_json_nil get :render_json_nil assert_equal 'null', @response.body @@ -76,5 +80,5 @@ class RenderTest < ActionController::TestCase get :render_json_with_render_to_string assert_equal '{"hello":"partial html"}', @response.body assert_equal 'application/json', @response.content_type - end -end
\ No newline at end of file + end +end diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index 05645e47fa..51c3c55545 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -2,139 +2,144 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' -class TestController < ActionController::Base - protect_from_forgery - layout :determine_layout +class RenderOtherTest < ActionController::TestCase + class TestController < ActionController::Base + protect_from_forgery - module RenderTestHelper - def rjs_helper_method_from_module - page.visual_effect :highlight + def self.controller_path + 'test' end - end - helper RenderTestHelper - helper do - def rjs_helper_method(value) - page.visual_effect :highlight, value + layout :determine_layout + + module RenderTestHelper + def rjs_helper_method_from_module + page.visual_effect :highlight + end end - end - def enum_rjs_test - render :update do |page| - page.select('.product').each do |value| - page.rjs_helper_method_from_module - page.rjs_helper_method(value) - page.sortable(value, :url => { :action => "order" }) - page.draggable(value) + helper RenderTestHelper + helper do + def rjs_helper_method(value) + page.visual_effect :highlight, value end end - end - - def render_explicit_html_template - end - - def render_custom_code_rjs - render :update, :status => 404 do |page| - page.replace :foo, :partial => 'partial' + + def enum_rjs_test + render :update do |page| + page.select('.product').each do |value| + page.rjs_helper_method_from_module + page.rjs_helper_method(value) + page.sortable(value, :url => { :action => "order" }) + page.draggable(value) + end + end end - end - - def render_implicit_html_template - end - - def render_js_with_explicit_template - @project_id = 4 - render :template => 'test/delete_with_js' - end - def render_js_with_explicit_action_template - @project_id = 4 - render :action => 'delete_with_js' - end - - def delete_with_js - @project_id = 4 - end - - def update_page - render :update do |page| - page.replace_html 'balance', '$37,000,000.00' - page.visual_effect :highlight, 'balance' + def render_explicit_html_template + end + + def render_custom_code_rjs + render :update, :status => 404 do |page| + page.replace :foo, :partial => 'partial' + end end - end - def update_page_with_instance_variables - @money = '$37,000,000.00' - @div_id = 'balance' - render :update do |page| - page.replace_html @div_id, @money - page.visual_effect :highlight, @div_id + def render_implicit_html_template end - end - def update_page_with_view_method - render :update do |page| - page.replace_html 'person', pluralize(2, 'person') + def render_js_with_explicit_template + @project_id = 4 + render :template => 'test/delete_with_js' end - end - - def partial_as_rjs - render :update do |page| - page.replace :foo, :partial => 'partial' + + def render_js_with_explicit_action_template + @project_id = 4 + render :action => 'delete_with_js' end - end - def respond_to_partial_as_rjs - respond_to do |format| - format.js do - render :update do |page| - page.replace :foo, :partial => 'partial' - end + def delete_with_js + @project_id = 4 + end + + def update_page + render :update do |page| + page.replace_html 'balance', '$37,000,000.00' + page.visual_effect :highlight, 'balance' end end - end - - def render_alternate_default - # For this test, the method "default_render" is overridden: - @alternate_default_render = lambda do + + def update_page_with_instance_variables + @money = '$37,000,000.00' + @div_id = 'balance' + render :update do |page| + page.replace_html @div_id, @money + page.visual_effect :highlight, @div_id + end + end + + def update_page_with_view_method + render :update do |page| + page.replace_html 'person', pluralize(2, 'person') + end + end + + def partial_as_rjs render :update do |page| page.replace :foo, :partial => 'partial' end end - end - -private - def default_render - if @alternate_default_render - @alternate_default_render.call - else - super + + def respond_to_partial_as_rjs + respond_to do |format| + format.js do + render :update do |page| + page.replace :foo, :partial => 'partial' + end + end + end end - end - def determine_layout - case action_name - when "hello_world", "layout_test", "rendering_without_layout", - "rendering_nothing_on_layout", "render_text_hello_world", - "render_text_hello_world_with_layout", - "hello_world_with_layout_false", - "partial_only", "partial_only_with_layout", - "accessing_params_in_template", - "accessing_params_in_template_with_layout", - "render_with_explicit_template", - "render_with_explicit_string_template", - "update_page", "update_page_with_instance_variables" - - "layouts/standard" - when "action_talk_to_layout", "layout_overriding_layout" - "layouts/talk_from_action" - when "render_implicit_html_template_from_xhr_request" - (request.xhr? ? 'layouts/xhr' : 'layouts/standard') - end - end -end + def render_alternate_default + # For this test, the method "default_render" is overridden: + @alternate_default_render = lambda do + render :update do |page| + page.replace :foo, :partial => 'partial' + end + end + end + + private + def default_render + if @alternate_default_render + @alternate_default_render.call + else + super + end + end + + def determine_layout + case action_name + when "hello_world", "layout_test", "rendering_without_layout", + "rendering_nothing_on_layout", "render_text_hello_world", + "render_text_hello_world_with_layout", + "hello_world_with_layout_false", + "partial_only", "partial_only_with_layout", + "accessing_params_in_template", + "accessing_params_in_template_with_layout", + "render_with_explicit_template", + "render_with_explicit_string_template", + "update_page", "update_page_with_instance_variables" + + "layouts/standard" + when "action_talk_to_layout", "layout_overriding_layout" + "layouts/talk_from_action" + when "render_implicit_html_template_from_xhr_request" + (request.xhr? ? 'layouts/xhr' : 'layouts/standard') + end + end + end -class RenderTest < ActionController::TestCase tests TestController def setup @@ -144,8 +149,8 @@ class RenderTest < ActionController::TestCase @controller.logger = Logger.new(nil) @request.host = "www.nextangle.com" - end - + end + def test_enum_rjs_test ActiveSupport::SecureRandom.stubs(:base64).returns("asdf") get :enum_rjs_test @@ -153,13 +158,13 @@ class RenderTest < ActionController::TestCase $$(".product").each(function(value, index) { new Effect.Highlight(element,{}); new Effect.Highlight(value,{}); - Sortable.create(value, {onUpdate:function(){new Ajax.Request('/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value) + '&authenticity_token=' + encodeURIComponent('asdf')})}}); + Sortable.create(value, {onUpdate:function(){new Ajax.Request('/render_other_test/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value) + '&authenticity_token=' + encodeURIComponent('asdf')})}}); new Draggable(value, {}); }); }.gsub(/^ /, '').strip assert_equal body, @response.body end - + def test_explicitly_rendering_an_html_template_with_implicit_html_template_renders_should_be_possible_from_an_rjs_template [:js, "js"].each do |format| assert_nothing_raised do @@ -167,14 +172,14 @@ class RenderTest < ActionController::TestCase assert_equal %(document.write("Hello world\\n");), @response.body end end - end - + end + def test_render_custom_code_rjs get :render_custom_code_rjs assert_response 404 assert_equal %(Element.replace("foo", "partial html");), @response.body end - + def test_render_in_an_rjs_template_should_pick_html_templates_when_available [:js, "js"].each do |format| assert_nothing_raised do @@ -183,7 +188,7 @@ class RenderTest < ActionController::TestCase end end end - + def test_render_rjs_template_explicitly get :render_js_with_explicit_template assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body @@ -193,12 +198,12 @@ class RenderTest < ActionController::TestCase get :render_js_with_explicit_action_template assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body end - + def test_render_rjs_with_default get :delete_with_js assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body end - + def test_update_page get :update_page assert_template nil @@ -219,8 +224,8 @@ class RenderTest < ActionController::TestCase assert_template nil assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"] assert_match /2 people/, @response.body - end - + end + def test_should_render_html_formatted_partial_with_rjs xhr :get, :partial_as_rjs assert_equal %(Element.replace("foo", "partial html");), @response.body @@ -230,9 +235,9 @@ class RenderTest < ActionController::TestCase xhr :get, :respond_to_partial_as_rjs assert_equal %(Element.replace("foo", "partial html");), @response.body end - + def test_should_render_with_alternate_default_render xhr :get, :render_alternate_default assert_equal %(Element.replace("foo", "partial html");), @response.body - end -end
\ No newline at end of file + end +end diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index e96e8a4d57..68a52c3e8c 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -2,37 +2,41 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' -class TestController < ActionController::Base - protect_from_forgery +class RenderXmlTest < ActionController::TestCase + class TestController < ActionController::Base + protect_from_forgery - def render_with_location - render :xml => "<hello/>", :location => "http://example.com", :status => 201 - end + def self.controller_path + 'test' + end - def render_with_object_location - customer = Customer.new("Some guy", 1) - render :xml => "<customer/>", :location => customer, :status => :created - end + def render_with_location + render :xml => "<hello/>", :location => "http://example.com", :status => 201 + end - def render_with_to_xml - to_xmlable = Class.new do - def to_xml - "<i-am-xml/>" - end - end.new + def render_with_object_location + customer = Customer.new("Some guy", 1) + render :xml => "<customer/>", :location => customer, :status => :created + end - render :xml => to_xmlable - end - - def formatted_xml_erb + def render_with_to_xml + to_xmlable = Class.new do + def to_xml + "<i-am-xml/>" + end + end.new + + render :xml => to_xmlable + end + + def formatted_xml_erb + end + + def render_xml_with_custom_content_type + render :xml => "<blah/>", :content_type => "application/atomsvc+xml" + end end - - def render_xml_with_custom_content_type - render :xml => "<blah/>", :content_type => "application/atomsvc+xml" - end -end -class RenderTest < ActionController::TestCase tests TestController def setup @@ -42,8 +46,8 @@ class RenderTest < ActionController::TestCase @controller.logger = Logger.new(nil) @request.host = "www.nextangle.com" - end - + end + def test_rendering_with_location_should_set_header get :render_with_location assert_equal "http://example.com", @response.headers["Location"] @@ -53,7 +57,7 @@ class RenderTest < ActionController::TestCase get :render_with_to_xml assert_equal "<i-am-xml/>", @response.body end - + def test_rendering_with_object_location_should_set_header_with_url_for with_routing do |set| set.draw do |map| @@ -65,19 +69,19 @@ class RenderTest < ActionController::TestCase assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"] end end - + def test_should_render_formatted_xml_erb_template get :formatted_xml_erb, :format => :xml assert_equal '<test>passed formatted xml erb</test>', @response.body end - + def test_should_render_xml_but_keep_custom_content_type get :render_xml_with_custom_content_type assert_equal "application/atomsvc+xml", @response.content_type end - + def test_should_use_implicit_content_type get :implicit_content_type, :format => 'atom' assert_equal Mime::ATOM, @response.content_type - end + end end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 490a4ff3b3..689359166f 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -227,12 +227,6 @@ class ControllerInheritanceRescueControllerTest < ActionController::TestCase end end -class ApplicationController < ActionController::Base - rescue_from ActionController::RoutingError do - render :text => 'no way' - end -end - class RescueControllerTest < ActionController::TestCase def test_rescue_handler get :not_authorized @@ -332,23 +326,17 @@ class RescueTest < ActionController::IntegrationTest end test 'rescue routing exceptions' do - assert_equal 1, ApplicationController.rescue_handlers.length - - begin - with_test_routing do - get '/no_way' - assert_equal 'no way', response.body - end - ensure - ActionController::Base.rescue_handlers.clear + @app = ActionDispatch::Rescue.new(ActionController::Routing::Routes) do + rescue_from ActionController::RoutingError, lambda { |env| [200, {"Content-Type" => "text/html"}, "Gotcha!"] } end + + get '/b00m' + assert_equal "Gotcha!", response.body end test 'unrescued exception' do - with_test_routing do - get '/b00m' - assert_match(/Action Controller: Exception caught/, response.body) - end + @app = ActionDispatch::Rescue.new(ActionController::Routing::Routes) + assert_raise(ActionController::RoutingError) { get '/b00m' } end private diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 0b639e363d..5b47de19ae 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -135,7 +135,7 @@ class ResourcesTest < ActionController::TestCase def test_with_custom_conditions with_restful_routing :messages, :conditions => { :subdomain => 'app' } do - assert_equal 'app', ActionController::Routing::Routes.named_routes.routes[:messages].conditions[:subdomain] + assert ActionController::Routing::Routes.recognize_path("/messages", :method => :get, :subdomain => 'app') end end @@ -1130,7 +1130,8 @@ class ResourcesTest < ActionController::TestCase map.resource :product end - assert_equal :get, set.named_routes.routes[:product].conditions[:method] + assert_routing '/product', :controller => 'products', :action => 'show' + assert set.recognize_path("/product", :method => :get) end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index d20684296f..edf243337f 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -232,14 +232,18 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def test_basic_named_route - rs.add_named_route :home, '', :controller => 'content', :action => 'list' + rs.draw do |map| + map.home '', :controller => 'content', :action => 'list' + end x = setup_for_named_route assert_equal("http://test.host/", x.send(:home_url)) end def test_basic_named_route_with_relative_url_root - rs.add_named_route :home, '', :controller => 'content', :action => 'list' + rs.draw do |map| + map.home '', :controller => 'content', :action => 'list' + end x = setup_for_named_route ActionController::Base.relative_url_root = "/foo" assert_equal("http://test.host/foo/", @@ -249,14 +253,18 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def test_named_route_with_option - rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page' + rs.draw do |map| + map.page 'page/:title', :controller => 'content', :action => 'show_page' + end x = setup_for_named_route assert_equal("http://test.host/page/new%20stuff", x.send(:page_url, :title => 'new stuff')) end def test_named_route_with_default - rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage' + rs.draw do |map| + map.page 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage' + end x = setup_for_named_route assert_equal("http://test.host/page/AboutRails", x.send(:page_url, :title => "AboutRails")) @@ -264,36 +272,46 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def test_named_route_with_name_prefix - rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :name_prefix => 'my_' + rs.draw do |map| + map.page 'page', :controller => 'content', :action => 'show_page', :name_prefix => 'my_' + end x = setup_for_named_route assert_equal("http://test.host/page", x.send(:my_page_url)) end def test_named_route_with_path_prefix - rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :path_prefix => 'my' + rs.draw do |map| + map.page 'page', :controller => 'content', :action => 'show_page', :path_prefix => 'my' + end x = setup_for_named_route assert_equal("http://test.host/my/page", x.send(:page_url)) end def test_named_route_with_blank_path_prefix - rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :path_prefix => '' + rs.draw do |map| + map.page 'page', :controller => 'content', :action => 'show_page', :path_prefix => '' + end x = setup_for_named_route assert_equal("http://test.host/page", x.send(:page_url)) end def test_named_route_with_nested_controller - rs.add_named_route :users, 'admin/user', :controller => 'admin/user', :action => 'index' + rs.draw do |map| + map.users 'admin/user', :controller => 'admin/user', :action => 'index' + end x = setup_for_named_route assert_equal("http://test.host/admin/user", x.send(:users_url)) end def test_optimised_named_route_call_never_uses_url_for - rs.add_named_route :users, 'admin/user', :controller => '/admin/user', :action => 'index' - rs.add_named_route :user, 'admin/user/:id', :controller=>'/admin/user', :action=>'show' + rs.draw do |map| + map.users 'admin/user', :controller => '/admin/user', :action => 'index' + map.user 'admin/user/:id', :controller=>'/admin/user', :action=>'show' + end x = setup_for_named_route x.expects(:url_for).never x.send(:users_url) @@ -303,7 +321,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def test_optimised_named_route_with_host - rs.add_named_route :pages, 'pages', :controller => 'content', :action => 'show_page', :host => 'foo.com' + rs.draw do |map| + map.pages 'pages', :controller => 'content', :action => 'show_page', :host => 'foo.com' + end x = setup_for_named_route x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => :pages).once x.send(:pages_url) @@ -378,7 +398,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def test_paths_slashes_unescaped_with_ordered_parameters - rs.add_named_route :path, '/file/*path', :controller => 'content' + rs.draw do |map| + map.path '/file/*path', :controller => 'content' + end # No / to %2F in URI, only for query params. x = setup_for_named_route @@ -647,21 +669,13 @@ class LegacyRouteSetTests < Test::Unit::TestCase %w(GET POST PUT DELETE).each do |request_method| define_method("test_request_method_recognized_with_#{request_method}") do - begin - Object.const_set(:BooksController, Class.new(ActionController::Base)) - - setup_request_method_routes_for(request_method) - - assert_nothing_raised { rs.recognize(@request) } - assert_equal request_method.downcase, @request.path_parameters[:action] - ensure - Object.send(:remove_const, :BooksController) rescue nil - end + setup_request_method_routes_for(request_method) + assert_nothing_raised { rs.recognize(@request) } + assert_equal request_method.downcase, @request.path_parameters[:action] end end def test_recognize_array_of_methods - Object.const_set(:BooksController, Class.new(ActionController::Base)) rs.draw do |r| r.connect '/match', :controller => 'books', :action => 'get_or_post', :conditions => { :method => [:get, :post] } r.connect '/match', :controller => 'books', :action => 'not_get_or_post' @@ -679,13 +693,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase @request.request_uri = "/match" assert_nothing_raised { rs.recognize(@request) } assert_equal 'not_get_or_post', @request.path_parameters[:action] - ensure - Object.send(:remove_const, :BooksController) rescue nil end def test_subpath_recognized - Object.const_set(:SubpathBooksController, Class.new(ActionController::Base)) - rs.draw do |r| r.connect '/books/:id/edit', :controller => 'subpath_books', :action => 'edit' r.connect '/items/:id/:action', :controller => 'subpath_books' @@ -708,13 +718,9 @@ class LegacyRouteSetTests < Test::Unit::TestCase hash = rs.recognize_path "/posts/7" assert_not_nil hash assert_equal %w(subpath_books show 7), [hash[:controller], hash[:action], hash[:id]] - ensure - Object.send(:remove_const, :SubpathBooksController) rescue nil end def test_subpath_generated - Object.const_set(:SubpathBooksController, Class.new(ActionController::Base)) - rs.draw do |r| r.connect '/books/:id/edit', :controller => 'subpath_books', :action => 'edit' r.connect '/items/:id/:action', :controller => 'subpath_books' @@ -724,8 +730,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase assert_equal "/books/7/edit", rs.generate(:controller => "subpath_books", :id => 7, :action => "edit") assert_equal "/items/15/complete", rs.generate(:controller => "subpath_books", :id => 15, :action => "complete") assert_equal "/posts/new/preview", rs.generate(:controller => "subpath_books", :action => "preview") - ensure - Object.send(:remove_const, :SubpathBooksController) rescue nil end def test_failed_requirements_raises_exception_with_violated_requirements @@ -1100,8 +1104,6 @@ class RouteSetTest < ActiveSupport::TestCase end def test_recognize_with_conditions - Object.const_set(:PeopleController, Class.new) - set.draw do |map| map.with_options(:controller => "people") do |people| people.people "/people", :action => "index", :conditions => { :method => :get } @@ -1161,14 +1163,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal [:get, :put, :delete], e.allowed_methods end request.recycle! - - ensure - Object.send(:remove_const, :PeopleController) end def test_recognize_with_alias_in_conditions - Object.const_set(:PeopleController, Class.new) - set.draw do |map| map.people "/people", :controller => 'people', :action => "index", :conditions => { :method => :get } @@ -1186,13 +1183,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("people", request.path_parameters[:controller]) assert_equal("index", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :PeopleController) end def test_typo_recognition - Object.const_set(:ArticlesController, Class.new) - set.draw do |map| map.connect 'articles/:year/:month/:day/:title', :controller => 'articles', :action => 'permalink', @@ -1207,9 +1200,6 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal("11", request.path_parameters[:month]) assert_equal("05", request.path_parameters[:day]) assert_equal("a-very-interesting-article", request.path_parameters[:title]) - - ensure - Object.send(:remove_const, :ArticlesController) end def test_routing_traversal_does_not_load_extra_classes @@ -1226,8 +1216,6 @@ class RouteSetTest < ActiveSupport::TestCase end def test_recognize_with_conditions_and_format - Object.const_set(:PeopleController, Class.new) - set.draw do |map| map.with_options(:controller => "people") do |people| people.person "/people/:id", :action => "show", :conditions => { :method => :get } @@ -1254,8 +1242,6 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal("show", request.path_parameters[:action]) assert_equal("5", request.path_parameters[:id]) assert_equal("png", request.path_parameters[:_format]) - ensure - Object.send(:remove_const, :PeopleController) end def test_generate_with_default_action @@ -1269,8 +1255,6 @@ class RouteSetTest < ActiveSupport::TestCase end def test_root_map - Object.const_set(:PeopleController, Class.new) - set.draw { |map| map.root :controller => "people" } request.path = "" @@ -1278,13 +1262,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("people", request.path_parameters[:controller]) assert_equal("index", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :PeopleController) end def test_namespace - Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) }) - set.draw do |map| map.namespace 'api' do |api| @@ -1298,13 +1278,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("api/products", request.path_parameters[:controller]) assert_equal("inventory", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :Api) end def test_namespaced_root_map - Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) }) - set.draw do |map| map.namespace 'api' do |api| @@ -1318,13 +1294,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("api/products", request.path_parameters[:controller]) assert_equal("index", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :Api) end def test_namespace_with_path_prefix - Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) }) - set.draw do |map| map.namespace 'api', :path_prefix => 'prefix' do |api| api.route 'inventory', :controller => "products", :action => 'inventory' @@ -1336,13 +1308,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("api/products", request.path_parameters[:controller]) assert_equal("inventory", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :Api) end def test_namespace_with_blank_path_prefix - Object.const_set(:Api, Module.new { |m| m.const_set(:ProductsController, Class.new) }) - set.draw do |map| map.namespace 'api', :path_prefix => '' do |api| api.route 'inventory', :controller => "products", :action => 'inventory' @@ -1354,8 +1322,6 @@ class RouteSetTest < ActiveSupport::TestCase assert_nothing_raised { set.recognize(request) } assert_equal("api/products", request.path_parameters[:controller]) assert_equal("inventory", request.path_parameters[:action]) - ensure - Object.send(:remove_const, :Api) end def test_generate_changes_controller_module @@ -1781,23 +1747,23 @@ class RouteSetTest < ActiveSupport::TestCase end def test_default_route_recognition - expected = {:controller => 'accounts', :action => 'show', :id => '10'} - assert_equal expected, default_route_set.recognize_path('/accounts/show/10') - assert_equal expected, default_route_set.recognize_path('/accounts/show/10/') + expected = {:controller => 'pages', :action => 'show', :id => '10'} + assert_equal expected, default_route_set.recognize_path('/pages/show/10') + assert_equal expected, default_route_set.recognize_path('/pages/show/10/') expected[:id] = 'jamis' - assert_equal expected, default_route_set.recognize_path('/accounts/show/jamis/') + assert_equal expected, default_route_set.recognize_path('/pages/show/jamis/') expected.delete :id - assert_equal expected, default_route_set.recognize_path('/accounts/show') - assert_equal expected, default_route_set.recognize_path('/accounts/show/') + assert_equal expected, default_route_set.recognize_path('/pages/show') + assert_equal expected, default_route_set.recognize_path('/pages/show/') expected[:action] = 'index' - assert_equal expected, default_route_set.recognize_path('/accounts/') - assert_equal expected, default_route_set.recognize_path('/accounts') + assert_equal expected, default_route_set.recognize_path('/pages/') + assert_equal expected, default_route_set.recognize_path('/pages') assert_raise(ActionController::RoutingError) { default_route_set.recognize_path('/') } - assert_raise(ActionController::RoutingError) { default_route_set.recognize_path('/accounts/how/goood/it/is/to/be/free') } + assert_raise(ActionController::RoutingError) { default_route_set.recognize_path('/pages/how/goood/it/is/to/be/free') } end def test_default_route_should_omit_default_action @@ -1813,15 +1779,15 @@ class RouteSetTest < ActiveSupport::TestCase end def test_default_route_should_uri_escape_pluses - expected = { :controller => 'accounts', :action => 'show', :id => 'hello world' } - assert_equal expected, default_route_set.recognize_path('/accounts/show/hello world') - assert_equal expected, default_route_set.recognize_path('/accounts/show/hello%20world') - assert_equal '/accounts/show/hello%20world', default_route_set.generate(expected, expected) + expected = { :controller => 'pages', :action => 'show', :id => 'hello world' } + assert_equal expected, default_route_set.recognize_path('/pages/show/hello world') + assert_equal expected, default_route_set.recognize_path('/pages/show/hello%20world') + assert_equal '/pages/show/hello%20world', default_route_set.generate(expected, expected) expected[:id] = 'hello+world' - assert_equal expected, default_route_set.recognize_path('/accounts/show/hello+world') - assert_equal expected, default_route_set.recognize_path('/accounts/show/hello%2Bworld') - assert_equal '/accounts/show/hello+world', default_route_set.generate(expected, expected) + assert_equal expected, default_route_set.recognize_path('/pages/show/hello+world') + assert_equal expected, default_route_set.recognize_path('/pages/show/hello%2Bworld') + assert_equal '/pages/show/hello+world', default_route_set.generate(expected, expected) end def test_parameter_shell diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 9b8d07222b..4c4bf9ade4 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'controller/fake_controllers' ActionController::UrlRewriter diff --git a/actionpack/test/controller/verification_test.rb b/actionpack/test/controller/verification_test.rb index ee558f3465..1a9eb65f29 100644 --- a/actionpack/test/controller/verification_test.rb +++ b/actionpack/test/controller/verification_test.rb @@ -111,13 +111,6 @@ class VerificationTest < ActionController::TestCase tests TestController - setup do - ActionController::Routing::Routes.draw do |map| - map.foo '/foo', :controller => 'test', :action => 'foo' - map.connect ":controller/:action/:id" - end - end - def test_using_symbol_back_with_no_referrer assert_raise(ActionController::RedirectBackError) { get :guarded_with_back } end @@ -130,8 +123,14 @@ class VerificationTest < ActionController::TestCase def test_no_deprecation_warning_for_named_route assert_not_deprecated do - get :guarded_one_for_named_route_test, :two => "not one" - assert_redirected_to '/foo' + with_routing do |set| + set.draw do |map| + map.foo '/foo', :controller => 'test', :action => 'foo' + map.connect ":controller/:action/:id" + end + get :guarded_one_for_named_route_test, :two => "not one" + assert_redirected_to '/foo' + end end end diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index c732d1c910..05d2c8407c 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -43,7 +43,7 @@ class ViewLoadPathsTest < ActionController::TestCase end def expand(array) - array.map {|x| File.expand_path(x)} + array.map {|x| File.expand_path(x.to_s)} end def assert_paths(*paths) diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 916124e221..0514c098bf 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -245,8 +245,8 @@ class WebServiceTest < ActionController::IntegrationTest private def with_params_parsers(parsers = {}) old_session = @integration_session - app = ActionDispatch::ParamsParser.new(ActionController::Routing::Routes, parsers) - @integration_session = open_session(app) + @app = ActionDispatch::ParamsParser.new(ActionController::Routing::Routes, parsers) + reset! yield ensure @integration_session = old_session @@ -259,7 +259,6 @@ class WebServiceTest < ActionController::IntegrationTest c.connect "/", :action => "assign_parameters" end end - reset! yield end end diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index 995f36bb29..db6cf7b330 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -59,7 +59,6 @@ class JsonParamsParsingTest < ActionController::IntegrationTest set.draw do |map| map.connect ':action', :controller => "json_params_parsing_test/test" end - reset! yield end end diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index d4ee4362eb..301080842e 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -153,7 +153,6 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest set.draw do |map| map.connect ':action', :controller => "multipart_params_parsing_test/test" end - reset! yield end end diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index 2261934e45..a31e326ddf 100644 --- a/actionpack/test/dispatch/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -111,7 +111,6 @@ class QueryStringParsingTest < ActionController::IntegrationTest set.draw do |map| map.connect ':action', :controller => "query_string_parsing_test/test" end - reset! get "/parse", actual assert_response :ok diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb index 6c9967d26e..7167cdafac 100644 --- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb @@ -132,7 +132,6 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest set.draw do |map| map.connect ':action', :controller => "url_encoded_params_parsing_test/test" end - reset! yield end end diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb index 2f2dd695c4..521002b519 100644 --- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb @@ -86,7 +86,6 @@ class XmlParamsParsingTest < ActionController::IntegrationTest set.draw do |map| map.connect ':action', :controller => "xml_params_parsing_test/test" end - reset! yield end end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 0723a76d2b..ab5fabde65 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -5,9 +5,6 @@ class CookieStoreTest < ActionController::IntegrationTest SessionKey = '_myapp_session' SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33' - # Make sure Session middleware doesnt get included in the middleware stack - ActionController::Base.session_store = nil - Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1') SignedBar = Verifier.generate(:foo => "bar", :session_id => ActiveSupport::SecureRandom.hex(16)) @@ -30,7 +27,7 @@ class CookieStoreTest < ActionController::IntegrationTest end def get_session_id - render :text => "foo: #{session[:foo].inspect}; id: #{request.session_options[:id]}" + render :text => "id: #{request.session_options[:id]}" end def call_reset_session @@ -46,10 +43,6 @@ class CookieStoreTest < ActionController::IntegrationTest def rescue_action(e) raise end end - def setup - reset_app! - end - def test_raises_argument_error_if_missing_session_key assert_raise(ArgumentError, nil.inspect) { ActionDispatch::Session::CookieStore.new(nil, @@ -119,7 +112,7 @@ class CookieStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success - assert_equal "foo: \"bar\"; id: #{session_id}", response.body + assert_equal "id: #{session_id}", response.body end end @@ -193,10 +186,7 @@ class CookieStoreTest < ActionController::IntegrationTest end def test_session_store_with_expire_after - with_test_route_set do - app = ActionDispatch::Session::CookieStore.new(ActionController::Dispatcher.new, :key => SessionKey, :secret => SessionSecret, :expire_after => 5.hours) - @integration_session = open_session(app) - + with_test_route_set(:expire_after => 5.hours) do # First request accesses the session time = Time.local(2008, 4, 24) Time.stubs(:now).returns(time) @@ -226,20 +216,13 @@ class CookieStoreTest < ActionController::IntegrationTest end private - def reset_app! - app = ActionDispatch::Session::CookieStore.new(ActionController::Dispatcher.new, - :key => SessionKey, :secret => SessionSecret) - @integration_session = open_session(app) - end - - def with_test_route_set + def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| - map.with_options :controller => "cookie_store_test/test" do |c| - c.connect "/:action" - end + map.connect "/:action", :controller => "cookie_store_test/test" end - reset_app! + options = {:key => SessionKey, :secret => SessionSecret}.merge(options) + @app = ActionDispatch::Session::CookieStore.new(set, options) yield end end diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 1588918be7..c7435bd06b 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -32,7 +32,9 @@ class MemCacheStoreTest < ActionController::IntegrationTest end begin - App = ActionDispatch::Session::MemCacheStore.new(ActionController::Dispatcher.new, :key => '_session_id') + require 'memcache' + memcache = MemCache.new('localhost:11211') + memcache.set('ping', '') def test_setting_and_getting_session_value with_test_route_set do @@ -99,7 +101,7 @@ class MemCacheStoreTest < ActionController::IntegrationTest get '/set_session_value', :_session_id => session_id assert_response :success - assert_equal nil, cookies['_session_id'] + assert_not_equal session_id, cookies['_session_id'] end end rescue LoadError, RuntimeError @@ -107,20 +109,12 @@ class MemCacheStoreTest < ActionController::IntegrationTest end private - def reset_app! - app = ActionDispatch::Session::MemCacheStore.new( - ActionController::Dispatcher.new, :key => '_session_id') - @integration_session = open_session(app) - end - def with_test_route_set with_routing do |set| set.draw do |map| - map.with_options :controller => "mem_cache_store_test/test" do |c| - c.connect "/:action" - end + map.connect "/:action", :controller => "mem_cache_store_test/test" end - reset_app! + @app = ActionDispatch::Session::MemCacheStore.new(set, :key => '_session_id') yield end end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index ce1973853e..9f6a93756c 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -35,7 +35,7 @@ class ShowExceptionsTest < ActionController::IntegrationTest DevelopmentApp = ActionDispatch::ShowExceptions.new(Boomer, true) test "rescue in public from a remote ip" do - @integration_session = open_session(ProductionApp) + @app = ProductionApp self.remote_addr = '208.77.188.166' get "/" @@ -52,7 +52,7 @@ class ShowExceptionsTest < ActionController::IntegrationTest end test "rescue locally from a local request" do - @integration_session = open_session(ProductionApp) + @app = ProductionApp self.remote_addr = '127.0.0.1' get "/" @@ -70,11 +70,10 @@ class ShowExceptionsTest < ActionController::IntegrationTest test "localize public rescue message" do # Change locale - old_locale = I18n.locale - I18n.locale = :da + old_locale, I18n.locale = I18n.locale, :da begin - @integration_session = open_session(ProductionApp) + @app = ProductionApp self.remote_addr = '208.77.188.166' get "/" @@ -90,7 +89,7 @@ class ShowExceptionsTest < ActionController::IntegrationTest end test "always rescue locally in development mode" do - @integration_session = open_session(DevelopmentApp) + @app = DevelopmentApp self.remote_addr = '208.77.188.166' get "/" diff --git a/railties/test/rack_static_test.rb b/actionpack/test/dispatch/static_test.rb index ad673f6f19..e6957bb0ea 100644 --- a/railties/test/rack_static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -1,21 +1,10 @@ require 'abstract_unit' -require 'action_controller' -require 'rails/rack' - -class RackStaticTest < ActiveSupport::TestCase - def setup - FileUtils.cp_r "#{RAILS_ROOT}/fixtures/public", "#{RAILS_ROOT}/public" - end - - def teardown - FileUtils.rm_rf "#{RAILS_ROOT}/public" - end - +class StaticTest < ActiveSupport::TestCase DummyApp = lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] } - App = Rails::Rack::Static.new(DummyApp) + App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public") test "serves dynamic content" do assert_equal "Hello, World!", get("/nofile") diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index 5da02b2ea6..b8e340e055 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -5,7 +5,7 @@ class TestRequestTest < ActiveSupport::TestCase env = ActionDispatch::TestRequest.new.env assert_equal "GET", env.delete("REQUEST_METHOD") - assert_equal "off", env.delete("HTTPS") + assert_equal nil, env.delete("HTTPS") assert_equal "http", env.delete("rack.url_scheme") assert_equal "example.org", env.delete("SERVER_NAME") assert_equal "80", env.delete("SERVER_PORT") @@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase assert_equal "0.0.0.0", env.delete("REMOTE_ADDR") assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT") - assert_equal [1, 0], env.delete("rack.version") + assert_equal [0, 1], env.delete("rack.version") assert_equal "", env.delete("rack.input").string assert_kind_of StringIO, env.delete("rack.errors") assert_equal true, env.delete("rack.multithread") diff --git a/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.xml.erb b/actionpack/test/fixtures/old_content_type/render_default_content_types_for_respond_to.xml.erb index 25dc746886..25dc746886 100644 --- a/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.xml.erb +++ b/actionpack/test/fixtures/old_content_type/render_default_content_types_for_respond_to.xml.erb diff --git a/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml b/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml index c7926d48bb..c7926d48bb 100644 --- a/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml +++ b/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml diff --git a/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs b/actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs index 8d614d04ad..8d614d04ad 100644 --- a/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs +++ b/actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs diff --git a/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml b/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml index 598d62e2fc..598d62e2fc 100644 --- a/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml +++ b/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml diff --git a/railties/test/fixtures/public/foo/bar.html b/actionpack/test/fixtures/public/foo/bar.html index 9a35646205..9a35646205 100644 --- a/railties/test/fixtures/public/foo/bar.html +++ b/actionpack/test/fixtures/public/foo/bar.html diff --git a/railties/test/fixtures/public/foo/index.html b/actionpack/test/fixtures/public/foo/index.html index 497a2e898f..497a2e898f 100644 --- a/railties/test/fixtures/public/foo/index.html +++ b/actionpack/test/fixtures/public/foo/index.html diff --git a/railties/test/fixtures/public/index.html b/actionpack/test/fixtures/public/index.html index 525950ba6b..525950ba6b 100644 --- a/railties/test/fixtures/public/index.html +++ b/actionpack/test/fixtures/public/index.html diff --git a/actionpack/test/fixtures/test/_from_helper.erb b/actionpack/test/fixtures/test/_from_helper.erb new file mode 100644 index 0000000000..16de7c0f8a --- /dev/null +++ b/actionpack/test/fixtures/test/_from_helper.erb @@ -0,0 +1 @@ +<%= render_from_helper %>
\ No newline at end of file diff --git a/actionpack/test/lib/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb index 6e02e2d21b..9ec7f330b8 100644 --- a/actionpack/test/lib/controller/fake_controllers.rb +++ b/actionpack/test/lib/controller/fake_controllers.rb @@ -1,15 +1,33 @@ class << Object; alias_method :const_available?, :const_defined?; end -class ContentController < ActionController::Base -end -class NotAController -end +class ContentController < ActionController::Base; end +class NotAController; end + module Admin class << self; alias_method :const_available?, :const_defined?; end class UserController < ActionController::Base; end class NewsFeedController < ActionController::Base; end end +module Api + class ProductsController < ActionController::Base; end +end + +# TODO: Reduce the number of test controllers we use +class AddressesController < ActionController::Base; end +class ArticlesController < ActionController::Base; end +class BarController < ActionController::Base; end +class BooksController < ActionController::Base; end +class BraveController < ActionController::Base; end +class CController < ActionController::Base; end +class ElsewhereController < ActionController::Base; end +class FooController < ActionController::Base; end +class HiController < ActionController::Base; end +class ImageController < ActionController::Base; end +class PeopleController < ActionController::Base; end +class SessionsController < ActionController::Base; end +class SubpathBooksController < ActionController::Base; end +class WeblogController < ActionController::Base; end # For speed test class SpeedController < ActionController::Base; end @@ -24,11 +42,5 @@ class UsersController < SpeedController; end class SettingsController < SpeedController; end class ChannelsController < SpeedController; end class ChannelVideosController < SpeedController; end -class SessionsController < SpeedController; end class LostPasswordsController < SpeedController; end class PagesController < SpeedController; end - -ActionController::Routing::Routes.draw do |map| - map.route_one 'route_one', :controller => 'elsewhere', :action => 'flash_me' - map.connect ':controller/:action/:id' -end diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 18eff7516b..823de8bdc7 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -51,3 +51,99 @@ module Quiz end end +class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) + extend ActiveModel::Naming + include ActiveModel::Conversion + + alias_method :secret?, :secret + + def new_record=(boolean) + @new_record = boolean + end + + def new_record? + @new_record + end + + attr_accessor :author + def author_attributes=(attributes); end + + attr_accessor :comments + def comments_attributes=(attributes); end + + attr_accessor :tags + def tags_attributes=(attributes); end +end + +class Comment + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + attr_reader :post_id + def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end + def save; @id = 1; @post_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def name + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end + + attr_accessor :relevances + def relevances_attributes=(attributes); end + +end + +class Tag + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + attr_reader :post_id + def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end + def save; @id = 1; @post_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end + + attr_accessor :relevances + def relevances_attributes=(attributes); end + +end + +class CommentRelevance + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + attr_reader :comment_id + def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end + def save; @id = 1; @comment_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end +end + +class TagRelevance + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + attr_reader :tag_id + def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end + def save; @id = 1; @tag_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end +end + +class Author < Comment + attr_accessor :post + def post_attributes=(attributes); end +end diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb index 8da92180d1..6b9e7c5270 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/test/lib/fixture_template.rb @@ -1,67 +1,24 @@ module ActionView #:nodoc: - class FixtureResolver < Resolver + class FixtureResolver < PathResolver def initialize(hash = {}, options = {}) super(options) @hash = hash end - def find_templates(name, details, prefix, partial) - if regexp = details_to_regexp(name, details, prefix, partial) - cached(regexp) do - templates = [] - @hash.select { |k,v| k =~ regexp }.each do |path, source| - templates << Template.new(source, path, *path_to_details(path)) - end - templates.sort_by {|t| -t.details.values.compact.size } - end - end - end - private - def formats_regexp - @formats_regexp ||= begin - formats = Mime::SET.symbols - '(?:' + formats.map { |l| "\\.#{Regexp.escape(l.to_s)}" }.join('|') + ')?' + def query(path, exts) + query = Regexp.escape(path) + exts.each do |ext| + query << '(?:' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << ')' end - end - - def handler_regexp - e = TemplateHandlers.extensions.map{|h| "\\.#{Regexp.escape(h.to_s)}"}.join("|") - "(?:#{e})" - end - def details_to_regexp(name, details, prefix, partial) - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - - extensions = "" - [:locales, :formats].each do |k| - extensions << if exts = details[k] - '(?:' + exts.map {|e| "\\.#{Regexp.escape(e.to_s)}"}.join('|') + ')?' - else - k == :formats ? formats_regexp : '' - end + templates = [] + @hash.select { |k,v| k =~ /^#{query}$/ }.each do |path, source| + templates << Template.new(source, path, *path_to_details(path)) end - - %r'^#{Regexp.escape(path)}#{extensions}#{handler_regexp}$' + templates.sort_by {|t| -t.details.values.compact.size } end - # TODO: fix me - # :api: plugin - def path_to_details(path) - # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'(_)?[\w-]+((?:\.[\w-]+)*)\.(\w+)$') - partial = m[1] == '_' - details = (m[2]||"").split('.').reject { |e| e.empty? } - handler = Template.handler_class_for_extension(m[3]) - - format = Mime[details.last] && details.pop.to_sym - locale = details.last && details.pop.to_sym - - return handler, :format => format, :locale => locale, :partial => partial - end - end end end
\ No newline at end of file diff --git a/actionpack/test/new_base/redirect_test.rb b/actionpack/test/new_base/redirect_test.rb deleted file mode 100644 index e591ebd05f..0000000000 --- a/actionpack/test/new_base/redirect_test.rb +++ /dev/null @@ -1 +0,0 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
\ No newline at end of file diff --git a/actionpack/test/new_base/test_helper.rb b/actionpack/test/new_base/test_helper.rb deleted file mode 100644 index b7ccd3db8d..0000000000 --- a/actionpack/test/new_base/test_helper.rb +++ /dev/null @@ -1,110 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../../lib') -$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/../lib') - -require 'test/unit' -require 'active_support' -require 'active_support/test_case' -require 'action_view' -require 'fixture_template' - -begin - require 'ruby-debug' - Debugger.settings[:autoeval] = true - Debugger.start -rescue LoadError - # Debugging disabled. `gem install ruby-debug` to enable. -end - -require 'action_controller' -require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late - -require 'action_controller/testing/process' -require 'action_controller/testing/integration' - -module Rails - def self.env - x = Object.new - def x.test?() true end - x - end -end - -# Temporary base class -class Rack::TestCase < ActionController::IntegrationTest - setup do - ActionController::Base.session_options[:key] = "abc" - ActionController::Base.session_options[:secret] = ("*" * 30) - end - - def app - @app ||= ActionController::Dispatcher.new - end - - def self.testing(klass = nil) - if klass - @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '') - else - @testing - end - end - - def get(thing, *args) - if thing.is_a?(Symbol) - super("#{self.class.testing}/#{thing}", *args) - else - super - end - end - - def assert_body(body) - assert_equal body, Array.wrap(response.body).join - end - - def assert_status(code) - assert_equal code, response.status - end - - def assert_response(body, status = 200, headers = {}) - assert_body body - assert_status status - headers.each do |header, value| - assert_header header, value - end - end - - def assert_content_type(type) - assert_equal type, response.headers["Content-Type"] - end - - def assert_header(name, value) - assert_equal value, response.headers[name] - end -end - -class ::ApplicationController < ActionController::Base -end - -module ActionController - class << Routing - def possible_controllers - @@possible_controllers ||= [] - end - end - - class Base - def self.inherited(klass) - name = klass.name.underscore.sub(/_controller$/, '') - ActionController::Routing.possible_controllers << name unless name.blank? - super - end - end -end - -class SimpleRouteCase < Rack::TestCase - setup do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - end - end -end diff --git a/actionpack/test/old_base/abstract_unit.rb b/actionpack/test/old_base/abstract_unit.rb deleted file mode 100644 index 3301041a41..0000000000 --- a/actionpack/test/old_base/abstract_unit.rb +++ /dev/null @@ -1,43 +0,0 @@ -if ENV['new_base'] - puts *caller - raise 'new_base/abstract_unit already loaded' -end -$:.unshift(File.dirname(__FILE__) + '/../lib') -$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') -$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') - -require 'rubygems' -require 'yaml' -require 'stringio' -require 'test/unit' - -begin - require 'ruby-debug' - Debugger.settings[:autoeval] = true - Debugger.start -rescue LoadError - # Debugging disabled. `gem install ruby-debug` to enable. -end - -require 'action_controller' -require 'action_controller/testing/process' -require 'action_view/test_case' - -$tags[:old_base] = true - -# Show backtraces for deprecated behavior for quicker cleanup. -ActiveSupport::Deprecation.debug = true - -ActionController::Base.logger = nil -ActionController::Routing::Routes.reload rescue nil - -ActionController::Base.session_store = nil - -# Register danish language for testing -I18n.backend.store_translations 'da', {} -I18n.backend.store_translations 'pt-BR', {} -ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort - -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') -ActionController::Base.view_paths = FIXTURE_LOAD_PATH diff --git a/actionpack/test/runner b/actionpack/test/runner deleted file mode 100755 index c2bbe63c75..0000000000 --- a/actionpack/test/runner +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby - - -ARGV.each do |arg| - Dir["#{Dir.pwd}/#{arg}/**/*_test.rb"].each do |file| - require file - end -end
\ No newline at end of file diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index ec3384f15d..c149070f2a 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -185,7 +185,7 @@ class ActiveRecordHelperTest < ActionView::TestCase end def test_form_with_action_option - @response.body = form("post", :action => "sign") + output_buffer << form("post", :action => "sign") assert_select "form[action=sign]" do |form| assert_select "input[type=submit][value=Sign]" end diff --git a/actionpack/test/javascript/ajax_test.rb b/actionpack/test/template/ajax_test.rb index b67a91dad3..670ba92697 100644 --- a/actionpack/test/javascript/ajax_test.rb +++ b/actionpack/test/template/ajax_test.rb @@ -32,7 +32,7 @@ class LinkToRemoteTest < AjaxTestCase end test "with no update" do - assert_html link, %w(href="/blog/destroy/3" Delete\ this\ post data-remote="true") + assert_html link, %w(href="/blog/destroy/4" Delete\ this\ post data-remote="true") end test "basic" do @@ -46,7 +46,7 @@ class LinkToRemoteTest < AjaxTestCase end test "with :html options" do - expected = %{<a href="/blog/destroy/3" data-custom="me" data-update-success="#posts">Delete this post</a>} + expected = %{<a href="/blog/destroy/4" data-custom="me" data-remote="true" data-update-success="#posts">Delete this post</a>} assert_equal expected, link(:update => "#posts", :html => {"data-custom" => "me"}) end @@ -74,7 +74,7 @@ class LinkToRemoteTest < AjaxTestCase end test "basic link_to_remote with :url =>" do - expected = %{<a href="/blog/destroy/3" data-update-success="#posts">Delete this post</a>} + expected = %{<a href="/blog/destroy/3" data-remote="true" data-update-success="#posts">Delete this post</a>} assert_equal expected, link_to_remote("Delete this post", :url => "/blog/destroy/3", :update => "#posts") end @@ -93,7 +93,7 @@ class ButtonToRemoteTest < AjaxTestCase def url_for(*) "/whatnot" end - + class StandardTest < ButtonToRemoteTest test "basic" do button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) @@ -103,13 +103,12 @@ class ButtonToRemoteTest < AjaxTestCase end end end - + class LegacyButtonToRemoteTest < ButtonToRemoteTest include ActionView::Helpers::AjaxHelper::Rails2Compatibility - + assert_callbacks_work do |callback| button(callback => "undoRequestCompleted(request)") end end - -end
\ No newline at end of file +end diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 28f9d48671..83fc6a282c 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -213,11 +213,11 @@ class AssetTagHelperTest < ActionView::TestCase end def test_javascript_include_tag_with_missing_source - assert_raise(Errno::ENOENT) { + assert_nothing_raised { javascript_include_tag('missing_security_guard') } - assert_raise(Errno::ENOENT) { + assert_nothing_raised { javascript_include_tag(:defaults, 'missing_security_guard') } @@ -276,7 +276,7 @@ class AssetTagHelperTest < ActionView::TestCase end def test_stylesheet_link_tag_with_missing_source - assert_raise(Errno::ENOENT) { + assert_nothing_raised { stylesheet_link_tag('missing_security_guard') } @@ -639,6 +639,40 @@ class AssetTagHelperTest < ActionView::TestCase assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) end + def test_caching_javascript_include_tag_when_caching_on_and_missing_javascript_file + ENV["RAILS_ASSET_ID"] = "" + ActionController::Base.perform_caching = true + + assert_raise(Errno::ENOENT) { + javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true) + } + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) + + assert_raise(Errno::ENOENT) { + javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money") + } + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + end + + def test_caching_javascript_include_tag_when_caching_off_and_missing_javascript_file + ENV["RAILS_ASSET_ID"] = "" + ActionController::Base.perform_caching = false + + assert_raise(Errno::ENOENT) { + javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true) + } + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) + + assert_raise(Errno::ENOENT) { + javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money") + } + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + end + def test_caching_stylesheet_link_tag_when_caching_on ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = 'http://a0.example.com' @@ -709,7 +743,6 @@ class AssetTagHelperTest < ActionView::TestCase def test_caching_stylesheet_link_tag_when_caching_on_and_missing_css_file ENV["RAILS_ASSET_ID"] = "" - ActionController::Base.asset_host = 'http://a0.example.com' ActionController::Base.perform_caching = true assert_raise(Errno::ENOENT) { @@ -729,6 +762,27 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) end + def test_caching_stylesheet_link_tag_when_caching_off_and_missing_css_file + ENV["RAILS_ASSET_ID"] = "" + ActionController::Base.perform_caching = false + + assert_raise(Errno::ENOENT) { + stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true) + } + + assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_raise(Errno::ENOENT) { + stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money") + } + + assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + end + def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" } diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb index 5d2af7cdd9..ac31fc6503 100644 --- a/actionpack/test/template/benchmark_helper_test.rb +++ b/actionpack/test/template/benchmark_helper_test.rb @@ -4,14 +4,14 @@ require 'action_view/helpers/benchmark_helper' class BenchmarkHelperTest < ActionView::TestCase tests ActionView::Helpers::BenchmarkHelper - def teardown - controller.logger.send(:clear_buffer) + def setup + super + controller.logger = ActiveSupport::BufferedLogger.new(StringIO.new) + controller.logger.auto_flushing = false end - def controller - logger = ActiveSupport::BufferedLogger.new(StringIO.new) - logger.auto_flushing = false - @controller ||= Struct.new(:logger).new(logger) + def teardown + controller.logger.send(:clear_buffer) end def test_without_block diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index 7734e6da73..632988bb2e 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -14,6 +14,9 @@ class CompiledTemplatesTest < Test::Unit::TestCase assert_equal "two", render(:file => "test/render_file_with_locals_and_default.erb", :locals => { :secret => "two" }) end + # This is broken in 1.8.6 (not supported in Rails 3.0) because the cache uses a Hash + # key. Since Ruby 1.8.6 implements Hash#hash using the hash's object_id, it will never + # successfully get a cache hit here. def test_template_changes_are_not_reflected_with_cached_templates assert_equal "Hello world!", render(:file => "test/hello_world.erb") modify_template "test/hello_world.erb", "Goodbye world!" do diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index bc011f59b8..b69a449617 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -20,15 +20,16 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase [60.seconds, true] => [:'x_minutes', 1], # without include_seconds - [29.seconds, false] => [:'less_than_x_minutes', 1], - [60.seconds, false] => [:'x_minutes', 1], - [44.minutes, false] => [:'x_minutes', 44], - [61.minutes, false] => [:'about_x_hours', 1], - [24.hours, false] => [:'x_days', 1], - [30.days, false] => [:'about_x_months', 1], - [60.days, false] => [:'x_months', 2], - [1.year, false] => [:'about_x_years', 1], - [3.years, false] => [:'over_x_years', 3] + [29.seconds, false] => [:'less_than_x_minutes', 1], + [60.seconds, false] => [:'x_minutes', 1], + [44.minutes, false] => [:'x_minutes', 44], + [61.minutes, false] => [:'about_x_hours', 1], + [24.hours, false] => [:'x_days', 1], + [30.days, false] => [:'about_x_months', 1], + [60.days, false] => [:'x_months', 2], + [1.year, false] => [:'about_x_years', 1], + [3.years + 6.months, false] => [:'over_x_years', 3], + [3.years + 10.months, false] => [:'almost_x_years', 4] }.each do |passed, expected| assert_distance_of_time_in_words_translates_key passed, expected diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 2e4763f446..9fb2080f77 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -53,13 +53,14 @@ class DateHelperTest < ActionView::TestCase assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds) assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds) - # 1440..2879 + # 1440..2529 assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds) - assert_equal "1 day", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 29.seconds) + assert_equal "1 day", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 29.seconds) - # 2880..43199 - assert_equal "2 days", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 30.seconds) - assert_equal "29 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds) + # 2530..43199 + assert_equal "2 days", distance_of_time_in_words(from, to + 42.hours + 59.minutes + 30.seconds) + assert_equal "3 days", distance_of_time_in_words(from, to + 2.days + 12.hours) + assert_equal "30 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds) # 43200..86399 assert_equal "about 1 month", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 30.seconds) @@ -69,13 +70,28 @@ class DateHelperTest < ActionView::TestCase assert_equal "2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 30.seconds) assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds) - # 525600..1051199 - assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds) - assert_equal "about 1 year", distance_of_time_in_words(from, to + 2.years - 31.seconds) - - # > 1051199 - assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds) - assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years) + # > 525599 + assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds) + assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 3.months - 1.day) + assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months) + + assert_equal "almost 2 years", distance_of_time_in_words(from, to + 2.years - 3.months + 1.day) + assert_equal "about 2 years", distance_of_time_in_words(from, to + 2.years + 3.months - 1.day) + assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 3.months + 1.day) + assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 9.months - 1.day) + assert_equal "almost 3 years", distance_of_time_in_words(from, to + 2.years + 9.months + 1.day) + + assert_equal "almost 5 years", distance_of_time_in_words(from, to + 5.years - 3.months + 1.day) + assert_equal "about 5 years", distance_of_time_in_words(from, to + 5.years + 3.months - 1.day) + assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 3.months + 1.day) + assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 9.months - 1.day) + assert_equal "almost 6 years", distance_of_time_in_words(from, to + 5.years + 9.months + 1.day) + + assert_equal "almost 10 years", distance_of_time_in_words(from, to + 10.years - 3.months + 1.day) + assert_equal "about 10 years", distance_of_time_in_words(from, to + 10.years + 3.months - 1.day) + assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 3.months + 1.day) + assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 9.months - 1.day) + assert_equal "almost 11 years", distance_of_time_in_words(from, to + 10.years + 9.months + 1.day) # test to < from assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to) @@ -104,7 +120,7 @@ class DateHelperTest < ActionView::TestCase def test_distance_in_words_with_dates start_date = Date.new 1975, 1, 31 end_date = Date.new 1977, 1, 31 - assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date)) + assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date)) end def test_distance_in_words_with_integers diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 8fd018f86d..6a08c99619 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1,103 +1,5 @@ require 'abstract_unit' - -silence_warnings do - class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) - extend ActiveModel::Naming - include ActiveModel::Conversion - - alias_method :secret?, :secret - - def new_record=(boolean) - @new_record = boolean - end - - def new_record? - @new_record - end - - attr_accessor :author - def author_attributes=(attributes); end - - attr_accessor :comments - def comments_attributes=(attributes); end - - attr_accessor :tags - def tags_attributes=(attributes); end - end - - class Comment - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - attr_reader :post_id - def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end - def save; @id = 1; @post_id = 1 end - def new_record?; @id.nil? end - def to_param; @id; end - def name - @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" - end - - attr_accessor :relevances - def relevances_attributes=(attributes); end - - end - - class Tag - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - attr_reader :post_id - def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end - def save; @id = 1; @post_id = 1 end - def new_record?; @id.nil? end - def to_param; @id; end - def value - @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" - end - - attr_accessor :relevances - def relevances_attributes=(attributes); end - - end - - class CommentRelevance - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - attr_reader :comment_id - def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end - def save; @id = 1; @comment_id = 1 end - def new_record?; @id.nil? end - def to_param; @id; end - def value - @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" - end - end - - class TagRelevance - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - attr_reader :tag_id - def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end - def save; @id = 1; @tag_id = 1 end - def new_record?; @id.nil? end - def to_param; @id; end - def value - @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" - end - end - - class Author < Comment - attr_accessor :post - def post_attributes=(attributes); end - end -end +require 'controller/fake_models' class FormHelperTest < ActionView::TestCase tests ActionView::Helpers::FormHelper @@ -784,6 +686,42 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_an_empty_supplied_attributes_collection + form_for(:post, @post) do |f| + concat f.text_field(:title) + f.fields_for(:comments, []) do |cf| + concat cf.text_field(:name) + end + end + + expected = '<form action="http://www.example.com" method="post">' + + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '</form>' + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_existing_records_on_a_supplied_nested_attributes_collection + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + + form_for(:post, @post) do |f| + concat f.text_field(:title) + f.fields_for(:comments, @post.comments) do |cf| + concat cf.text_field(:name) + end + end + + expected = '<form action="http://www.example.com" method="post">' + + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '</form>' + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_on_a_nested_attributes_collection_association_yields_only_builder @post.comments = [Comment.new(321), Comment.new] yielded_comments = [] diff --git a/actionpack/test/html-scanner/cdata_node_test.rb b/actionpack/test/template/html-scanner/cdata_node_test.rb index 1822cc565a..1822cc565a 100644 --- a/actionpack/test/html-scanner/cdata_node_test.rb +++ b/actionpack/test/template/html-scanner/cdata_node_test.rb diff --git a/actionpack/test/html-scanner/document_test.rb b/actionpack/test/template/html-scanner/document_test.rb index c68f04fa75..c68f04fa75 100644 --- a/actionpack/test/html-scanner/document_test.rb +++ b/actionpack/test/template/html-scanner/document_test.rb diff --git a/actionpack/test/html-scanner/node_test.rb b/actionpack/test/template/html-scanner/node_test.rb index b0df36877e..b0df36877e 100644 --- a/actionpack/test/html-scanner/node_test.rb +++ b/actionpack/test/template/html-scanner/node_test.rb diff --git a/actionpack/test/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb index e85a5c7abf..e85a5c7abf 100644 --- a/actionpack/test/html-scanner/sanitizer_test.rb +++ b/actionpack/test/template/html-scanner/sanitizer_test.rb diff --git a/actionpack/test/html-scanner/tag_node_test.rb b/actionpack/test/template/html-scanner/tag_node_test.rb index d1d4667378..d1d4667378 100644 --- a/actionpack/test/html-scanner/tag_node_test.rb +++ b/actionpack/test/template/html-scanner/tag_node_test.rb diff --git a/actionpack/test/html-scanner/text_node_test.rb b/actionpack/test/template/html-scanner/text_node_test.rb index 1ab3f4454e..1ab3f4454e 100644 --- a/actionpack/test/html-scanner/text_node_test.rb +++ b/actionpack/test/template/html-scanner/text_node_test.rb diff --git a/actionpack/test/html-scanner/tokenizer_test.rb b/actionpack/test/template/html-scanner/tokenizer_test.rb index a001bcbbad..a001bcbbad 100644 --- a/actionpack/test/html-scanner/tokenizer_test.rb +++ b/actionpack/test/template/html-scanner/tokenizer_test.rb diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 4144fea678..77d1374020 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'controller/fake_models' class Post extend ActiveModel::Naming diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index c86d5215cd..3c192906ae 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -33,18 +33,14 @@ module RenderTestCases end def test_render_file_with_localization - begin - old_locale = I18n.locale - I18n.locale = :da - assert_equal "Hey verden", @view.render(:file => "test/hello_world") - ensure - I18n.locale = old_locale - end + old_locale, I18n.locale = I18n.locale, :da + assert_equal "Hey verden", @view.render(:file => "test/hello_world") + ensure + I18n.locale = old_locale end def test_render_file_with_dashed_locale - old_locale = I18n.locale - I18n.locale = :"pt-BR" + old_locale, I18n.locale = I18n.locale, :"pt-BR" assert_equal "Ola mundo", @view.render(:file => "test/hello_world") ensure I18n.locale = old_locale diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb new file mode 100644 index 0000000000..5db42c4d68 --- /dev/null +++ b/actionpack/test/template/test_case_test.rb @@ -0,0 +1,172 @@ +require 'abstract_unit' +require 'controller/fake_controllers' + +module ActionView + class TestCase + module ATestHelper + end + + module AnotherTestHelper + def from_another_helper + 'Howdy!' + end + end + + module ASharedTestHelper + def from_shared_helper + 'Holla!' + end + end + helper ASharedTestHelper + + module SharedTests + def self.included(test_case) + test_case.class_eval do + test "helpers defined on ActionView::TestCase are available" do + assert test_case.ancestors.include?(ASharedTestHelper) + assert 'Holla!', from_shared_helper + end + end + end + end + + class GeneralViewTest < ActionView::TestCase + include SharedTests + test_case = self + + test "works without testing a helper module" do + assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + end + + helper AnotherTestHelper + test "additional helper classes can be specified as in a controller" do + assert test_case.ancestors.include?(AnotherTestHelper) + assert 'Howdy!', from_another_helper + end + end + + class ClassMethodsTest < ActionView::TestCase + include SharedTests + test_case = self + + tests ATestHelper + test "tests the specified helper module" do + assert_equal ATestHelper, test_case.helper_class + assert test_case.ancestors.include?(ATestHelper) + end + + helper AnotherTestHelper + test "additional helper classes can be specified as in a controller" do + assert test_case.ancestors.include?(AnotherTestHelper) + assert 'Howdy!', from_another_helper + + test_case.helper_class.module_eval do + def render_from_helper + from_another_helper + end + end + assert 'Howdy!', render(:partial => 'test/from_helper') + end + end + + class ATestHelperTest < ActionView::TestCase + include SharedTests + test_case = self + + test "inflects the name of the helper module to test from the test case class" do + assert_equal ATestHelper, test_case.helper_class + assert test_case.ancestors.include?(ATestHelper) + end + + test "a configured test controller is available" do + assert_kind_of ActionController::Base, controller + assert_equal '', controller.controller_path + end + + test "helper class that is being tested is always included in view instance" do + self.class.helper_class.module_eval do + def render_from_helper + render :partial => 'customer', :collection => @customers + end + end + + TestController.stubs(:controller_path).returns('test') + + @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + assert_match /Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper') + end + + test "no additional helpers should shared across test cases" do + assert !test_case.ancestors.include?(AnotherTestHelper) + assert_raise(NoMethodError) { send :from_another_helper } + end + + test "is able to use routes" do + controller.request.assign_parameters('foo', 'index') + assert_equal '/foo', url_for + assert_equal '/bar', url_for(:controller => 'bar') + end + + test "is able to use named routes" do + with_routing do |set| + set.draw { |map| 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 + end + + test "named routes can be used from helper included in view" do + with_routing do |set| + set.draw { |map| map.resources :contents } + _helpers.module_eval do + def render_from_helper + new_content_url + end + end + + assert_equal 'http://test.host/contents/new', render(:partial => 'test/from_helper') + end + end + + test "is able to render partials with local variables" do + assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + assert_equal 'Eloy', render(:partial => 'developers/developer', + :locals => { :developer => stub(:name => 'Eloy') }) + end + + test "is able to render partials from templates and also use instance variables" do + TestController.stubs(:controller_path).returns('test') + + @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') + end + + test "is able to make methods available to the view" do + _helpers.module_eval do + def render_from_helper; from_test_case end + end + assert_equal 'Word!', render(:partial => 'test/from_helper') + end + + def from_test_case; 'Word!'; end + helper_method :from_test_case + end + + class AssertionsTest < ActionView::TestCase + def render_from_helper + form_tag('/foo') do + concat render(:text => '<ul><li>foo</li></ul>') + end + end + helper_method :render_from_helper + + test "uses the output_buffer for assert_select" do + render(:partial => 'test/from_helper') + + assert_select 'form' do + assert_select 'li', :text => 'foo' + end + end + end + end +end diff --git a/actionpack/test/template/test_test.rb b/actionpack/test/template/test_test.rb index f32d0b3d42..05a14f3554 100644 --- a/actionpack/test/template/test_test.rb +++ b/actionpack/test/template/test_test.rb @@ -19,32 +19,41 @@ module PeopleHelper end class PeopleHelperTest < ActionView::TestCase - def setup - super - ActionController::Routing::Routes.draw do |map| - map.people 'people', :controller => 'people', :action => 'index' - map.connect ':controller/:action/:id' - end - end - def test_title assert_equal "<h1>Ruby on Rails</h1>", title("Ruby on Rails") end def test_homepage_path - assert_equal "/people", homepage_path + with_test_route_set do + assert_equal "/people", homepage_path + end end def test_homepage_url - assert_equal "http://test.host/people", homepage_url + with_test_route_set do + assert_equal "http://test.host/people", homepage_url + end end def test_link_to_person - person = mock(:name => "David") - person.class.extend ActiveModel::Naming - expects(:mocha_mock_path).with(person).returns("/people/1") - assert_equal '<a href="/people/1">David</a>', link_to_person(person) + with_test_route_set do + person = mock(:name => "David") + person.class.extend ActiveModel::Naming + expects(:mocha_mock_path).with(person).returns("/people/1") + assert_equal '<a href="/people/1">David</a>', link_to_person(person) + end end + + private + def with_test_route_set + with_routing do |set| + set.draw do |map| + map.people 'people', :controller => 'people', :action => 'index' + map.connect ':controller/:action/:id' + end + yield + end + end end class CrazyHelperTest < ActionView::TestCase diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 0e24fbd24d..ce99482078 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -1,11 +1,10 @@ # encoding: utf-8 require 'abstract_unit' +require 'controller/fake_controllers' RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env) class UrlHelperTest < ActionView::TestCase - tests ActionView::Helpers::UrlHelper - def setup super @controller = Class.new do @@ -367,27 +366,25 @@ class UrlHelperTest < ActionView::TestCase end end -class UrlHelperWithControllerTest < ActionView::TestCase - class UrlHelperController < ActionController::Base - def self.controller_path; 'url_helper_with_controller' end - - def show_url_for - render :inline => "<%= url_for :controller => 'url_helper_with_controller', :action => 'show_url_for' %>" - end +class UrlHelperController < ActionController::Base + def self.controller_path; 'url_helper_with_controller' end - def show_named_route - render :inline => "<%= show_named_route_#{params[:kind]} %>" - end + def show_url_for + render :inline => "<%= url_for :controller => 'url_helper_with_controller', :action => 'show_url_for' %>" + end - def nil_url_for - render :inline => '<%= url_for(nil) %>' - end + def show_named_route + render :inline => "<%= show_named_route_#{params[:kind]} %>" + end - def rescue_action(e) raise e end + def nil_url_for + render :inline => '<%= url_for(nil) %>' end - tests ActionView::Helpers::UrlHelper + def rescue_action(e) raise e end +end +class UrlHelperWithControllerTest < ActionController::TestCase def setup super @request = ActionController::TestRequest.new @@ -416,7 +413,7 @@ class UrlHelperWithControllerTest < ActionView::TestCase def test_url_for_nil_returns_current_path get :nil_url_for - assert_equal '/url_helper_with_controller/nil_url_for', @response.body + assert_equal '/url_helper/nil_url_for', @response.body end def test_named_route_should_show_host_and_path_using_controller_default_url_options @@ -436,37 +433,33 @@ class UrlHelperWithControllerTest < ActionView::TestCase def with_url_helper_routing with_routing do |set| set.draw do |map| - map.show_named_route 'url_helper_with_controller/show_named_route', :controller => 'url_helper_with_controller', :action => 'show_named_route' + map.show_named_route 'url_helper_with_controller/show_named_route', :controller => 'url_helper', :action => 'show_named_route' end yield end end end -class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase - class TasksController < ActionController::Base - def self.controller_path; 'tasks' end - - def index - render_default - end - - def show - render_default - end - - def rescue_action(e) raise e end +class TasksController < ActionController::Base + def index + render_default + end - protected - def render_default - render :inline => - "<%= link_to_unless_current(\"tasks\", tasks_path) %>\n" + - "<%= link_to_unless_current(\"tasks\", tasks_url) %>" - end + def show + render_default end - tests ActionView::Helpers::UrlHelper + def rescue_action(e) raise e end + protected + def render_default + render :inline => + "<%= link_to_unless_current(\"tasks\", tasks_path) %>\n" + + "<%= link_to_unless_current(\"tasks\", tasks_url) %>" + end +end + +class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase def setup super @request = ActionController::TestRequest.new @@ -537,43 +530,37 @@ class Session end end -class PolymorphicControllerTest < ActionView::TestCase - class WorkshopsController < ActionController::Base - def self.controller_path; 'workshops' end - - def index - @workshop = Workshop.new(1, true) - render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>" - end - - def show - @workshop = Workshop.new(params[:id], false) - render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>" - end - - def rescue_action(e) raise e end +class WorkshopsController < ActionController::Base + def index + @workshop = Workshop.new(1, true) + render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>" end - class SessionsController < ActionController::Base - def self.controller_path; 'sessions' end + def show + @workshop = Workshop.new(params[:id], false) + render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>" + end - def index - @workshop = Workshop.new(params[:workshop_id], false) - @session = Session.new(1, true) - render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" - end + def rescue_action(e) raise e end +end - def show - @workshop = Workshop.new(params[:workshop_id], false) - @session = Session.new(params[:id], false) - render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" - end +class SessionsController < ActionController::Base + def index + @workshop = Workshop.new(params[:workshop_id], false) + @session = Session.new(1, true) + render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" + end - def rescue_action(e) raise e end + def show + @workshop = Workshop.new(params[:workshop_id], false) + @session = Session.new(params[:id], false) + render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" end - tests ActionView::Helpers::UrlHelper + def rescue_action(e) raise e end +end +class PolymorphicControllerTest < ActionController::TestCase def setup super @request = ActionController::TestRequest.new diff --git a/actionpack/test/ts_isolated.rb b/actionpack/test/ts_isolated.rb new file mode 100644 index 0000000000..21d62f6aa7 --- /dev/null +++ b/actionpack/test/ts_isolated.rb @@ -0,0 +1,17 @@ +$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') + +require 'test/unit' +require 'rbconfig' +require 'active_support/core_ext/kernel/reporting' + +class TestIsolated < Test::Unit::TestCase + ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) + + Dir["#{File.dirname(__FILE__)}/{abstract,controller,dispatch,template}/**/*_test.rb"].each do |file| + define_method("test #{file}") do + command = "#{ruby} -Ilib:test #{file}" + silence_stderr { `#{command}` } + assert_equal 0, $?.to_i, command + end + end +end diff --git a/actionpack/test/view/test_case_test.rb b/actionpack/test/view/test_case_test.rb deleted file mode 100644 index 9124198b28..0000000000 --- a/actionpack/test/view/test_case_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'abstract_unit' - -class TestCaseTest < ActionView::TestCase - def test_should_have_current_url - controller = TestController.new - assert_nothing_raised(NoMethodError){ controller.url_for({:controller => "foo", :action => "index"}) } - end -end diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE new file mode 100644 index 0000000000..e7accc5ea1 --- /dev/null +++ b/activemodel/MIT-LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2004-2009 David Heinemeier Hansson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/activemodel/Rakefile b/activemodel/Rakefile index ae2fbdb002..fd69a557aa 100755 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -1,7 +1,13 @@ -#!/usr/bin/env ruby -require 'rake' +require File.join(File.dirname(__FILE__), 'lib', 'active_model', 'version') + +PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' +PKG_NAME = 'activemodel' +PKG_VERSION = ActiveModel::VERSION::STRING + PKG_BUILD +PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" +RELEASE_NAME = "REL #{PKG_VERSION}" + + require 'rake/testtask' -require 'rake/rdoctask' task :default => :test @@ -9,14 +15,19 @@ Rake::TestTask.new do |t| t.libs << "test" t.test_files = Dir.glob("test/cases/**/*_test.rb").sort t.verbose = true + t.warning = true end + task :isolated_test do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) Dir.glob("test/**/*_test.rb").all? do |file| - system(ruby, '-Ilib:test', file) + system(ruby, '-w', '-Ilib:test', file) end or raise "Failures" end + +require 'rake/rdoctask' + # Generate the RDoc documentation Rake::RDocTask.new do |rdoc| rdoc.rdoc_dir = 'doc' @@ -27,3 +38,15 @@ Rake::RDocTask.new do |rdoc| rdoc.rdoc_files.include('README', 'CHANGES') rdoc.rdoc_files.include('lib/**/*.rb') end + + +require 'rake/packagetask' +require 'rake/gempackagetask' + +spec = eval(File.read('activemodel.gemspec')) + +Rake::GemPackageTask.new(spec) do |p| + p.gem_spec = spec + p.need_tar = true + p.need_zip = true +end diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec new file mode 100644 index 0000000000..c3ff624419 --- /dev/null +++ b/activemodel/activemodel.gemspec @@ -0,0 +1,19 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'activemodel' + s.version = '3.0.pre' + s.summary = "A toolkit for building other modeling frameworks like ActiveRecord" + s.description = %q{Extracts common modeling concerns from ActiveRecord to share between similar frameworks like ActiveResource.} + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.rubyforge_project = "activemodel" + s.homepage = "http://www.rubyonrails.org" + + s.has_rdoc = true + + s.add_dependency('activesupport', '= 3.0.pre') + + s.require_path = 'lib' + s.files = Dir["CHANGELOG", "MIT-LICENSE", "README", "lib/**/*"] +end diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb index 3f8311ff96..b039897ea5 100644 --- a/activemodel/examples/validations.rb +++ b/activemodel/examples/validations.rb @@ -1,4 +1,4 @@ -require 'activemodel' +require 'active_model' class Person include ActiveModel::Conversion diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 244f3a546e..67f529262d 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -31,6 +31,7 @@ module ActiveModel 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' @@ -40,6 +41,7 @@ module ActiveModel autoload :TestCase, 'active_model/test_case' autoload :Validations, 'active_model/validations' autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper' + autoload :VERSION, 'active_model/version' module Serializers autoload :JSON, 'active_model/serializers/json' diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 1091ad3095..aa35a2726e 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -181,7 +181,7 @@ module ActiveModel end def attribute_methods_generated? - @attribute_methods_generated ? true : false + @attribute_methods_generated ||= nil end protected diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 624c3647ca..735c61df74 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -72,12 +72,26 @@ module ActiveModel changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h } end + # Map of attributes that were changed when the model was saved. + # person.name # => 'bob' + # person.name = 'robert' + # person.save + # person.previous_changes # => {'name' => ['bob, 'robert']} + def previous_changes + previously_changed_attributes + end + private # Map of change <tt>attr => original value</tt>. def changed_attributes @changed_attributes ||= {} end + # Map of fields that were changed when the model was saved + def previously_changed_attributes + @previously_changed || {} + end + # Handle <tt>*_changed?</tt> for +method_missing+. def attribute_changed?(attr) changed_attributes.include?(attr) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 7a3001174f..7a48960f89 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -1,7 +1,8 @@ require 'active_support/core_ext/string/inflections' +require 'active_support/ordered_hash' module ActiveModel - class Errors < Hash + class Errors < ActiveSupport::OrderedHash include DeprecatedErrorMethods def initialize(base) @@ -113,7 +114,7 @@ module ActiveModel full_messages end - # Translates an error message in it's default scope (<tt>activemodel.errrors.messages</tt>). + # Translates an error message in its default scope (<tt>activemodel.errors.messages</tt>). # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there, # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the # default message (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name, diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb new file mode 100644 index 0000000000..ceaa29dc8c --- /dev/null +++ b/activemodel/lib/active_model/lint.rb @@ -0,0 +1,95 @@ +# You can test whether an object is compliant with the ActiveModel API by +# calling ActiveModel::Lint.test(object). It will emit a Test::Unit +# output that tells you whether your object is fully compliant, or if not, +# which aspects of the API are not implemented. +# +# These tests do not attempt to determine the semantic correctness of the +# returned values. For instance, you could implement valid? to always +# return true, and the tests would pass. It is up to you to ensure that +# the values are semantically meaningful. +# +# Objects you pass in are expected to return a compliant object from a +# call to to_model. It is perfectly fine for to_model to return self. +module ActiveModel + module Lint + def self.test(object, verbosity = 2, output = STDOUT) + require "test/unit" + require "test/unit/ui/console/testrunner" + + test_class = Class.new(::Test::Unit::TestCase) do + include Test + + define_method(:setup) do + assert object.respond_to?(:to_model), "The object should respond_to :to_model" + @object = object.to_model + super + end + end + + ::Test::Unit::UI::Console::TestRunner.new(test_class, verbosity, output).start + end + + module Test + def assert_boolean(name, result) + assert result == true || result == false, "#{name} should be a boolean" + end + + # valid? + # ------ + # + # Returns a boolean that specifies whether the object is in a valid or invalid + # state. + def test_valid? + assert @object.respond_to?(:valid?), "The model should respond to valid?" + assert_boolean "valid?", @object.valid? + end + + # new_record? + # ----------- + # + # Returns a boolean that specifies whether the object has been persisted yet. + # This is used when calculating the URL for an object. If the object is + # not persisted, a form for that object, for instance, will be POSTed to the + # collection. If it is persisted, a form for the object will put PUTed to the + # URL for the object. + def test_new_record? + assert @object.respond_to?(:new_record?), "The model should respond to new_record?" + assert_boolean "new_record?", @object.new_record? + end + + def test_destroyed? + assert @object.respond_to?(:destroyed?), "The model should respond to destroyed?" + assert_boolean "destroyed?", @object.destroyed? + end + + # errors + # ------ + # + # Returns an object that has :[] and :full_messages defined on it. See below + # for more details. + def setup + assert @object.respond_to?(:errors), "The model should respond to errors" + @errors = @object.errors + end + + # This module tests the #errors object + module Errors + # Returns an Array of Strings that are the errors for the attribute in + # question. If localization is used, the Strings should be localized + # for the current locale. If no error is present, this method should + # return an empty Array. + def test_errors_aref + assert @errors[:hello].is_a?(Array), "errors#[] should return an Array" + end + + # Returns an Array of all error messages for the object. Each message + # should contain information about the field, if applicable. + def test_errors_full_messages + assert @errors.full_messages.is_a?(Array), "errors#full_messages should return an Array" + end + end + + include Errors + end + end +end diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 3b230c43b9..d9d1ab8967 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -40,23 +40,6 @@ module ActiveModel observers.each { |o| instantiate_observer(o) } end - # Wraps methods with before and after notifications. - # - # wrap_with_notifications :create, :save, :update, :destroy - def wrap_with_notifications(*methods) - methods.each do |method| - class_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{method}_with_notifications(*args, &block) - notify_observers(:before_#{method}) - result = #{method}_without_notifications(*args, &block) - notify_observers(:after_#{method}) - result - end - EOS - alias_method_chain(method, :notifications) - end - end - protected def instantiate_observer(observer) #:nodoc: # string/symbol diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 7d49e60790..edeb508a08 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -6,10 +6,10 @@ require 'active_support/callbacks' module ActiveModel module Validations extend ActiveSupport::Concern - include ActiveSupport::Callbacks + include ActiveSupport::NewCallbacks included do - define_callbacks :validate + define_callbacks :validate, :scope => :name end module ClassMethods @@ -64,7 +64,7 @@ module ActiveModel attrs = attrs.flatten # Declare the validation. - send(validation_method(options[:on]), options) do |record| + validate options do |record| attrs.each do |attr| value = record.send(:read_attribute_for_validation, attr) next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) @@ -73,10 +73,14 @@ module ActiveModel end end - private - def validation_method(on) - :validate + def validate(*args, &block) + options = args.last + if options.is_a?(Hash) && options.key?(:on) + options[:if] = Array(options[:if]) + options[:if] << "@_on_validate == :#{options[:on]}" end + set_callback(:validate, *args, &block) + end end # Returns the Errors object that holds all information about attribute error messages. @@ -87,7 +91,7 @@ module ActiveModel # Runs all the specified validations and returns true if no errors were added otherwise false. def valid? errors.clear - run_callbacks(:validate) + _run_validate_callbacks errors.empty? end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index ada6e28594..32dbcd82d0 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -31,6 +31,21 @@ module ActiveModel # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The # method, proc or string should return or evaluate to a true or false value. + # + # The following checks can also be supplied with a proc or a symbol which corresponds to a method: + # * <tt>:greater_than</tt> + # * <tt>:greater_than_or_equal_to</tt> + # * <tt>:equal_to</tt> + # * <tt>:less_than</tt> + # * <tt>:less_than_or_equal_to</tt> + # + # class Person < ActiveRecord::Base + # validates_numericality_of :width, :less_than => Proc.new { |person| person.height } + # validates_numericality_of :width, :greater_than => :minimum_weight + # end + # + # + def validates_numericality_of(*attr_names) configuration = { :only_integer => false, :allow_nil => false } configuration.update(attr_names.extract_options!) @@ -38,7 +53,8 @@ module ActiveModel numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys (numericality_options - [ :odd, :even ]).each do |option| - raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric) + value = configuration[option] + raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol) end validates_each(attr_names,configuration) do |record, attr_name, value| @@ -74,6 +90,9 @@ module ActiveModel record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) end else + configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc + configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol + unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) end diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index 72d6b1c6f0..3ff677c137 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -32,7 +32,7 @@ module ActiveModel # can't use validates_each here, because it cannot cope with nonexistent attributes, # while errors.add_on_empty can - send(validation_method(configuration[:on]), configuration) do |record| + validate configuration do |record| record.errors.add_on_blank(attr_names, configuration[:message]) end end diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 851cdfebf0..edc2133ddc 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -51,7 +51,7 @@ module ActiveModel def validates_with(*args) configuration = args.extract_options! - send(validation_method(configuration[:on]), configuration) do |record| + validate configuration do |record| args.each do |klass| klass.new(record, configuration.except(:on, :if, :unless)).validate end diff --git a/activemodel/lib/active_model/validations_repair_helper.rb b/activemodel/lib/active_model/validations_repair_helper.rb index 432e411308..40741e6dbe 100644 --- a/activemodel/lib/active_model/validations_repair_helper.rb +++ b/activemodel/lib/active_model/validations_repair_helper.rb @@ -2,43 +2,34 @@ module ActiveModel module ValidationsRepairHelper extend ActiveSupport::Concern - module Toolbox - def self.record_validations(*model_classes) - model_classes.inject({}) do |repair, klass| - repair[klass] ||= {} - [:validate, :validate_on_create, :validate_on_update].each do |callback| - the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks") - repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup) - end - repair - end - end - - def self.reset_validations(recorded) - recorded.each do |klass, repairs| - [:validate, :validate_on_create, :validate_on_update].each do |callback| - klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback]) - end - end - end - end - module ClassMethods def repair_validations(*model_classes) setup do - @validation_repairs = Toolbox.record_validations(*model_classes) + @_stored_callbacks = {} + model_classes.each do |k| + @_stored_callbacks[k] = k._validate_callbacks.dup + end end teardown do - Toolbox.reset_validations(@validation_repairs) + model_classes.each do |k| + k._validate_callbacks = @_stored_callbacks[k] + k.__update_callbacks(:validate) + end end end end def repair_validations(*model_classes, &block) - validation_repairs = Toolbox.record_validations(*model_classes) + @__stored_callbacks = {} + model_classes.each do |k| + @__stored_callbacks[k] = k._validate_callbacks.dup + end return block.call ensure - Toolbox.reset_validations(validation_repairs) + model_classes.each do |k| + k._validate_callbacks = @__stored_callbacks[k] + k.__update_callbacks(:validate) + end end end end diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb new file mode 100644 index 0000000000..0c233b7b4f --- /dev/null +++ b/activemodel/lib/active_model/version.rb @@ -0,0 +1,9 @@ +module ActiveModel + module VERSION #:nodoc: + MAJOR = 3 + MINOR = 0 + TINY = "pre" + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/activemodel/lib/activemodel.rb b/activemodel/lib/activemodel.rb deleted file mode 100644 index da3133103b..0000000000 --- a/activemodel/lib/activemodel.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_model' diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb new file mode 100644 index 0000000000..ed576a91e2 --- /dev/null +++ b/activemodel/test/cases/lint_test.rb @@ -0,0 +1,50 @@ +require "cases/helper" + +class TestLint < ActiveModel::TestCase + class CompliantObject + def to_model + self + end + + def valid?() true end + def new_record?() true end + def destroyed?() true end + + def errors + obj = Object.new + def obj.[](key) [] end + def obj.full_messages() [] end + obj + end + end + + def assert_output(object, failures, errors, *test_names) + ActiveModel::Lint.test(object, 3, output = StringIO.new) + regex = %r{#{failures} failures, #{errors} errors} + assert_match regex, output.string + + test_names.each do |test_name| + assert_match test_name, output.string + end + end + + def test_valid + assert_output(CompliantObject.new, 0, 0, /test_valid/) + end + + def test_new_record + assert_output(CompliantObject.new, 0, 0, /test_new_record?/) + end + + def test_destroyed + assert_output(CompliantObject.new, 0, 0, /test_destroyed/) + end + + def test_errors_aref + assert_output(CompliantObject.new, 0, 0, /test_errors_aref/) + end + + def test_errors_full_messages + assert_output(CompliantObject.new, 0, 0, /test_errors_aref/) + end +end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index e75d4541a3..4d97af3d13 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -class NamingTest < Test::Unit::TestCase +class NamingTest < ActiveModel::TestCase def setup @model_name = ActiveModel::Name.new('Post::TrackBack') end diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb index fbf93c19ef..e23bda0528 100644 --- a/activemodel/test/cases/observing_test.rb +++ b/activemodel/test/cases/observing_test.rb @@ -78,6 +78,7 @@ class ObserverTest < ActiveModel::TestCase def teardown FooObserver.instance_eval do + undef_method :observed_classes alias_method :observed_classes, :original_observed_classes end end diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb index 6227aedc39..81df52fcb9 100644 --- a/activemodel/test/cases/serializeration/json_serialization_test.rb +++ b/activemodel/test/cases/serializeration/json_serialization_test.rb @@ -1,12 +1,14 @@ require 'cases/helper' require 'models/contact' +require 'active_support/core_ext/object/instance_variables' class Contact + extend ActiveModel::Naming include ActiveModel::Serializers::JSON def attributes instance_values - end + end unless method_defined?(:attributes) end class JsonSerializationTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb index 428e5a6bd1..6340aad531 100644 --- a/activemodel/test/cases/serializeration/xml_serialization_test.rb +++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb @@ -1,12 +1,14 @@ require 'cases/helper' require 'models/contact' +require 'active_support/core_ext/object/instance_variables' class Contact + extend ActiveModel::Naming include ActiveModel::Serializers::Xml def attributes instance_values - end + end unless method_defined?(:attributes) end module Admin diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 37bba5e95e..443a81c6ac 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -3,9 +3,9 @@ require 'cases/tests_database' require 'models/person' -class I18nGenerateMessageValidationTest < Test::Unit::TestCase +class I18nGenerateMessageValidationTest < ActiveModel::TestCase def setup - reset_callbacks Person + Person.reset_callbacks(:validate) @person = Person.new @old_load_path, @old_backend = I18n.load_path, I18n.backend @@ -45,12 +45,6 @@ class I18nGenerateMessageValidationTest < Test::Unit::TestCase I18n.backend = @old_backend end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) def test_generate_message_inclusion_with_default_message assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index cc68d847a2..fc4f1926b0 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -7,8 +7,7 @@ class I18nValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase def setup - reset_callbacks Person - + Person.reset_callbacks(:validate) @person = Person.new @old_load_path, @old_backend = I18n.load_path, I18n.backend @@ -18,17 +17,11 @@ class I18nValidationTest < ActiveModel::TestCase end def teardown - reset_callbacks Person + Person.reset_callbacks(:validate) I18n.load_path.replace @old_load_path I18n.backend = @old_backend end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated assert_not_deprecated do default = "%s interpolation syntax was deprecated" @@ -107,32 +100,6 @@ class I18nValidationTest < ActiveModel::TestCase @person.valid? end - def test_validates_length_of_within_generates_message_with_title_too_short - Person.validates_length_of :title, :within => 3..5 - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long - Person.validates_length_of :title, :within => 3..5 - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @person.valid? - end - # validates_length_of :within w/ mocha def test_validates_length_of_within_generates_message_with_title_too_short @@ -280,7 +247,7 @@ class I18nValidationTest < ActiveModel::TestCase @person.valid? end - def test_validates_numericality_of_odd_generates_message_with_custom_default_message + def test_validates_numericality_of_less_than_odd_generates_message_with_custom_default_message Person.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' @person.title = 1 @person.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) @@ -384,24 +351,6 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ['global message'], @person.errors[:title] end - def test_validates_length_of_is_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activemodel => {:errors => {:messages => {:wrong_length => 'global message'}}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_length_of_is_finds_global_default_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:messages => {:wrong_length => 'global message'}}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['global message'], @person.errors[:title] - end - - # validates_format_of w/o mocha def test_validates_format_of_finds_custom_model_key_translation @@ -576,4 +525,4 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ["I am a custom error"], @person.errors[:title] end -end
\ No newline at end of file +end diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 0af6eb69ce..d3201966dc 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -106,6 +106,24 @@ class NumericalityValidationTest < ActiveModel::TestCase valid!([2]) end + def test_validates_numericality_with_proc + Topic.send(:define_method, :min_approved, lambda { 5 }) + Topic.validates_numericality_of :approved, :greater_than_or_equal_to => Proc.new {|topic| topic.min_approved } + + invalid!([3, 4]) + valid!([5, 6]) + Topic.send(:remove_method, :min_approved) + end + + def test_validates_numericality_with_symbol + Topic.send(:define_method, :max_approved, lambda { 5 }) + Topic.validates_numericality_of :approved, :less_than_or_equal_to => :max_approved + + invalid!([6]) + valid!([4, 5]) + Topic.send(:remove_method, :max_approved) + end + def test_validates_numericality_with_numeric_message Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}" topic = Topic.new("title" => "numeric test", "approved" => 10) diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index bb6fb91774..90b0951a77 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -5,6 +5,7 @@ require 'cases/tests_database' require 'models/topic' require 'models/developer' require 'models/person' +require 'models/custom_reader' class PresenceValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index f55fdc5864..c290b49a28 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -1,9 +1,11 @@ # encoding: utf-8 require 'cases/helper' +require 'cases/tests_database' require 'models/topic' class ValidatesWithTest < ActiveRecord::TestCase + include ActiveModel::TestsDatabase include ActiveModel::ValidationsRepairHelper repair_validations(Topic) diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 0b340e68bf..78565177d8 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -121,8 +121,8 @@ class ValidationsTest < ActiveModel::TestCase end def test_invalid_validator - Topic.validate 3 - assert_raise(ArgumentError) { t = Topic.create } + Topic.validate :i_dont_exist + assert_raise(NameError) { t = Topic.create } end def test_errors_to_xml @@ -141,6 +141,22 @@ class ValidationsTest < ActiveModel::TestCase t = Topic.new("title" => "") assert !t.valid? assert_equal "can't be blank", t.errors["title"].first + Topic.validates_presence_of :title, :author_name + Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')} + Topic.validates_length_of :title, :content, :minimum => 2 + + t = Topic.new :title => '' + assert !t.valid? + + assert_equal :title, key = t.errors.keys.first + assert_equal "can't be blank", t.errors[key].first + assert_equal 'is too short (minimum is 2 characters)', t.errors[key].second + assert_equal :author_name, key = t.errors.keys.second + assert_equal "can't be blank", t.errors[key].first + assert_equal :author_email_address, key = t.errors.keys.third + assert_equal 'will never be valid', t.errors[key].first + assert_equal :content, key = t.errors.keys.fourth + assert_equal 'is too short (minimum is 2 characters)', t.errors[key].first end def test_invalid_should_be_the_opposite_of_valid @@ -189,4 +205,4 @@ class ValidationsTest < ActiveModel::TestCase all_errors = t.errors.to_a assert_deprecated { assert_equal all_errors, t.errors.each_full{|err| err} } end -end
\ No newline at end of file +end diff --git a/activemodel/test/models/reply.rb b/activemodel/test/models/reply.rb index acfd801674..e86692677f 100644 --- a/activemodel/test/models/reply.rb +++ b/activemodel/test/models/reply.rb @@ -2,11 +2,11 @@ require 'models/topic' class Reply < Topic validate :errors_on_empty_content - validate_on_create :title_is_wrong_create + validate :title_is_wrong_create, :on => :create validate :check_empty_title - validate_on_create :check_content_mismatch - validate_on_update :check_wrong_update + validate :check_content_mismatch, :on => :create + validate :check_wrong_update, :on => :update attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read diff --git a/activerecord/Rakefile b/activerecord/Rakefile index a9e4a92e37..1d8062e042 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -191,40 +191,7 @@ task :rdoc do end -# Create compressed packages - -dist_dirs = [ "lib", "test", "examples" ] - -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Implements the ActiveRecord pattern for ORM." - s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.} - - s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG" ] - dist_dirs.each do |dir| - s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - end - - s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD) - - s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" - s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" - s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite3" - s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite3" - s.require_path = 'lib' - s.autorequire = 'active_record' - - s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.homepage = "http://www.rubyonrails.org" - s.rubyforge_project = "activerecord" -end +spec = eval(File.read('activerecord.gemspec')) Rake::GemPackageTask.new(spec) do |p| p.gem_spec = spec diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec new file mode 100644 index 0000000000..7b6791adc8 --- /dev/null +++ b/activerecord/activerecord.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'activerecord' + s.version = '3.0.pre' + s.summary = "Implements the ActiveRecord pattern for ORM." + s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.} + + s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*'] + + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('activemodel', '= 3.0.pre') + + s.require_path = 'lib' + s.autorequire = 'active_record' + + s.has_rdoc = true + s.extra_rdoc_files = %w( README ) + s.rdoc_options.concat ['--main', 'README'] + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "activerecord" +end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 016745add2..0888c41396 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -280,9 +280,10 @@ module ActiveRecord # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be # aware of, mostly involving the saving of associated objects. # - # Unless you enable the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>, - # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association, - # in which case the members are always saved. + # Unless you set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>, + # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it + # to +true+ will _always_ save the members, whereas setting it to +false+ will + # _never_ save the members. # # === One-to-one associations # @@ -937,7 +938,9 @@ module ActiveRecord # if the real class name is Person, you'll have to specify it with this option. # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as <tt>rank = 5</tt>. + # SQL fragment, such as <tt>rank = 5</tt>. Record creation from the association is scoped if a hash + # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create an enabled account with <tt>@company.create_account</tt> + # or <tt>@company.build_account</tt>. # [:order] # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment, # such as <tt>last_name, first_name DESC</tt>. @@ -1491,24 +1494,43 @@ module ActiveRecord end before_destroy method_name when :delete_all - module_eval %Q{ - before_destroy do |record| # before_destroy do |record| - delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record, - "#{reflection.name}", # "posts", - #{reflection.class_name}, # Post, - %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) - end # end - } + # before_destroy do |record| + # self.class.send(:delete_all_has_many_dependencies, + # record, + # "posts", + # Post, + # %@...@) # this is a string literal like %(...) + # end + # end + module_eval <<-CALLBACK + before_destroy do |record| + self.class.send(:delete_all_has_many_dependencies, + record, + "#{reflection.name}", + #{reflection.class_name}, + %@#{dependent_conditions}@) + end + CALLBACK when :nullify - module_eval %Q{ - before_destroy do |record| # before_destroy do |record| - nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record, - "#{reflection.name}", # "posts", - #{reflection.class_name}, # Post, - "#{reflection.primary_key_name}", # "user_id", - %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) - end # end - } + # before_destroy do |record| + # self.class.send(:nullify_has_many_dependencies, + # record, + # "posts", + # Post, + # "user_id", + # %@...@) # this is a string literal like %(...) + # end + # end + module_eval <<-CALLBACK + before_destroy do |record| + self.class.send(:nullify_has_many_dependencies, + record, + "#{reflection.name}", + #{reflection.class_name}, + "#{reflection.primary_key_name}", + %@#{dependent_conditions}@) + end + CALLBACK else raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})" end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index da1fbfe0b7..4672b0723e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -24,8 +24,8 @@ module ActiveRecord def has_primary_key? return @has_primary_key unless @has_primary_key.nil? - @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && - ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) + @has_primary_key = (@owner.connection.supports_primary_key? && + @owner.connection.primary_key(@reflection.options[:join_table])) end protected diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index c2568d0c0c..b85a40b2e5 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -8,18 +8,21 @@ module ActiveRecord def create(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.create_association(attrs) end end def create!(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.create_association!(attrs) end end def build(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.build_association(attrs) end end @@ -128,6 +131,12 @@ module ActiveRecord inverse = @reflection.inverse_of return !inverse.nil? end + + def merge_with_conditions(attrs={}) + attrs ||= {} + attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) + attrs + end end end end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index b6c4df2a49..4df0f1af69 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -19,6 +19,7 @@ module ActiveRecord # Attempts to +save+ the record and clears changed attributes if successful. def save_with_dirty(*args) #:nodoc: if status = save_without_dirty(*args) + @previously_changed = changes changed_attributes.clear end status @@ -26,12 +27,18 @@ module ActiveRecord # Attempts to <tt>save!</tt> the record and clears changed attributes if successful. def save_with_dirty!(*args) #:nodoc: - save_without_dirty!(*args).tap { changed_attributes.clear } + save_without_dirty!(*args).tap do + @previously_changed = changes + changed_attributes.clear + end end # <tt>reload</tt> the record and clears changed attributes. def reload_with_dirty(*args) #:nodoc: - reload_without_dirty(*args).tap { changed_attributes.clear } + reload_without_dirty(*args).tap do + previously_changed_attributes.clear + changed_attributes.clear + end end private diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c1bc8423a9..8f37fcd515 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -158,7 +158,7 @@ module ActiveRecord def add_autosave_association_callbacks(reflection) save_method = "autosave_associated_records_for_#{reflection.name}" validation_method = "validate_associated_records_for_#{reflection.name}" - validate validation_method + force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) case reflection.macro when :has_many, :has_and_belongs_to_many @@ -169,7 +169,10 @@ module ActiveRecord after_create save_method after_update save_method - define_method(validation_method) { validate_collection_association(reflection) } + if force_validation || (reflection.macro == :has_many && reflection.options[:validate] != false) + define_method(validation_method) { validate_collection_association(reflection) } + validate validation_method + end else case reflection.macro when :has_one @@ -179,7 +182,11 @@ module ActiveRecord define_method(save_method) { save_belongs_to_association(reflection) } before_save save_method end - define_method(validation_method) { validate_single_association(reflection) } + + if force_validation + define_method(validation_method) { validate_single_association(reflection) } + validate validation_method + end end end end @@ -223,10 +230,8 @@ module ActiveRecord # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is # turned on for the association specified by +reflection+. def validate_single_association(reflection) - if reflection.options[:validate] == true || reflection.options[:autosave] == true - if (association = association_instance_get(reflection.name)) && !association.target.nil? - association_valid?(reflection, association) - end + if (association = association_instance_get(reflection.name)) && !association.target.nil? + association_valid?(reflection, association) end end @@ -234,7 +239,7 @@ module ActiveRecord # <tt>:autosave</tt> is turned on for the association specified by # +reflection+. def validate_collection_association(reflection) - if reflection.options[:validate] != false && association = association_instance_get(reflection.name) + if association = association_instance_get(reflection.name) if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave]) records.each { |record| association_valid?(reflection, record) } end @@ -243,15 +248,15 @@ module ActiveRecord # Returns whether or not the association is valid and applies any errors to # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt> - # enabled records if they're marked_for_destruction?. + # enabled records if they're marked_for_destruction? or destroyed. def association_valid?(reflection, association) + return true if association.destroyed? || association.marked_for_destruction? + unless valid = association.valid? if reflection.options[:autosave] - unless association.marked_for_destruction? - association.errors.each do |attribute, message| - attribute = "#{reflection.name}_#{attribute}" - errors[attribute] << message if errors[attribute].empty? - end + association.errors.each do |attribute, message| + attribute = "#{reflection.name}_#{attribute}" + errors[attribute] << message if errors[attribute].empty? end else errors.add(reflection.name) @@ -281,9 +286,11 @@ module ActiveRecord if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) records.each do |record| + next if record.destroyed? + if autosave && record.marked_for_destruction? association.destroy(record) - elsif @new_record_before_save || record.new_record? + elsif autosave != false && (@new_record_before_save || record.new_record?) if autosave association.send(:insert_record, record, false, false) else @@ -309,14 +316,17 @@ module ActiveRecord # This all happens inside a transaction, _if_ the Transactions module is included into # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_has_one_association(reflection) - if (association = association_instance_get(reflection.name)) && !association.target.nil? + if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? association.destroy - elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave - association[reflection.primary_key_name] = id - association.save(!autosave) + else + key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id + if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave) + association[reflection.primary_key_name] = key + association.save(!autosave) + end end end end @@ -330,12 +340,12 @@ module ActiveRecord # This all happens inside a transaction, _if_ the Transactions module is included into # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_belongs_to_association(reflection) - if association = association_instance_get(reflection.name) + if (association = association_instance_get(reflection.name)) && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? association.destroy - else + elsif autosave != false association.save(!autosave) if association.new_record? || autosave if association.updated? diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 60e69f020c..668506c01a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1672,46 +1672,31 @@ module ActiveRecord #:nodoc: # single-table inheritance model that makes it possible to create # objects of different types from the same table. def instantiate(record) - object = - if subclass_name = record[inheritance_column] - # No type given. - if subclass_name.empty? - allocate + object = find_sti_class(record[inheritance_column]).allocate - else - # Ignore type if no column is present since it was probably - # pulled in from a sloppy join. - unless columns_hash.include?(inheritance_column) - allocate + object.instance_variable_set(:'@attributes', record) + object.instance_variable_set(:'@attributes_cache', {}) - else - begin - compute_type(subclass_name).allocate - rescue NameError - raise SubclassNotFound, - "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " + - "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " + - "Please rename this column if you didn't intend it to be used for storing the inheritance class " + - "or overwrite #{self.to_s}.inheritance_column to use another column for that information." - end - end - end - else - allocate - end + object.send(:_run_find_callbacks) + object.send(:_run_initialize_callbacks) - object.instance_variable_set("@attributes", record) - object.instance_variable_set("@attributes_cache", Hash.new) - - if object.respond_to_without_attributes?(:after_find) - object.send(:callback, :after_find) - end + object + end - if object.respond_to_without_attributes?(:after_initialize) - object.send(:callback, :after_initialize) + def find_sti_class(type_name) + if type_name.blank? || !columns_hash.include?(inheritance_column) + self + else + begin + compute_type(type_name) + rescue NameError + raise SubclassNotFound, + "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " + + "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " + + "Please rename this column if you didn't intend it to be used for storing the inheritance class " + + "or overwrite #{name}.inheritance_column to use another column for that information." + end end - - object end # Nest the type name in the same module as this class. @@ -2467,7 +2452,7 @@ module ActiveRecord #:nodoc: self.attributes = attributes unless attributes.nil? self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create) result = yield self if block_given? - callback(:after_initialize) if respond_to_without_attributes?(:after_initialize) + _run_initialize_callbacks result end @@ -2960,7 +2945,9 @@ module ActiveRecord #:nodoc: end def log_protected_attribute_removal(*attributes) - logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}" + if logger + logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}" + end end # The primary key and inheritance column can never be set by mass-assignment for security reasons. diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 4a2ec5bf95..40a25811c4 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -10,16 +10,14 @@ module ActiveRecord # * (-) <tt>save</tt> # * (-) <tt>valid</tt> # * (1) <tt>before_validation</tt> - # * (2) <tt>before_validation_on_create</tt> # * (-) <tt>validate</tt> # * (-) <tt>validate_on_create</tt> - # * (3) <tt>after_validation</tt> - # * (4) <tt>after_validation_on_create</tt> - # * (5) <tt>before_save</tt> - # * (6) <tt>before_create</tt> + # * (2) <tt>after_validation</tt> + # * (3) <tt>before_save</tt> + # * (4) <tt>before_create</tt> # * (-) <tt>create</tt> - # * (7) <tt>after_create</tt> - # * (8) <tt>after_save</tt> + # * (5) <tt>after_create</tt> + # * (6) <tt>after_save</tt> # # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each @@ -212,162 +210,122 @@ module ActiveRecord # instead of quietly returning +false+. module Callbacks extend ActiveSupport::Concern + include ActiveSupport::NewCallbacks - CALLBACKS = %w( - after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation - after_validation before_validation_on_create after_validation_on_create before_validation_on_update - after_validation_on_update before_destroy after_destroy - ) + CALLBACKS = [ + :after_initialize, :after_find, :before_validation, :after_validation, + :before_save, :around_save, :after_save, :before_create, :around_create, + :after_create, :before_update, :around_update, :after_update, + :before_destroy, :around_destroy, :after_destroy + ] included do - extend Observable - [:create_or_update, :valid?, :create, :update, :destroy].each do |method| alias_method_chain method, :callbacks end - include ActiveSupport::Callbacks - define_callbacks *CALLBACKS + define_callbacks :initialize, :find, :save, :create, :update, :destroy, + :validation, :terminator => "result == false", :scope => [:kind, :name] end - # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>. - #def after_find() end + module ClassMethods + def after_initialize(*args, &block) + options = args.extract_options! + options[:prepend] = true + set_callback(:initialize, :after, *(args << options), &block) + end + + def after_find(*args, &block) + options = args.extract_options! + options[:prepend] = true + set_callback(:find, :after, *(args << options), &block) + end + + [:save, :create, :update, :destroy].each do |callback| + module_eval <<-CALLBACKS, __FILE__, __LINE__ + def before_#{callback}(*args, &block) + set_callback(:#{callback}, :before, *args, &block) + end - # Is called after the object has been instantiated by a call to <tt>Base.new</tt>. - #def after_initialize() end + def around_#{callback}(*args, &block) + set_callback(:#{callback}, :around, *args, &block) + end - # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save). - def before_save() end + def after_#{callback}(*args, &block) + options = args.extract_options! + options[:prepend] = true + options[:if] = Array(options[:if]) << "!halted && value != false" + set_callback(:#{callback}, :after, *(args << options), &block) + end + CALLBACKS + end + + def before_validation(*args, &block) + options = args.extract_options! + if options[:on] + options[:if] = Array(options[:if]) + options[:if] << "@_on_validate == :#{options[:on]}" + end + set_callback(:validation, :before, *(args << options), &block) + end + + def after_validation(*args, &block) + options = args.extract_options! + options[:if] = Array(options[:if]) + options[:if] << "!halted" + options[:if] << "@_on_validate == :#{options[:on]}" if options[:on] + options[:prepend] = true + set_callback(:validation, :after, *(args << options), &block) + end + + def method_added(meth) + super + if CALLBACKS.include?(meth.to_sym) + ActiveSupport::Deprecation.warn("Base##{meth} has been deprecated, please use Base.#{meth} :method instead", caller[0,1]) + send(meth.to_sym, meth.to_sym) + end + end + end - # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save). - # Note that this callback is still wrapped in the transaction around +save+. For example, if you - # invoke an external indexer at this point it won't see the changes in the database. - # - # class Contact < ActiveRecord::Base - # after_save { logger.info( 'New contact saved!' ) } - # end - def after_save() end def create_or_update_with_callbacks #:nodoc: - return false if callback(:before_save) == false - if result = create_or_update_without_callbacks - callback(:after_save) + _run_save_callbacks do + create_or_update_without_callbacks end - result end private :create_or_update_with_callbacks - # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists). - def before_create() end - - # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists). - # Note that this callback is still wrapped in the transaction around +save+. For example, if you - # invoke an external indexer at this point it won't see the changes in the database. - # - # class Contact < ActiveRecord::Base - # after_create { |record| logger.info( "Contact #{record.id} was created." ) } - # end - def after_create() end def create_with_callbacks #:nodoc: - return false if callback(:before_create) == false - result = create_without_callbacks - callback(:after_create) - result + _run_create_callbacks do + create_without_callbacks + end end private :create_with_callbacks - # Is called _before_ <tt>Base.save</tt> on existing objects that have a record. - # - # class Contact < ActiveRecord::Base - # before_update { |record| logger.info( "Contact #{record.id} is about to be updated." ) } - # end - def before_update() end - - # Is called _after_ <tt>Base.save</tt> on existing objects that have a record. - # Note that this callback is still wrapped in the transaction around +save+. For example, if you - # invoke an external indexer at this point it won't see the changes in the database. - # - # class Contact < ActiveRecord::Base - # after_update { |record| logger.info( "Contact #{record.id} was updated." ) } - # end - def after_update() end - def update_with_callbacks(*args) #:nodoc: - return false if callback(:before_update) == false - result = update_without_callbacks(*args) - callback(:after_update) - result + _run_update_callbacks do + update_without_callbacks(*args) + end end private :update_with_callbacks - # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call). - def before_validation() end - - # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call). - def after_validation() end - - # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects - # that haven't been saved yet (no record exists). - def before_validation_on_create() end - - # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects - # that haven't been saved yet (no record exists). - def after_validation_on_create() end - - # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on - # existing objects that have a record. - def before_validation_on_update() end - - # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on - # existing objects that have a record. - def after_validation_on_update() end - def valid_with_callbacks? #:nodoc: - return false if callback(:before_validation) == false - if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end - return false if false == result - - result = valid_without_callbacks? - - callback(:after_validation) - if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end - - return result + @_on_validate = new_record? ? :create : :update + _run_validation_callbacks do + valid_without_callbacks? + end end - # Is called _before_ <tt>Base.destroy</tt>. - # - # Note: If you need to _destroy_ or _nullify_ associated records first, - # use the <tt>:dependent</tt> option on your associations. - # - # class Contact < ActiveRecord::Base - # after_destroy { |record| logger.info( "Contact #{record.id} is about to be destroyed." ) } - # end - def before_destroy() end - - # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen). - # - # class Contact < ActiveRecord::Base - # after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) } - # end - def after_destroy() end def destroy_with_callbacks #:nodoc: - return false if callback(:before_destroy) == false - result = destroy_without_callbacks - callback(:after_destroy) - result + _run_destroy_callbacks do + destroy_without_callbacks + end end - private - def callback(method) - result = run_callbacks(method) { |result, object| false == result } - - if result != false && respond_to_without_attributes?(method) - result = send(method) - end - - notify_observers(method) - - return result + def deprecated_callback_method(symbol) #:nodoc: + if respond_to?(symbol) + ActiveSupport::Deprecation.warn("Base##{symbol} has been deprecated, please use Base.#{symbol} :method instead") + send(symbol) end + end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index fab70f34b9..78c7a4b697 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -201,16 +201,12 @@ module ActiveRecord protected def log(sql, name) - if block_given? - result = nil - ms = Benchmark.ms { result = yield } - @runtime += ms - log_info(sql, name, ms) - result - else - log_info(sql, name, 0) - nil + event = ActiveSupport::Orchestra.instrument(:sql, :sql => sql, :name => name) do + yield if block_given? end + @runtime += event.duration + log_info(sql, name, event.duration) + event.result rescue Exception => e # Log message and raise exception. # Set last_verification to 0, so that connection gets verified @@ -221,10 +217,10 @@ module ActiveRecord raise translate_exception(e, message) end - def translate_exception(e, message) - # override in derived class - ActiveRecord::StatementInvalid.new(message) - end + def translate_exception(e, message) + # override in derived class + ActiveRecord::StatementInvalid.new(message) + end def format_log_entry(message, dump = nil) if ActiveRecord::Base.colorize_logging diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index d3ca7c819f..ad36ff22e3 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -8,7 +8,8 @@ module MysqlCompat #:nodoc: raise 'Mysql not loaded' unless defined?(::Mysql) target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes - return if target.instance_methods.include?('all_hashes') + return if target.instance_methods.include?('all_hashes') || + target.instance_methods.include?(:all_hashes) # Ruby driver has a version string and returns null values in each_hash # C driver >= 2.7 returns null values in each_hash @@ -64,12 +65,15 @@ module ActiveRecord raise end end + MysqlCompat.define_all_hashes_method! mysql = Mysql.init mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey] - ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config) + default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0 + options = [host, username, password, database, port, socket, default_flags] + ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config) end end diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 320659596f..fcc9ebb4af 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -1,25 +1,3 @@ -# Copyright (c) 2006 Shugo Maeda <shugo@ruby-lang.org> -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject -# to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - module ActiveRecord module Locking # Locking::Pessimistic provides support for row-level locking using diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index bc4cca7855..3c8140816c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -66,10 +66,10 @@ module ActiveRecord # accepts_nested_attributes_for :avatar, :allow_destroy => true # end # - # Now, when you add the <tt>_delete</tt> key to the attributes hash, with a + # Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a # value that evaluates to +true+, you will destroy the associated model: # - # member.avatar_attributes = { :id => '2', :_delete => '1' } + # member.avatar_attributes = { :id => '2', :_destroy => '1' } # member.avatar.marked_for_destruction? # => true # member.save # member.avatar #=> nil @@ -89,14 +89,14 @@ module ActiveRecord # the attribute hash. # # For each hash that does _not_ have an <tt>id</tt> key a new record will - # be instantiated, unless the hash also contains a <tt>_delete</tt> key + # be instantiated, unless the hash also contains a <tt>_destroy</tt> key # that evaluates to +true+. # # params = { :member => { # :name => 'joe', :posts_attributes => [ # { :title => 'Kari, the awesome Ruby documentation browser!' }, # { :title => 'The egalitarian assumption of the modern citizen' }, - # { :title => '', :_delete => '1' } # this will be ignored + # { :title => '', :_destroy => '1' } # this will be ignored # ] # }} # @@ -144,7 +144,7 @@ module ActiveRecord # By default the associated records are protected from being destroyed. If # you want to destroy any of the associated records through the attributes # hash, you have to enable it first using the <tt>:allow_destroy</tt> - # option. This will allow you to also use the <tt>_delete</tt> key to + # option. This will allow you to also use the <tt>_destroy</tt> key to # destroy existing records: # # class Member < ActiveRecord::Base @@ -153,7 +153,7 @@ module ActiveRecord # end # # params = { :member => { - # :posts_attributes => [{ :id => '2', :_delete => '1' }] + # :posts_attributes => [{ :id => '2', :_destroy => '1' }] # }} # # member.attributes = params['member'] @@ -176,14 +176,14 @@ module ActiveRecord # Supported options: # [:allow_destroy] # If true, destroys any members from the attributes hash with a - # <tt>_delete</tt> key and a value that evaluates to +true+ + # <tt>_destroy</tt> key and a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'). This option is off by default. # [:reject_if] # Allows you to specify a Proc that checks whether a record should be # built for a certain attribute hash. The hash is passed to the Proc # and the Proc should return either +true+ or +false+. When no Proc # is specified a record will be built for all attribute hashes that - # do not have a <tt>_delete</tt> that evaluates to true. + # do not have a <tt>_destroy</tt> value that evaluates to true. # Passing <tt>:all_blank</tt> instead of a Proc will create a proc # that will reject a record where all the attributes are blank. # @@ -236,15 +236,25 @@ module ActiveRecord # destruction of this association. # # See ActionView::Helpers::FormHelper::fields_for for more info. - def _delete + def _destroy marked_for_destruction? end + # Deal with deprecated _delete. + # + def _delete #:nodoc: + ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead." + _destroy + end + private # Attribute hash keys that should not be assigned as normal attributes. # These hash keys are nested attributes implementation details. - UNASSIGNABLE_KEYS = %w{ id _delete } + # + # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are + # removed. + UNASSIGNABLE_KEYS = %w( id _destroy _delete ) # Assigns the given attributes to the association. # @@ -253,14 +263,19 @@ module ActiveRecord # record will be built. # # If the given attributes include a matching <tt>:id</tt> attribute _and_ a - # <tt>:_delete</tt> key set to a truthy value, then the existing record + # <tt>:_destroy</tt> key set to a truthy value, then the existing record # will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes, allow_destroy) attributes = attributes.stringify_keys if attributes['id'].blank? unless reject_new_record?(association_name, attributes) - send("build_#{association_name}", attributes.except(*UNASSIGNABLE_KEYS)) + method = "build_#{association_name}" + if respond_to?(method) + send(method, attributes.except(*UNASSIGNABLE_KEYS)) + else + raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?" + end end elsif (existing_record = send(association_name)) && existing_record.id.to_s == attributes['id'].to_s assign_to_or_mark_for_destruction(existing_record, attributes, allow_destroy) @@ -272,7 +287,7 @@ module ActiveRecord # Hashes with an <tt>:id</tt> value matching an existing associated record # will update that record. Hashes without an <tt>:id</tt> value will build # a new record for the association. Hashes with a matching <tt>:id</tt> - # value and a <tt>:_delete</tt> key set to a truthy value will mark the + # value and a <tt>:_destroy</tt> key set to a truthy value will mark the # matched record for destruction. # # For example: @@ -280,7 +295,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, { # '1' => { :id => '1', :name => 'Peter' }, # '2' => { :name => 'John' }, - # '3' => { :id => '2', :_delete => true } + # '3' => { :id => '2', :_destroy => true } # }) # # Will update the name of the Person with ID 1, build a new associated @@ -292,7 +307,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, [ # { :id => '1', :name => 'Peter' }, # { :name => 'John' }, - # { :id => '2', :_delete => true } + # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection, allow_destroy) unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) @@ -317,25 +332,26 @@ module ActiveRecord end # Updates a record with the +attributes+ or marks it for destruction if - # +allow_destroy+ is +true+ and has_delete_flag? returns +true+. + # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy) - if has_delete_flag?(attributes) && allow_destroy + if has_destroy_flag?(attributes) && allow_destroy record.mark_for_destruction else record.attributes = attributes.except(*UNASSIGNABLE_KEYS) end end - # Determines if a hash contains a truthy _delete key. - def has_delete_flag?(hash) - ConnectionAdapters::Column.value_to_boolean hash['_delete'] + # Determines if a hash contains a truthy _destroy key. + def has_destroy_flag?(hash) + ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) || + ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation. end # Determines if a new record should be build by checking for - # has_delete_flag? or if a <tt>:reject_if</tt> proc exists for this + # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this # association and evaluates to +true+. def reject_new_record?(association_name, attributes) - has_delete_flag?(attributes) || + has_destroy_flag?(attributes) || self.class.reject_new_nested_attributes_procs[association_name].try(:call, attributes) end end diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index a34ff4a47a..4e05b819b5 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -1,6 +1,3 @@ -require 'singleton' -require 'set' - module ActiveRecord # Observer classes respond to lifecycle callbacks to implement trigger-like # behavior outside the original class. This is a great way to reduce the @@ -88,11 +85,17 @@ module ActiveRecord # singletons and that call instantiates and registers them. # class Observer < ActiveModel::Observer + extlib_inheritable_accessor(:observed_methods){ [] } + def initialize super observed_subclasses.each { |klass| add_observer!(klass) } end + def self.method_added(method) + observed_methods << method if ActiveRecord::Callbacks::CALLBACKS.include?(method.to_sym) + end + protected def observed_subclasses observed_classes.sum([]) { |klass| klass.send(:subclasses) } @@ -100,8 +103,15 @@ module ActiveRecord def add_observer!(klass) super - if respond_to?(:after_find) && !klass.method_defined?(:after_find) - klass.class_eval 'def after_find() end' + + # Check if a notifier callback was already added to the given class. If + # it was not, add it. + self.observed_methods.each do |method| + callback = :"_notify_observers_for_#{method}" + if (klass.instance_methods & [callback, callback.to_s]).empty? + klass.class_eval "def #{callback}; notify_observers(:#{method}); end" + klass.send(method, callback) + end end end end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 5fc41cf054..ab79b520a2 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -110,8 +110,6 @@ module ActiveRecord included do alias_method_chain :save, :validation alias_method_chain :save!, :validation - - define_callbacks :validate_on_create, :validate_on_update end module ClassMethods @@ -127,17 +125,6 @@ module ActiveRecord object end end - - def validation_method(on) - case on - when :create - :validate_on_create - when :update - :validate_on_update - else - :validate - end - end end module InstanceMethods @@ -165,27 +152,15 @@ module ActiveRecord def valid? errors.clear - run_callbacks(:validate) + @_on_validate = new_record? ? :create : :update + _run_validate_callbacks - if respond_to?(:validate) - ActiveSupport::Deprecation.warn("Base#validate has been deprecated, please use Base.validate :method instead") - validate - end + deprecated_callback_method(:validate) if new_record? - run_callbacks(:validate_on_create) - - if respond_to?(:validate_on_create) - ActiveSupport::Deprecation.warn("Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead") - validate_on_create - end + deprecated_callback_method(:validate_on_create) else - run_callbacks(:validate_on_update) - - if respond_to?(:validate_on_update) - ActiveSupport::Deprecation.warn("Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead") - validate_on_update - end + deprecated_callback_method(:validate_on_update) end errors.empty? diff --git a/activerecord/lib/activerecord.rb b/activerecord/lib/activerecord.rb deleted file mode 100644 index cd62b2afdc..0000000000 --- a/activerecord/lib/activerecord.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_record' diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index f7178f2c5e..b193f8d8ba 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -813,7 +813,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm = companies(:first_firm) clients = firm.clients assert_equal 2, clients.length - clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end } + clients.last.instance_eval { def overwrite_to_raise() raise "Trigger rollback" end } firm.destroy rescue "do nothing" diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 7140de77ea..289c89d1e2 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -36,6 +36,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_equal accounts(:rails_core_account), firm.account_using_primary_key end + def test_update_with_foreign_and_primary_keys + firm = companies(:first_firm) + account = firm.account_using_foreign_and_primary_keys + assert_equal Account.find_by_firm_name(firm.name), account + firm.save + firm.reload + assert_equal account, firm.account_using_foreign_and_primary_keys + end + def test_can_marshal_has_one_association_with_nil_target firm = Firm.new assert_nothing_raised do @@ -306,4 +315,22 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end + + def test_build_respects_hash_condition + account = companies(:first_firm).build_account_limit_500_with_hash_conditions + assert account.save + assert_equal 500, account.credit_limit + end + + def test_create_respects_hash_condition + account = companies(:first_firm).create_account_limit_500_with_hash_conditions + assert !account.new_record? + assert_equal 500, account.credit_limit + end + + def test_create!_respects_hash_condition + account = companies(:first_firm).create_account_limit_500_with_hash_conditions! + assert !account.new_record? + assert_equal 500, account.credit_limit + end end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 271086af8e..9164701601 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -443,6 +443,70 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa end end +class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase + def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship + new_account = Account.new("credit_limit" => 1000) + new_firm = Firm.new("name" => "some firm") + + assert new_firm.new_record? + new_account.firm = new_firm + new_account.save! + + assert !new_firm.new_record? + + new_account = Account.new("credit_limit" => 1000) + new_autosaved_firm = Firm.new("name" => "some firm") + + assert new_autosaved_firm.new_record? + new_account.unautosaved_firm = new_autosaved_firm + new_account.save! + + assert new_autosaved_firm.new_record? + end + + def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.account = account + firm.save! + + assert !account.new_record? + + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + firm.unautosaved_account = account + + assert account.new_record? + firm.unautosaved_account = account + firm.save! + + assert account.new_record? + end + + def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.accounts << account + + firm.save! + assert !account.new_record? + + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.unautosaved_accounts << account + + firm.save! + assert account.new_record? + end +end + class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase self.use_transactional_fixtures = false @@ -480,9 +544,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert !@pirate.valid? @pirate.ship.mark_for_destruction + @pirate.ship.expects(:valid?).never assert_difference('Ship.count', -1) { @pirate.save! } end + def test_a_child_marked_for_destruction_should_not_be_destroyed_twice + @pirate.ship.mark_for_destruction + assert @pirate.save + @pirate.ship.expects(:destroy).never + assert @pirate.save + end + def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child # Stub the save method of the @pirate.ship instance to destroy and then raise an exception class << @pirate.ship @@ -517,9 +589,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert !@ship.valid? @ship.pirate.mark_for_destruction + @ship.pirate.expects(:valid?).never assert_difference('Pirate.count', -1) { @ship.save! } end + def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice + @ship.pirate.mark_for_destruction + assert @ship.save + @ship.pirate.expects(:destroy).never + assert @ship.save + end + def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate @@ -560,9 +640,33 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase children.each { |child| child.name = '' } assert !@pirate.valid? - children.each { |child| child.mark_for_destruction } + children.each do |child| + child.mark_for_destruction + child.expects(:valid?).never + end assert_difference("#{association_name.classify}.count", -2) { @pirate.save! } end + + define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do + @pirate.send(association_name).create!(:name => "#{association_name}_1") + children = @pirate.send(association_name) + + children.each { |child| child.name = '' } + assert !@pirate.valid? + + children.each { |child| child.destroy } + assert @pirate.valid? + end + + define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do + @pirate.send(association_name).create!(:name => "#{association_name}_1") + children = @pirate.send(association_name) + + children.each { |child| child.mark_for_destruction } + assert @pirate.save + children.each { |child| child.expects(:destroy).never } + assert @pirate.save + end define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") } @@ -952,3 +1056,118 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T include AutosaveAssociationOnACollectionAssociationTests end + +class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @pirate.birds.create(:name => 'cookoo') + end + + test "should automatically validate associations" do + assert @pirate.valid? + @pirate.birds.each { |bird| bird.name = '' } + + assert !@pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @pirate.create_ship(:name => 'titanic') + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.ship.name = '' + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_ship.name = '' + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.parrot = Parrot.new(:name => '') + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_parrot = Parrot.new(:name => '') + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.parrots = [ Parrot.new(:name => 'popuga') ] + @pirate.parrots.each { |parrot| parrot.name = '' } + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ] + @pirate.non_validated_parrots.each { |parrot| parrot.name = '' } + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.new + end + + test "should generate validation methods for has_many associations" do + assert @pirate.respond_to?(:validate_associated_records_for_birds) + end + + test "should generate validation methods for has_one associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_ship) + end + + test "should not generate validation methods for has_one associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship) + end + + test "should generate validation methods for belongs_to associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_parrot) + end + + test "should not generate validation methods for belongs_to associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot) + end + + test "should generate validation methods for HABTM associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_parrots) + end + + test "should not generate validation methods for HABTM associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots) + end +end diff --git a/activerecord/test/cases/callbacks_observers_test.rb b/activerecord/test/cases/callbacks_observers_test.rb index 87de524923..52ce384844 100644 --- a/activerecord/test/cases/callbacks_observers_test.rb +++ b/activerecord/test/cases/callbacks_observers_test.rb @@ -5,7 +5,7 @@ class Comment < ActiveRecord::Base before_validation :record_callers - def after_validation + after_validation do record_callers end @@ -32,7 +32,6 @@ class CallbacksObserversTest < ActiveRecord::TestCase CommentObserver.instance.callers = callers comment.valid? - assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified" end end diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 95fddaeef6..5a084a611e 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -13,8 +13,8 @@ class CallbackDeveloper < ActiveRecord::Base end def define_callback_method(callback_method) - define_method("#{callback_method}_method") do |model| - model.history << [callback_method, :method] + define_method(callback_method) do + self.history << [callback_method, :method] end end @@ -27,26 +27,20 @@ class CallbackDeveloper < ActiveRecord::Base end end - ActiveRecord::Callbacks::CALLBACKS.each do |callback_method| - callback_method_sym = callback_method.to_sym - define_callback_method(callback_method_sym) - send(callback_method, callback_method_sym) - send(callback_method, callback_string(callback_method_sym)) - send(callback_method, callback_proc(callback_method_sym)) - send(callback_method, callback_object(callback_method_sym)) - send(callback_method) { |model| model.history << [callback_method_sym, :block] } + ActiveSupport::Deprecation.silence do + ActiveRecord::Callbacks::CALLBACKS.each do |callback_method| + next if callback_method.to_s =~ /^around_/ + define_callback_method(callback_method) + send(callback_method, callback_string(callback_method)) + send(callback_method, callback_proc(callback_method)) + send(callback_method, callback_object(callback_method)) + send(callback_method) { |model| model.history << [callback_method, :block] } + end end def history @history ||= [] end - - # after_initialize and after_find are invoked only if instance methods have been defined. - def after_initialize - end - - def after_find - end end class ParentDeveloper < ActiveRecord::Base @@ -108,12 +102,12 @@ class ImmutableMethodDeveloper < ActiveRecord::Base @cancelled == true end - def before_save + before_save do @cancelled = true false end - def before_destroy + before_destroy do @cancelled = true false end @@ -125,15 +119,15 @@ class CallbackCancellationDeveloper < ActiveRecord::Base attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy - def before_save; !@cancel_before_save; end - def before_create; !@cancel_before_create; end - def before_update; !@cancel_before_update; end - def before_destroy; !@cancel_before_destroy; end + before_save { !@cancel_before_save } + before_create { !@cancel_before_create } + before_update { !@cancel_before_update } + before_destroy { !@cancel_before_destroy } - def after_save; @after_save_called = true; end - def after_update; @after_update_called = true; end - def after_create; @after_create_called = true; end - def after_destroy; @after_destroy_called = true; end + after_save { @after_save_called = true } + after_update { @after_update_called = true } + after_create { @after_create_called = true } + after_destroy { @after_destroy_called = true } end class CallbacksTest < ActiveRecord::TestCase @@ -142,6 +136,7 @@ class CallbacksTest < ActiveRecord::TestCase def test_initialize david = CallbackDeveloper.new assert_equal [ + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], @@ -152,10 +147,12 @@ class CallbacksTest < ActiveRecord::TestCase def test_find david = CallbackDeveloper.find(1) assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], @@ -167,26 +164,21 @@ class CallbacksTest < ActiveRecord::TestCase david = CallbackDeveloper.new david.valid? assert_equal [ + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_validation, :method ], [ :before_validation, :string ], [ :before_validation, :proc ], [ :before_validation, :object ], [ :before_validation, :block ], - [ :before_validation_on_create, :string ], - [ :before_validation_on_create, :proc ], - [ :before_validation_on_create, :object ], - [ :before_validation_on_create, :block ], + [ :after_validation, :method ], [ :after_validation, :string ], [ :after_validation, :proc ], [ :after_validation, :object ], [ :after_validation, :block ], - [ :after_validation_on_create, :string ], - [ :after_validation_on_create, :proc ], - [ :after_validation_on_create, :object ], - [ :after_validation_on_create, :block ] ], david.history end @@ -194,68 +186,63 @@ class CallbacksTest < ActiveRecord::TestCase david = CallbackDeveloper.find(1) david.valid? assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_validation, :method ], [ :before_validation, :string ], [ :before_validation, :proc ], [ :before_validation, :object ], [ :before_validation, :block ], - [ :before_validation_on_update, :string ], - [ :before_validation_on_update, :proc ], - [ :before_validation_on_update, :object ], - [ :before_validation_on_update, :block ], + [ :after_validation, :method ], [ :after_validation, :string ], [ :after_validation, :proc ], [ :after_validation, :object ], [ :after_validation, :block ], - [ :after_validation_on_update, :string ], - [ :after_validation_on_update, :proc ], - [ :after_validation_on_update, :object ], - [ :after_validation_on_update, :block ] ], david.history end def test_create david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000) assert_equal [ + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_validation, :method ], [ :before_validation, :string ], [ :before_validation, :proc ], [ :before_validation, :object ], [ :before_validation, :block ], - [ :before_validation_on_create, :string ], - [ :before_validation_on_create, :proc ], - [ :before_validation_on_create, :object ], - [ :before_validation_on_create, :block ], + [ :after_validation, :method ], [ :after_validation, :string ], [ :after_validation, :proc ], [ :after_validation, :object ], [ :after_validation, :block ], - [ :after_validation_on_create, :string ], - [ :after_validation_on_create, :proc ], - [ :after_validation_on_create, :object ], - [ :after_validation_on_create, :block ], + [ :before_save, :method ], [ :before_save, :string ], [ :before_save, :proc ], [ :before_save, :object ], [ :before_save, :block ], + [ :before_create, :method ], [ :before_create, :string ], [ :before_create, :proc ], [ :before_create, :object ], [ :before_create, :block ], + [ :after_create, :method ], [ :after_create, :string ], [ :after_create, :proc ], [ :after_create, :object ], [ :after_create, :block ], + [ :after_save, :method ], [ :after_save, :string ], [ :after_save, :proc ], [ :after_save, :object ], @@ -267,42 +254,42 @@ class CallbacksTest < ActiveRecord::TestCase david = CallbackDeveloper.find(1) david.save assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_validation, :method ], [ :before_validation, :string ], [ :before_validation, :proc ], [ :before_validation, :object ], [ :before_validation, :block ], - [ :before_validation_on_update, :string ], - [ :before_validation_on_update, :proc ], - [ :before_validation_on_update, :object ], - [ :before_validation_on_update, :block ], + [ :after_validation, :method ], [ :after_validation, :string ], [ :after_validation, :proc ], [ :after_validation, :object ], [ :after_validation, :block ], - [ :after_validation_on_update, :string ], - [ :after_validation_on_update, :proc ], - [ :after_validation_on_update, :object ], - [ :after_validation_on_update, :block ], + [ :before_save, :method ], [ :before_save, :string ], [ :before_save, :proc ], [ :before_save, :object ], [ :before_save, :block ], + [ :before_update, :method ], [ :before_update, :string ], [ :before_update, :proc ], [ :before_update, :object ], [ :before_update, :block ], + [ :after_update, :method ], [ :after_update, :string ], [ :after_update, :proc ], [ :after_update, :object ], [ :after_update, :block ], + [ :after_save, :method ], [ :after_save, :string ], [ :after_save, :proc ], [ :after_save, :object ], @@ -314,18 +301,22 @@ class CallbacksTest < ActiveRecord::TestCase david = CallbackDeveloper.find(1) david.destroy assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_destroy, :method ], [ :before_destroy, :string ], [ :before_destroy, :proc ], [ :before_destroy, :object ], [ :before_destroy, :block ], + [ :after_destroy, :method ], [ :after_destroy, :string ], [ :after_destroy, :proc ], [ :after_destroy, :object ], @@ -337,10 +328,12 @@ class CallbacksTest < ActiveRecord::TestCase david = CallbackDeveloper.find(1) CallbackDeveloper.delete(david.id) assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], @@ -407,14 +400,17 @@ class CallbacksTest < ActiveRecord::TestCase CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] } david.save assert_equal [ + [ :after_find, :method ], [ :after_find, :string ], [ :after_find, :proc ], [ :after_find, :object ], [ :after_find, :block ], + [ :after_initialize, :method ], [ :after_initialize, :string ], [ :after_initialize, :proc ], [ :after_initialize, :object ], [ :after_initialize, :block ], + [ :before_validation, :method ], [ :before_validation, :string ], [ :before_validation, :proc ], [ :before_validation, :object ], diff --git a/activerecord/test/cases/class_inheritable_attributes_test.rb b/activerecord/test/cases/class_inheritable_attributes_test.rb deleted file mode 100644 index abeb63c591..0000000000 --- a/activerecord/test/cases/class_inheritable_attributes_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test/unit' -require "cases/helper" -require 'active_support/core_ext/class/inheritable_attributes' - -class A - include ClassInheritableAttributes -end - -class B < A - write_inheritable_array "first", [ :one, :two ] -end - -class C < A - write_inheritable_array "first", [ :three ] -end - -class D < B - write_inheritable_array "first", [ :four ] -end - - -class ClassInheritableAttributesTest < ActiveRecord::TestCase - def test_first_level - assert_equal [ :one, :two ], B.read_inheritable_attribute("first") - assert_equal [ :three ], C.read_inheritable_attribute("first") - end - - def test_second_level - assert_equal [ :one, :two, :four ], D.read_inheritable_attribute("first") - assert_equal [ :one, :two ], B.read_inheritable_attribute("first") - end -end diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb index f79ee2f1f7..8e4842a1b6 100644 --- a/activerecord/test/cases/connection_test_mysql.rb +++ b/activerecord/test/cases/connection_test_mysql.rb @@ -41,6 +41,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase sleep 2 @connection.verify! assert @connection.active? + end + + # Test that MySQL allows multiple results for stored procedures + if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) + def test_multi_results + rows = ActiveRecord::Base.connection.select_rows('CALL ten();') + assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" + end end private diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 74571d923a..f456d273fe 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -308,6 +308,84 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_previous_changes + # original values should be in previous_changes + pirate = Pirate.new + + assert_equal Hash.new, pirate.previous_changes + pirate.catchphrase = "arrr" + pirate.save! + + assert_equal 4, pirate.previous_changes.size + assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase'] + assert_equal [nil, pirate.id], pirate.previous_changes['id'] + assert_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert_nil pirate.previous_changes['created_on'][0] + assert_not_nil pirate.previous_changes['created_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + + # original values should be in previous_changes + pirate = Pirate.new + + assert_equal Hash.new, pirate.previous_changes + pirate.catchphrase = "arrr" + pirate.save + + assert_equal 4, pirate.previous_changes.size + assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase'] + assert_equal [nil, pirate.id], pirate.previous_changes['id'] + assert pirate.previous_changes.include?('updated_on') + assert pirate.previous_changes.include?('created_on') + assert !pirate.previous_changes.key?('parrot_id') + + pirate.catchphrase = "Yar!!" + pirate.reload + assert_equal Hash.new, pirate.previous_changes + + pirate = Pirate.find_by_catchphrase("arrr") + pirate.catchphrase = "Me Maties!" + pirate.save! + + assert_equal 2, pirate.previous_changes.size + assert_equal ["arrr", "Me Maties!"], pirate.previous_changes['catchphrase'] + assert_not_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + assert !pirate.previous_changes.key?('created_on') + + pirate = Pirate.find_by_catchphrase("Me Maties!") + pirate.catchphrase = "Thar She Blows!" + pirate.save + + assert_equal 2, pirate.previous_changes.size + assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes['catchphrase'] + assert_not_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + assert !pirate.previous_changes.key?('created_on') + + pirate = Pirate.find_by_catchphrase("Thar She Blows!") + pirate.update_attributes(:catchphrase => "Ahoy!") + + assert_equal 2, pirate.previous_changes.size + assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes['catchphrase'] + assert_not_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + assert !pirate.previous_changes.key?('created_on') + + pirate = Pirate.find_by_catchphrase("Ahoy!") + pirate.update_attribute(:catchphrase, "Ninjas suck!") + + assert_equal 2, pirate.previous_changes.size + assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase'] + assert_not_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + assert !pirate.previous_changes.key?('created_on') + end + private def with_partial_updates(klass, on = true) old = klass.partial_updates? diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index d1e7caed89..aa09c7061f 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -12,8 +12,6 @@ require 'active_record/test_case' require 'active_record/fixtures' require 'connection' -require 'cases/repair_helper' - begin require 'ruby-debug' rescue LoadError diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb index 54fb3d8c39..aa7ce2ecb6 100644 --- a/activerecord/test/cases/lifecycle_test.rb +++ b/activerecord/test/cases/lifecycle_test.rb @@ -1,11 +1,9 @@ -require "cases/helper" +require 'cases/helper' require 'models/topic' require 'models/developer' require 'models/reply' require 'models/minimalistic' -class Topic; def after_find() end end -class Developer; def after_find() end end class SpecialDeveloper < Developer; end class TopicManualObserver @@ -43,6 +41,11 @@ class TopicObserver < ActiveRecord::Observer def after_find(topic) @topic = topic end + + # Create an after_save callback, so a notify_observer hook is created + # on :topic. + def after_save(nothing) + end end class MinimalisticObserver < ActiveRecord::Observer @@ -159,34 +162,6 @@ class LifecycleTest < ActiveRecord::TestCase assert_equal topic, observer.topic end - def test_after_find_is_not_created_if_its_not_used - # use a fresh class so an observer can't have defined an - # after_find on it - model_class = Class.new(ActiveRecord::Base) - observer_class = Class.new(ActiveRecord::Observer) - observer_class.observe(model_class) - - observer = observer_class.instance - - assert !model_class.method_defined?(:after_find) - end - - def test_after_find_is_not_clobbered_if_it_already_exists - # use a fresh observer class so we can instantiate it (Observer is - # a Singleton) - model_class = Class.new(ActiveRecord::Base) do - def after_find; end - end - original_method = model_class.instance_method(:after_find) - observer_class = Class.new(ActiveRecord::Observer) do - def after_find; end - end - observer_class.observe(model_class) - - observer = observer_class.instance - assert_equal original_method, model_class.instance_method(:after_find) - end - def test_invalid_observer assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers } end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index d033c1e760..721792132c 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -68,24 +68,38 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase ship = pirate.create_ship(:name => 'Nights Dirty Lightning') assert_no_difference('Ship.count') do - pirate.update_attributes(:ship_attributes => { '_delete' => true }) + pirate.update_attributes(:ship_attributes => { '_destroy' => true }) end end - def test_a_model_should_respond_to_underscore_delete_and_return_if_it_is_marked_for_destruction + def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction ship = Ship.create!(:name => 'Nights Dirty Lightning') - assert !ship._delete + assert !ship._destroy ship.mark_for_destruction - assert ship._delete + assert ship._destroy + end + + def test_underscore_delete_is_deprecated + ActiveSupport::Deprecation.expects(:warn) + ship = Ship.create!(:name => 'Nights Dirty Lightning') + ship._delete end end class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase + include AssertRaiseWithMessage + def setup @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?") @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning') end + def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to + assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do + Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"}) + end + end + def test_should_define_an_attribute_writer_method_for_the_association assert_respond_to @pirate, :ship_attributes= end @@ -98,9 +112,9 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name end - def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy + def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy @ship.destroy - @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' } + @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' } assert_nil @pirate.ship end @@ -120,8 +134,8 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Nights Dirty Lightning', @ship.name end - def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy - @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' } + def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy + @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' } assert_equal @ship, @pirate.ship assert_equal 'Nights Dirty Lightning', @pirate.ship.name @@ -148,29 +162,29 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name end - def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy + def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy @pirate.ship.destroy [1, '1', true, 'true'].each do |truth| @pirate.reload.create_ship(:name => 'Mister Pablo') assert_difference('Ship.count', -1) do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => truth }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth }) end end end - def test_should_not_delete_an_existing_record_if_delete_is_not_truthy + def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy [nil, '0', 0, 'false', false].each do |not_truth| assert_no_difference('Ship.count') do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => not_truth }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth }) end end end - def test_should_not_delete_an_existing_record_if_allow_destroy_is_false + def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } assert_no_difference('Ship.count') do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => '1' }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' }) end Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } @@ -193,7 +207,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('Ship.count') do - @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_delete => '1' } } + @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } } end assert_difference('Ship.count', -1) do @pirate.save @@ -224,9 +238,9 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Arr', @ship.pirate.catchphrase end - def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy + def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy @pirate.destroy - @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' } + @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' } assert_nil @ship.pirate end @@ -246,8 +260,8 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Aye', @pirate.catchphrase end - def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy - @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' } + def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy + @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' } assert_equal @pirate, @ship.pirate assert_equal 'Aye', @ship.pirate.catchphrase @@ -274,29 +288,29 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Arr', @ship.pirate.catchphrase end - def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy + def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy @ship.pirate.destroy [1, '1', true, 'true'].each do |truth| @ship.reload.create_pirate(:catchphrase => 'Arr') assert_difference('Pirate.count', -1) do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => truth }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth }) end end end - def test_should_not_delete_an_existing_record_if_delete_is_not_truthy + def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy [nil, '0', 0, 'false', false].each do |not_truth| assert_no_difference('Pirate.count') do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => not_truth }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth }) end end end - def test_should_not_delete_an_existing_record_if_allow_destroy_is_false + def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } assert_no_difference('Pirate.count') do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => '1' }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' }) end Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } @@ -312,7 +326,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('Pirate.count') do - @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_delete' => true } } + @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } } end assert_difference('Pirate.count', -1) { @ship.save } end @@ -380,18 +394,18 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name end - def test_should_not_assign_delete_key_to_a_record + def test_should_not_assign_destroy_key_to_a_record assert_nothing_raised ActiveRecord::UnknownAttributeError do - @pirate.send(association_setter, { 'foo' => { '_delete' => '0' }}) + @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }}) end end - def test_should_ignore_new_associated_records_with_truthy_delete_attribute + def test_should_ignore_new_associated_records_with_truthy_destroy_attribute @pirate.send(@association_name).destroy_all @pirate.reload.attributes = { association_getter => { 'foo' => { :name => 'Grace OMalley' }, - 'bar' => { :name => 'Privateers Greed', '_delete' => '1' } + 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' } } } @@ -443,7 +457,7 @@ module NestedAttributesOnACollectionAssociationTests ['1', 1, 'true', true].each do |true_variable| record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley') @pirate.send(association_setter, - @alternate_params[association_getter].merge('baz' => { :id => record.id, '_delete' => true_variable }) + @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable }) ) assert_difference('@pirate.send(@association_name).count', -1) do @@ -454,7 +468,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument [nil, '', '0', 0, 'false', false].each do |false_variable| - @alternate_params[association_getter]['foo']['_delete'] = false_variable + @alternate_params[association_getter]['foo']['_destroy'] = false_variable assert_no_difference('@pirate.send(@association_name).count') do @pirate.update_attributes(@alternate_params) end @@ -463,7 +477,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('@pirate.send(@association_name).count') do - @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_delete' => true })) + @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true })) end assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save } end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index a164f5e060..f3ed8ccd8d 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -176,9 +176,9 @@ class ReflectionTest < ActiveRecord::TestCase def test_reflection_of_all_associations # FIXME these assertions bust a lot - assert_equal 31, Firm.reflect_on_all_associations.size - assert_equal 24, Firm.reflect_on_all_associations(:has_many).size - assert_equal 7, Firm.reflect_on_all_associations(:has_one).size + assert_equal 36, Firm.reflect_on_all_associations.size + assert_equal 26, Firm.reflect_on_all_associations(:has_many).size + assert_equal 10, Firm.reflect_on_all_associations(:has_one).size assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb deleted file mode 100644 index 80d04010d6..0000000000 --- a/activerecord/test/cases/repair_helper.rb +++ /dev/null @@ -1,46 +0,0 @@ -module ActiveRecord - module Testing - module RepairHelper - extend ActiveSupport::Concern - - module Toolbox - def self.record_validations(*model_classes) - model_classes.inject({}) do |repair, klass| - repair[klass] ||= {} - [:validate, :validate_on_create, :validate_on_update].each do |callback| - the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks") - repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup) - end - repair - end - end - - def self.reset_validations(recorded) - recorded.each do |klass, repairs| - [:validate, :validate_on_create, :validate_on_update].each do |callback| - klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback]) - end - end - end - end - - module ClassMethods - def repair_validations(*model_classes) - setup do - @validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes) - end - teardown do - ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(@validation_repairs) - end - end - end - - def repair_validations(*model_classes, &block) - validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes) - return block.call - ensure - ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(validation_repairs) - end - end - end -end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index f6533b5396..aca70b4238 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -382,28 +382,28 @@ class TransactionTest < ActiveRecord::TestCase private def add_exception_raising_after_save_callback_to_topic - Topic.class_eval { def after_save() raise "Make the transaction rollback" end } + Topic.class_eval "def after_save_for_transaction; raise 'Make the transaction rollback' end" end def remove_exception_raising_after_save_callback_to_topic - Topic.class_eval { remove_method :after_save } + Topic.class_eval "def after_save_for_transaction; end" end def add_exception_raising_after_create_callback_to_topic - Topic.class_eval { def after_create() raise "Make the transaction rollback" end } + Topic.class_eval "def after_create_for_transaction; raise 'Make the transaction rollback' end" end def remove_exception_raising_after_create_callback_to_topic - Topic.class_eval { remove_method :after_create } + Topic.class_eval "def after_create_for_transaction; end" end %w(validation save destroy).each do |filter| define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do - Topic.class_eval "def before_#{filter}() Book.create; false end" + Topic.class_eval "def before_#{filter}_for_transaction() Book.create; false end" end define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do - Topic.class_eval "remove_method :before_#{filter}" + Topic.class_eval "def before_#{filter}_for_transaction; end" end end end diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 29c10de4fe..3794a0ebb9 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -2,9 +2,9 @@ require "cases/helper" require 'models/topic' require 'models/reply' -class I18nGenerateMessageValidationTest < Test::Unit::TestCase +class I18nGenerateMessageValidationTest < ActiveRecord::TestCase def setup - reset_callbacks Topic + Topic.reset_callbacks(:validate) @topic = Topic.new I18n.backend.store_translations :'en', { :activerecord => { @@ -17,14 +17,6 @@ class I18nGenerateMessageValidationTest < Test::Unit::TestCase } end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) def test_generate_message_inclusion_with_default_message assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index 73d9c7249c..252138c0d6 100644 --- a/activerecord/test/cases/validations/i18n_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -4,7 +4,7 @@ require 'models/reply' class I18nValidationTest < ActiveRecord::TestCase def setup - reset_callbacks Topic + Topic.reset_callbacks(:validate) @topic = Topic.new @old_load_path, @old_backend = I18n.load_path, I18n.backend I18n.load_path.clear @@ -13,7 +13,7 @@ class I18nValidationTest < ActiveRecord::TestCase end def teardown - reset_callbacks Topic + Topic.reset_callbacks(:validate) I18n.load_path.replace @old_load_path I18n.backend = @old_backend end @@ -30,14 +30,6 @@ class I18nValidationTest < ActiveRecord::TestCase end end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated assert_not_deprecated do default = "%s interpolation syntax was deprecated" @@ -710,9 +702,9 @@ class I18nValidationTest < ActiveRecord::TestCase end end -class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase +class ActiveRecordValidationsGenerateMessageI18nTests < ActiveRecord::TestCase + def setup - reset_callbacks Topic @topic = Topic.new I18n.backend.store_translations :'en', { :activerecord => { @@ -743,14 +735,6 @@ class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase } end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) def test_generate_message_inclusion_with_default_message assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index a4e874e5e6..5cdb623eef 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -149,24 +149,24 @@ class ValidationsTest < ActiveRecord::TestCase end def test_validates_length_with_globally_modified_error_message - ActiveSupport::Deprecation.silence do - ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}' - end + defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages } + original_message = defaults[:too_short] + defaults[:too_short] = 'tu est trops petit hombre {{count}}' Topic.validates_length_of :title, :minimum => 10 t = Topic.create(:title => 'too short') assert !t.valid? assert_equal ['tu est trops petit hombre 10'], t.errors[:title] + + ensure + defaults[:too_short] = original_message end def test_validates_acceptance_of_as_database_column - repair_validations(Reply) do - Reply.validates_acceptance_of(:author_name) - - reply = Reply.create("author_name" => "Dan Brown") - assert_equal "Dan Brown", reply["author_name"] - end + Topic.validates_acceptance_of(:author_name) + topic = Topic.create("author_name" => "Dan Brown") + assert_equal "Dan Brown", topic["author_name"] end def test_deprecated_validation_instance_methods diff --git a/activerecord/test/fixtures/accounts.yml b/activerecord/test/fixtures/accounts.yml index b2d0191900..32583042a8 100644 --- a/activerecord/test/fixtures/accounts.yml +++ b/activerecord/test/fixtures/accounts.yml @@ -2,6 +2,7 @@ signals37: id: 1 firm_id: 1 credit_limit: 50 + firm_name: 37signals unknown: id: 2 diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index f264f980d6..7cbc6e803f 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -94,8 +94,9 @@ class Author < ActiveRecord::Base belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress" attr_accessor :post_log + after_initialize :set_post_log - def after_initialize + def set_post_log @post_log = [] end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index ab09f88a9f..469f5399ae 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -78,7 +78,14 @@ class Firm < Company # added order by id as in fixtures there are two accounts for Rails Core # Oracle tests were failing because of that as the second fixture was selected has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account", :order => "id" + has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account" has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete + + has_one :account_limit_500_with_hash_conditions, :foreign_key => "firm_id", :class_name => "Account", :conditions => { :credit_limit => 500 } + + has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false + has_many :accounts + has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false end class DependentFirm < Company @@ -111,6 +118,8 @@ class Client < Company true end + before_destroy :overwrite_to_raise + # Used to test that read and question methods are not generated for these attributes def ruby_type read_attribute :ruby_type @@ -120,6 +129,9 @@ class Client < Company query_attribute :rating end + def overwrite_to_raise + end + class << self private @@ -144,6 +156,7 @@ end class Account < ActiveRecord::Base belongs_to :firm + belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false def self.destroyed_account_ids @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] } diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index acf53fce8b..3d7c4bc48a 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -1,6 +1,8 @@ class Pirate < ActiveRecord::Base - belongs_to :parrot - has_and_belongs_to_many :parrots + belongs_to :parrot, :validate => true + belongs_to :non_validated_parrot, :class_name => 'Parrot' + has_and_belongs_to_many :parrots, :validate => true + has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot' has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot", :before_add => :log_before_add, :after_add => :log_after_add, @@ -16,7 +18,8 @@ class Pirate < ActiveRecord::Base has_many :treasure_estimates, :through => :treasures, :source => :price_estimates # These both have :autosave enabled because accepts_nested_attributes_for is used on them. - has_one :ship + has_one :ship, :validate => true + has_one :non_validated_ship, :class_name => 'Ship' has_many :birds has_many :birds_with_method_callbacks, :class_name => "Bird", :before_add => :log_before_add, diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 422b12dc83..416032cb75 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -22,8 +22,9 @@ class Project < ActiveRecord::Base has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" attr_accessor :developers_log + after_initialize :set_developers_log - def after_initialize + def set_developers_log @developers_log = [] end diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb index f5906dedd1..ba5a1d1d01 100644 --- a/activerecord/test/models/reply.rb +++ b/activerecord/test/models/reply.rb @@ -8,13 +8,13 @@ class Reply < Topic has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id" validate :errors_on_empty_content - validate_on_create :title_is_wrong_create + validate :title_is_wrong_create, :on => :create attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title validate :check_empty_title - validate_on_create :check_content_mismatch - validate_on_update :check_wrong_update + validate :check_content_mismatch, :on => :create + validate :check_wrong_update, :on => :update def check_empty_title errors[:title] << "Empty" unless attribute_present?("title") @@ -47,4 +47,4 @@ module Web class Reply < Web::Topic belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true, :class_name => 'Web::Topic' end -end
\ No newline at end of file +end diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 06759d64b8..d0df951622 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -1,7 +1,7 @@ class Ship < ActiveRecord::Base self.record_timestamps = false - belongs_to :pirate + belongs_to :pirate, :validate => true has_many :parts, :class_name => 'ShipPart', :autosave => true accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 9594dc300a..baca4972cb 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -52,6 +52,15 @@ class Topic < ActiveRecord::Base id end + before_validation :before_validation_for_transaction + before_save :before_save_for_transaction + before_destroy :before_destroy_for_transaction + + after_save :after_save_for_transaction + after_create :after_create_for_transaction + + after_initialize :set_email_address + protected def approved=(val) @custom_approved = val @@ -66,15 +75,21 @@ class Topic < ActiveRecord::Base self.class.delete_all "parent_id = #{id}" end - def after_initialize + def set_email_address if self.new_record? self.author_email_address = 'test@test.com' end end + + def before_validation_for_transaction; end + def before_save_for_transaction; end + def before_destroy_for_transaction; end + def after_save_for_transaction; end + def after_create_for_transaction; end end module Web class Topic < ActiveRecord::Base has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply' end -end
\ No newline at end of file +end diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb index 97c690c110..2a98e74f2c 100644 --- a/activerecord/test/models/treasure.rb +++ b/activerecord/test/models/treasure.rb @@ -3,4 +3,6 @@ class Treasure < ActiveRecord::Base belongs_to :looter, :polymorphic => true has_many :price_estimates, :as => :estimate_of + + accepts_nested_attributes_for :looter end diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb index f44c33ae67..c78d99f4af 100644 --- a/activerecord/test/schema/mysql_specific_schema.rb +++ b/activerecord/test/schema/mysql_specific_schema.rb @@ -9,4 +9,16 @@ ActiveRecord::Schema.define do t.text :medium_text, :limit => 16777215 t.text :long_text, :limit => 2147483647 end + + ActiveRecord::Base.connection.execute <<-SQL +DROP PROCEDURE IF EXISTS ten; +SQL + + ActiveRecord::Base.connection.execute <<-SQL +CREATE PROCEDURE ten() SQL SECURITY INVOKER +BEGIN + select 10; +END +SQL + end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 9ab4cf6f43..15e5e12d03 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -22,6 +22,7 @@ ActiveRecord::Schema.define do # unless the ordering matters. In which case, define them below create_table :accounts, :force => true do |t| t.integer :firm_id + t.string :firm_name t.integer :credit_limit end diff --git a/activeresource/Rakefile b/activeresource/Rakefile index def489fad9..c87345e3b5 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -34,11 +34,12 @@ Rake::TestTask.new { |t| t.verbose = true t.warning = true } + task :isolated_test do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib" Dir.glob("test/**/*_test.rb").all? do |file| - system(ruby, "-Ilib:test:#{activesupport_path}", file) + system(ruby, '-w', "-Ilib:test:#{activesupport_path}", file) end or raise "Failures" end @@ -57,37 +58,8 @@ Rake::RDocTask.new { |rdoc| } -# Create compressed packages - -dist_dirs = [ "lib", "test", "examples", "dev-utils" ] - -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Think Active Record for web resources." - s.description = %q{Wraps web resources in model classes that can be manipulated through XML over REST.} +spec = eval(File.read('activeresource.gemspec')) - s.files = [ "Rakefile", "README", "CHANGELOG" ] - dist_dirs.each do |dir| - s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - end - - s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD) - - s.require_path = 'lib' - s.autorequire = 'active_resource' - - s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.homepage = "http://www.rubyonrails.org" - s.rubyforge_project = "activeresource" -end - Rake::GemPackageTask.new(spec) do |p| p.gem_spec = spec p.need_tar = true diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec new file mode 100644 index 0000000000..ad2031c9e7 --- /dev/null +++ b/activeresource/activeresource.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'activeresource' + s.version = '3.0.pre' + s.summary = "Think Active Record for web resources." + s.description = %q{Wraps web resources in model classes that can be manipulated through XML over REST.} + + s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*'] + + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('activemodel', '= 3.0.pre') + + s.require_path = 'lib' + s.autorequire = 'active_resource' + + s.has_rdoc = true + s.extra_rdoc_files = %w( README ) + s.rdoc_options.concat ['--main', 'README'] + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "activeresource" +end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index e5b8589fb3..b21d8db613 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1,6 +1,7 @@ require 'active_support' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/module/attr_accessor_with_default' require 'active_support/core_ext/module/delegation' @@ -155,7 +156,7 @@ module ActiveResource # # <tt>404</tt> is just one of the HTTP error response codes that Active Resource will handle with its own exception. The # following HTTP response codes will also result in these exceptions: - # + # # * 200..399 - Valid response, no exception (other than 301, 302) # * 301, 302 - ActiveResource::Redirection # * 400 - ActiveResource::BadRequest @@ -414,7 +415,7 @@ module ActiveResource attr_accessor_with_default(:collection_name) { ActiveSupport::Inflector.pluralize(element_name) } #:nodoc: attr_accessor_with_default(:primary_key, 'id') #:nodoc: - + # Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>) # This method is regenerated at runtime based on what the \prefix is set to. def prefix(options={}) @@ -770,7 +771,7 @@ module ActiveResource # my_other_course = Course.new(:name => "Philosophy: Reason and Being", :lecturer => "Ralph Cling") # my_other_course.save def initialize(attributes = {}) - @attributes = {} + @attributes = {}.with_indifferent_access @prefix_options = {} load(attributes) end @@ -915,7 +916,7 @@ module ActiveResource def save new? ? create : update end - + # Saves the resource. # # If the resource is new, it is created via +POST+, otherwise the @@ -924,7 +925,7 @@ module ActiveResource # With <tt>save!</tt> validations always run. If any of them fail # ActiveResource::ResourceInvalid gets raised, and nothing is POSTed to # the remote system. - # See ActiveResource::Validations for more information. + # See ActiveResource::Validations for more information. # # There's a series of callbacks associated with <tt>save!</tt>. If any # of the <tt>before_*</tt> callbacks return +false+ the action is @@ -1099,6 +1100,36 @@ module ActiveResource self end + # Updates a single attribute and then saves the object. + # + # Note: Unlike ActiveRecord::Base.update_attribute, this method <b>is</b> + # subject to normal validation routines as an update sends the whole body + # of the resource in the request. (See Validations). + # + # As such, this method is equivalent to calling update_attributes with a single attribute/value pair. + # + # If the saving fails because of a connection or remote service error, an + # exception will be raised. If saving fails because the resource is + # invalid then <tt>false</tt> will be returned. + def update_attribute(name, value) + self.send("#{name}=".to_sym, value) + self.save + end + + # Updates this resource with all the attributes from the passed-in Hash + # and requests that the record be saved. + # + # If the saving fails because of a connection or remote service error, an + # exception will be raised. If saving fails because the resource is + # invalid then <tt>false</tt> will be returned. + # + # Note: Though this request can be made with a partial set of the + # resource's attributes, the full body of the request will still be sent + # in the save request to the remote service. + def update_attributes(attributes) + load(attributes) && save + end + # For checking <tt>respond_to?</tt> without searching the attributes (which is faster). alias_method :respond_to_without_attributes?, :respond_to? @@ -1119,7 +1150,6 @@ module ActiveResource super end - protected def connection(refresh = false) self.class.connection(refresh) diff --git a/activeresource/lib/active_resource/observing.rb b/activeresource/lib/active_resource/observing.rb index 94836f4bb1..3c74d49c80 100644 --- a/activeresource/lib/active_resource/observing.rb +++ b/activeresource/lib/active_resource/observing.rb @@ -4,7 +4,18 @@ module ActiveResource include ActiveModel::Observing included do - wrap_with_notifications :create, :save, :update, :destroy + %w( create save update destroy ).each do |method| + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{method}_with_notifications(*args, &block) + notify_observers(:before_#{method}) + if result = #{method}_without_notifications(*args, &block) + notify_observers(:after_#{method}) + end + result + end + EOS + alias_method_chain(method, :notifications) + end end end end diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb index d4d282e273..67b69fa505 100644 --- a/activeresource/lib/active_resource/validations.rb +++ b/activeresource/lib/active_resource/validations.rb @@ -93,9 +93,9 @@ module ActiveResource # content-type of the error-block received def load_remote_errors(remote_errors, save_cache = false ) #:nodoc: case remote_errors.response['Content-Type'] - when 'application/xml' + when /xml/ errors.from_xml(remote_errors.response.body, save_cache) - when 'application/json' + when /json/ errors.from_json(remote_errors.response.body, save_cache) end end diff --git a/activeresource/lib/activeresource.rb b/activeresource/lib/activeresource.rb deleted file mode 100644 index e076455b16..0000000000 --- a/activeresource/lib/activeresource.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_resource' diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb index 1952f5b5f0..189a4d81fe 100644 --- a/activeresource/test/cases/base/load_test.rb +++ b/activeresource/test/cases/base/load_test.rb @@ -15,26 +15,21 @@ module Highrise module Deeply module Nested - class Note < ActiveResource::Base self.site = "http://37s.sunrise.i:3000" end - class Comment < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000" - end - - module TestDifferentLevels - - class Note < ActiveResource::Base - self.site = "http://37s.sunrise.i:3000" - end - - end + class Comment < ActiveResource::Base + self.site = "http://37s.sunrise.i:3000" + end + module TestDifferentLevels + class Note < ActiveResource::Base + self.site = "http://37s.sunrise.i:3000" + end + end end end - end @@ -68,6 +63,19 @@ class BaseLoadTest < Test::Unit::TestCase assert_equal @matz.stringify_keys, @person.load(@matz).attributes end + def test_after_load_attributes_are_accessible + assert_equal Hash.new, @person.attributes + assert_equal @matz.stringify_keys, @person.load(@matz).attributes + assert_equal @matz[:name], @person.attributes['name'] + end + + def test_after_load_attributes_are_accessible_via_indifferent_access + assert_equal Hash.new, @person.attributes + assert_equal @matz.stringify_keys, @person.load(@matz).attributes + assert_equal @matz[:name], @person.attributes['name'] + assert_equal @matz[:name], @person.attributes[:name] + end + def test_load_one_with_existing_resource address = @person.load(:street_address => @first_address).street_address assert_kind_of StreetAddress, address @@ -143,7 +151,7 @@ class BaseLoadTest < Test::Unit::TestCase assert_kind_of String, places.first assert_equal @deep[:street][:state][:places].first, places.first end - + def test_nested_collections_within_the_same_namespace n = Highrise::Note.new(:comments => [{ :name => "1" }]) assert_kind_of Highrise::Comment, n.comments.first @@ -158,6 +166,4 @@ class BaseLoadTest < Test::Unit::TestCase n = Highrise::Deeply::Nested::TestDifferentLevels::Note.new(:comments => [{ :name => "1" }]) assert_kind_of Highrise::Deeply::Nested::Comment, n.comments.first end - - end diff --git a/activeresource/test/cases/base_errors_test.rb b/activeresource/test/cases/base_errors_test.rb index eca00e9ca8..1eb7765132 100644 --- a/activeresource/test/cases/base_errors_test.rb +++ b/activeresource/test/cases/base_errors_test.rb @@ -4,8 +4,8 @@ require "fixtures/person" class BaseErrorsTest < Test::Unit::TestCase def setup ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error><error>Name can't be blank</error><error>Name must start with a letter</error><error>Person quota full for today.</error></errors>), 422, {'Content-Type' => 'application/xml'} - mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json'} + mock.post "/people.xml", {}, %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Age can't be blank</error><error>Name can't be blank</error><error>Name must start with a letter</error><error>Person quota full for today.</error></errors>), 422, {'Content-Type' => 'application/xml; charset=utf-8'} + mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json; charset=utf-8'} end end diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb index 8c0217aad6..1593e25595 100644 --- a/activeresource/test/cases/base_test.rb +++ b/activeresource/test/cases/base_test.rb @@ -11,12 +11,12 @@ class BaseTest < Test::Unit::TestCase @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') @greg = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person') - @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address') + @addy = { :id => 1, :street => '12345 Street', :country => 'Australia' }.to_xml(:root => 'address') @default_request_headers = { 'Content-Type' => 'application/xml' } @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person") @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people') @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people') - @addresses = [{ :id => 1, :street => '12345 Street' }].to_xml(:root => 'addresses') + @addresses = [{ :id => 1, :street => '12345 Street', :country => 'Australia' }].to_xml(:root => 'addresses') # - deep nested resource - # - Luis (Customer) @@ -102,6 +102,9 @@ class BaseTest < Test::Unit::TestCase Person.password = nil end + ######################################################################## + # Tests relating to setting up the API-connection configuration + ######################################################################## def test_site_accessor_accepts_uri_or_string_argument site = URI.parse('http://localhost') @@ -509,6 +512,11 @@ class BaseTest < Test::Unit::TestCase assert_not_equal(first_connection, second_connection, 'Connection should be re-created') end + + ######################################################################## + # Tests for setting up remote URLs for a given model (including adding + # parameters appropriately) + ######################################################################## def test_collection_name assert_equal "people", Person.collection_name end @@ -637,6 +645,10 @@ class BaseTest < Test::Unit::TestCase assert_equal [:person_id].to_set, StreetAddress.__send__(:prefix_parameters) end + + ######################################################################## + # Tests basic CRUD functions (find/save/create etc) + ######################################################################## def test_respond_to matz = Person.find(1) assert matz.respond_to?(:name) @@ -813,6 +825,55 @@ class BaseTest < Test::Unit::TestCase assert_raise(ActiveResource::ResourceConflict) { Person.find(2).save } end + + ###### + # update_attribute(s)(!) + + def test_update_attribute_as_symbol + matz = Person.first + matz.expects(:save).returns(true) + + assert_equal "Matz", matz.name + assert matz.update_attribute(:name, "David") + assert_equal "David", matz.name + end + + def test_update_attribute_as_string + matz = Person.first + matz.expects(:save).returns(true) + + assert_equal "Matz", matz.name + assert matz.update_attribute('name', "David") + assert_equal "David", matz.name + end + + + def test_update_attributes_as_symbols + addy = StreetAddress.first(:params => {:person_id => 1}) + addy.expects(:save).returns(true) + + assert_equal "12345 Street", addy.street + assert_equal "Australia", addy.country + assert addy.update_attributes(:street => '54321 Street', :country => 'USA') + assert_equal "54321 Street", addy.street + assert_equal "USA", addy.country + end + + def test_update_attributes_as_strings + addy = StreetAddress.first(:params => {:person_id => 1}) + addy.expects(:save).returns(true) + + assert_equal "12345 Street", addy.street + assert_equal "Australia", addy.country + assert addy.update_attributes('street' => '54321 Street', 'country' => 'USA') + assert_equal "54321 Street", addy.street + assert_equal "USA", addy.country + end + + + ##### + # Mayhem and destruction + def test_destroy assert Person.find(1).destroy ActiveResource::HttpMock.respond_to do |mock| @@ -852,7 +913,7 @@ class BaseTest < Test::Unit::TestCase end assert_raise(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) } end - + def test_delete_with_410_gone assert Person.delete(1) ActiveResource::HttpMock.respond_to do |mock| @@ -861,6 +922,9 @@ class BaseTest < Test::Unit::TestCase assert_raise(ActiveResource::ResourceGone) { Person.find(1) } end + ######################################################################## + # Tests the more miscelaneous helper methods + ######################################################################## def test_exists # Class method. assert !Person.exists?(nil) diff --git a/activeresource/test/cases/observing_test.rb b/activeresource/test/cases/observing_test.rb index 334b256772..9599ff7b0f 100644 --- a/activeresource/test/cases/observing_test.rb +++ b/activeresource/test/cases/observing_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'fixtures/person' +require 'active_support/core_ext/hash/conversions' class ObservingTest < Test::Unit::TestCase cattr_accessor :history diff --git a/activeresource/test/cases/validations_test.rb b/activeresource/test/cases/validations_test.rb index a8ab7d64e7..c05f625fb7 100644 --- a/activeresource/test/cases/validations_test.rb +++ b/activeresource/test/cases/validations_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' -require "fixtures/project" +require 'fixtures/project' +require 'active_support/core_ext/hash/conversions' # The validations are tested thoroughly under ActiveModel::Validations # This test case simply makes sur that they are all accessible by diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 238594debb..c27167287d 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -15,17 +15,17 @@ RUBY_FORGE_PROJECT = "activesupport" RUBY_FORGE_USER = "webster132" task :default => :test -Rake::TestTask.new { |t| - t.libs << "test" +Rake::TestTask.new do |t| + t.libs << 'test' t.pattern = 'test/**/*_test.rb' t.verbose = true t.warning = true -} -task :isolated_test do - ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - Dir['test/**/*_test.rb'].all? do |file| - system(ruby, '-Ilib:test', file) - end or raise "Failures" +end + +namespace :test do + Rake::TestTask.new(:isolated) do |t| + t.pattern = 'test/ts_isolated.rb' + end end # Create compressed packages @@ -45,22 +45,7 @@ Rake::RDocTask.new { |rdoc| rdoc.rdoc_files.exclude('lib/active_support/vendor/*') } -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Support and utility classes used by the Rails framework." - s.description = %q{Utility library which carries commonly used classes and goodies from the Rails framework} - - s.files = [ "CHANGELOG", "README" ] + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) } - s.require_path = 'lib' - s.has_rdoc = true - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.homepage = "http://www.rubyonrails.org" - s.rubyforge_project = "activesupport" -end +spec = eval(File.read('activesupport.gemspec')) Rake::GemPackageTask.new(spec) do |p| p.gem_spec = spec diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec new file mode 100644 index 0000000000..cabda2b073 --- /dev/null +++ b/activesupport/activesupport.gemspec @@ -0,0 +1,17 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = "activesupport" + s.version = "3.0.pre" + s.date = "2009-09-01" + s.summary = "Support and utility classes used by the Rails framework." + s.description = %q{Utility library which carries commonly used classes and goodies from the Rails framework} + + s.files = Dir['CHANGELOG', 'README', 'lib/**/*'] + s.require_path = 'lib' + s.has_rdoc = true + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "activesupport" +end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index a20635ba62..0478ae4ebc 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -37,4 +37,5 @@ end require 'active_support/autoload' require 'active_support/vendor' +require 'i18n' I18n.load_path << "#{File.dirname(__FILE__)}/active_support/locale/en.yml" diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb index 75706855d6..423d5448c3 100644 --- a/activesupport/lib/active_support/autoload.rb +++ b/activesupport/lib/active_support/autoload.rb @@ -17,6 +17,7 @@ module ActiveSupport autoload :Multibyte, 'active_support/multibyte' autoload :NewCallbacks, 'active_support/new_callbacks' autoload :OptionMerger, 'active_support/option_merger' + autoload :Orchestra, 'active_support/orchestra' autoload :OrderedHash, 'active_support/ordered_hash' autoload :OrderedOptions, 'active_support/ordered_options' autoload :Rescuable, 'active_support/rescuable' diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index e28df8efa5..25f9555388 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -107,16 +107,14 @@ module ActiveSupport class Store cattr_accessor :logger - attr_reader :silence, :logger_off + attr_reader :silence + alias :silence? :silence def silence! @silence = true self end - alias silence? silence - alias logger_off? logger_off - # Fetches data from the cache, using the given key. If there is data in # the cache with the given key, then that data is returned. # @@ -157,26 +155,13 @@ module ActiveSupport # cache.fetch("foo") # => "bar" # sleep(6) # cache.fetch("foo") # => nil - def fetch(key, options = {}) - @logger_off = true + def fetch(key, options = {}, &block) if !options[:force] && value = read(key, options) - @logger_off = false - log("hit", key, options) value elsif block_given? - @logger_off = false - log("miss", key, options) - - value = nil - ms = Benchmark.ms { value = yield } - - @logger_off = true - write(key, value, options) - @logger_off = false - - log('write (will save %.2fms)' % ms, key, nil) - - value + result = instrument(:generate, key, options, &block) + write(key, result, options) + result end end @@ -191,8 +176,8 @@ module ActiveSupport # For example, FileStore supports the +:expires_in+ option, which # makes the method return nil for cache items older than the specified # period. - def read(key, options = nil) - log("read", key, options) + def read(key, options = nil, &block) + instrument(:read, key, options, &block) end # Writes the given value to the cache, with the given key. @@ -210,20 +195,20 @@ module ActiveSupport # cache.read("foo") # => "bar" # sleep(6) # cache.read("foo") # => nil - def write(key, value, options = nil) - log("write", key, options) + def write(key, value, options = nil, &block) + instrument(:write, key, options, &block) end - def delete(key, options = nil) - log("delete", key, options) + def delete(key, options = nil, &block) + instrument(:delete, key, options, &block) end - def delete_matched(matcher, options = nil) - log("delete matched", matcher.inspect, options) + def delete_matched(matcher, options = nil, &block) + instrument(:delete_matched, matcher.inspect, options, &block) end - def exist?(key, options = nil) - log("exist?", key, options) + def exist?(key, options = nil, &block) + instrument(:exist?, key, options, &block) end def increment(key, amount = 1) @@ -247,14 +232,21 @@ module ActiveSupport private def expires_in(options) expires_in = options && options[:expires_in] - raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric) - expires_in || 0 end + def instrument(operation, key, options, &block) + payload = { :key => key } + payload.merge!(options) if options.is_a?(Hash) + + event = ActiveSupport::Orchestra.instrument(:"cache_#{operation}", payload, &block) + log("#{operation} (%.1fms)" % event.duration, key, options) + event.result + end + def log(operation, key, options) - logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off? + logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? end end end diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 75eed5ed94..7521efe7c5 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -16,49 +16,53 @@ module ActiveSupport # - +:expires_in+ - the number of seconds that this value may stay in # the cache. def read(name, options = nil) - super + super do + file_name = real_file_path(name) + expires = expires_in(options) - file_name = real_file_path(name) - expires = expires_in(options) - - if File.exist?(file_name) && (expires <= 0 || Time.now - File.mtime(file_name) < expires) - File.open(file_name, 'rb') { |f| Marshal.load(f) } + if File.exist?(file_name) && (expires <= 0 || Time.now - File.mtime(file_name) < expires) + File.open(file_name, 'rb') { |f| Marshal.load(f) } + end end end # Writes a value to the cache. def write(name, value, options = nil) - super - ensure_cache_path(File.dirname(real_file_path(name))) - File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) } - value + super do + ensure_cache_path(File.dirname(real_file_path(name))) + File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) } + value + end rescue => e logger.error "Couldn't create cache directory: #{name} (#{e.message})" if logger end def delete(name, options = nil) - super - File.delete(real_file_path(name)) + super do + File.delete(real_file_path(name)) + end rescue SystemCallError => e # If there's no cache, then there's nothing to complain about end def delete_matched(matcher, options = nil) - super - search_dir(@cache_path) do |f| - if f =~ matcher - begin - File.delete(f) - rescue SystemCallError => e - # If there's no cache, then there's nothing to complain about + super do + search_dir(@cache_path) do |f| + if f =~ matcher + begin + File.delete(f) + rescue SystemCallError => e + # If there's no cache, then there's nothing to complain about + end end end end end def exist?(name, options = nil) - super - File.exist?(real_file_path(name)) + super do + File.exist?(real_file_path(name)) + end end private diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 96a8000778..516af99ce9 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -54,8 +54,9 @@ module ActiveSupport end def read(key, options = nil) # :nodoc: - super - @data.get(key, raw?(options)) + super do + @data.get(key, raw?(options)) + end rescue MemCache::MemCacheError => e logger.error("MemCacheError (#{e}): #{e.message}") nil @@ -69,22 +70,24 @@ module ActiveSupport # - <tt>:expires_in</tt> - the number of seconds that this value may stay in # the cache. See ActiveSupport::Cache::Store#write for an example. def write(key, value, options = nil) - super - method = options && options[:unless_exist] ? :add : :set - # memcache-client will break the connection if you send it an integer - # in raw mode, so we convert it to a string to be sure it continues working. - value = value.to_s if raw?(options) - response = @data.send(method, key, value, expires_in(options), raw?(options)) - response == Response::STORED + super do + method = options && options[:unless_exist] ? :add : :set + # memcache-client will break the connection if you send it an integer + # in raw mode, so we convert it to a string to be sure it continues working. + value = value.to_s if raw?(options) + response = @data.send(method, key, value, expires_in(options), raw?(options)) + response == Response::STORED + end rescue MemCache::MemCacheError => e logger.error("MemCacheError (#{e}): #{e.message}") false end def delete(key, options = nil) # :nodoc: - super - response = @data.delete(key, expires_in(options)) - response == Response::DELETED + super do + response = @data.delete(key, expires_in(options)) + response == Response::DELETED + end rescue MemCache::MemCacheError => e logger.error("MemCacheError (#{e}): #{e.message}") false @@ -94,7 +97,9 @@ module ActiveSupport # Doesn't call super, cause exist? in memcache is in fact a read # But who cares? Reading is very fast anyway # Local cache is checked first, if it doesn't know then memcache itself is read from - !read(key, options).nil? + super do + !read(key, options).nil? + end end def increment(key, amount = 1) # :nodoc: diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index 21ba79cf3d..66ce1bc93a 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -20,28 +20,33 @@ module ActiveSupport end def read(name, options = nil) - super - @data[name] + super do + @data[name] + end end def write(name, value, options = nil) - super - @data[name] = (value.duplicable? ? value.dup : value).freeze + super do + @data[name] = (value.duplicable? ? value.dup : value).freeze + end end def delete(name, options = nil) - super - @data.delete(name) + super do + @data.delete(name) + end end def delete_matched(matcher, options = nil) - super - @data.delete_if { |k,v| k =~ matcher } + super do + @data.delete_if { |k,v| k =~ matcher } + end end def exist?(name,options = nil) - super - @data.has_key?(name) + super do + @data.has_key?(name) + end end def clear diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 1fe4ffb8e1..ce3bebc25a 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -82,6 +82,7 @@ class Date # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>. def advance(options) + options = options.dup d = self d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] diff --git a/activesupport/lib/active_support/core_ext/rexml.rb b/activesupport/lib/active_support/core_ext/rexml.rb index decc4f6d9c..5288b639a6 100644 --- a/activesupport/lib/active_support/core_ext/rexml.rb +++ b/activesupport/lib/active_support/core_ext/rexml.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/kernel/reporting' + # Fixes the rexml vulnerability disclosed at: # http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/ # This fix is identical to rexml-expansion-fix version 1.0.1 @@ -5,11 +7,11 @@ require 'rexml/rexml' # Earlier versions of rexml defined REXML::Version, newer ones REXML::VERSION unless (defined?(REXML::VERSION) ? REXML::VERSION : REXML::Version) > "3.1.7.2" - require 'rexml/document' + silence_warnings { require 'rexml/document' } # REXML in 1.8.7 has the patch but early patchlevels didn't update Version from 3.1.7.2. unless REXML::Document.respond_to?(:entity_expansion_limit=) - require 'rexml/entity' + silence_warnings { require 'rexml/entity' } module REXML #:nodoc: class Entity < Child #:nodoc: diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb index 649e6301d1..c6c17a3c4e 100644 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -2,10 +2,9 @@ require 'json' unless defined?(JSON) module ActiveSupport module JSON - ParseError = ::JSON::ParserError unless const_defined?(:ParseError) - module Backends module JSONGem + ParseError = ::JSON::ParserError extend self # Parses a JSON string or IO and convert it into an object @@ -38,4 +37,4 @@ module ActiveSupport end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 59d2c37e40..215b3d6f90 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -2,13 +2,9 @@ require 'active_support/core_ext/string/starts_ends_with' module ActiveSupport module JSON - unless const_defined?(:ParseError) - class ParseError < StandardError - end - end - module Backends module Yaml + ParseError = ::StandardError extend self # Parses a JSON string or IO and converts it into an object diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 356b6cebeb..a5908365af 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -7,6 +7,7 @@ module ActiveSupport module JSON class << self + attr_reader :parse_error delegate :decode, :to => :backend def backend @@ -21,6 +22,7 @@ module ActiveSupport require "active_support/json/backends/#{name.to_s.downcase}.rb" @backend = ActiveSupport::JSON::Backends::const_get(name) end + @parse_error = @backend::ParseError end def with_backend(name) diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 7724b9d88b..b197fbc9bf 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -57,10 +57,10 @@ module ActiveSupport end end - def flush_cache(*syms, &block) + def flush_cache(*syms) syms.each do |sym| (methods + private_methods + protected_methods).each do |m| - if m.to_s =~ /^_unmemoized_(#{sym})/ + if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/ ivar = ActiveSupport::Memoizable.memoized_ivar_for($1) instance_variable_get(ivar).clear if instance_variable_defined?(ivar) end diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index b24acb9f47..74e080a23d 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/bytesize' + module ActiveSupport # MessageVerifier makes it easy to generate and verify messages which are signed # to prevent tampering. @@ -25,10 +27,10 @@ module ActiveSupport def verify(signed_message) data, digest = signed_message.split("--") - if digest != generate_digest(data) - raise InvalidSignature - else + if secure_compare(digest, generate_digest(data)) Marshal.load(ActiveSupport::Base64.decode64(data)) + else + raise InvalidSignature end end @@ -38,9 +40,20 @@ module ActiveSupport end private + # constant-time comparison algorithm to prevent timing attacks + def secure_compare(a, b) + return false unless a.bytesize == b.bytesize + + l = a.unpack "C#{a.bytesize}" + + res = 0 + b.each_byte { |byte| res |= byte ^ l.shift } + res == 0 + end + def generate_digest(data) require 'openssl' unless defined?(OpenSSL) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data) + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data) end end end diff --git a/activesupport/lib/active_support/mini.rb b/activesupport/lib/active_support/mini.rb deleted file mode 100644 index b787650655..0000000000 --- a/activesupport/lib/active_support/mini.rb +++ /dev/null @@ -1,9 +0,0 @@ -$LOAD_PATH.unshift File.dirname(__FILE__) - -# whole object.rb pulls up rarely used introspection extensions -require "core_ext/object/blank" -require "core_ext/object/metaclass" -require 'core_ext/array' -require 'core_ext/hash' -require 'core_ext/module/attribute_accessors' -require 'core_ext/string/inflections' diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb index d8d58f3bce..6f2016a409 100644 --- a/activesupport/lib/active_support/multibyte.rb +++ b/activesupport/lib/active_support/multibyte.rb @@ -1,9 +1,5 @@ # encoding: utf-8 -require 'active_support/multibyte/chars' -require 'active_support/multibyte/exceptions' -require 'active_support/multibyte/unicode_database' - require 'active_support/core_ext/module/attribute_accessors' module ActiveSupport #:nodoc: @@ -29,7 +25,35 @@ module ActiveSupport #:nodoc: # # Example: # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 - mattr_accessor :proxy_class - self.proxy_class = ActiveSupport::Multibyte::Chars + def self.proxy_class=(klass) + @proxy_class = klass + end + + # Returns the currect proxy class + def self.proxy_class + @proxy_class ||= ActiveSupport::Multibyte::Chars + end + + # Regular expressions that describe valid byte sequences for a character + VALID_CHARACTER = { + # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site) + 'UTF-8' => /\A(?: + [\x00-\x7f] | + [\xc2-\xdf] [\x80-\xbf] | + \xe0 [\xa0-\xbf] [\x80-\xbf] | + [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] | + \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] | + [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] | + \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn, + # Quick check for valid Shift-JIS characters, disregards the odd-even pairing + 'Shift_JIS' => /\A(?: + [\x00-\x7e \xa1-\xdf] | + [\x81-\x9f \xe0-\xef] [\x40-\x7e \x80-\x9e \x9f-\xfc])\z /xn + } end end + +require 'active_support/multibyte/chars' +require 'active_support/multibyte/exceptions' +require 'active_support/multibyte/unicode_database' +require 'active_support/multibyte/utils' diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 64a35dca40..579ccc124d 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -74,16 +74,7 @@ module ActiveSupport #:nodoc: UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/ - # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site) - UTF8_PAT = /\A(?: - [\x00-\x7f] | - [\xc2-\xdf] [\x80-\xbf] | - \xe0 [\xa0-\xbf] [\x80-\xbf] | - [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] | - \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] | - [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] | - \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf] - )*\z/xn + UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'] attr_reader :wrapped_string alias to_s wrapped_string @@ -308,23 +299,23 @@ module ActiveSupport #:nodoc: def rstrip chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, '')) end - + # Strips entire range of Unicode whitespace from the left of the string. def lstrip chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, '')) end - + # Strips entire range of Unicode whitespace from the right and left of the string. def strip rstrip.lstrip end - + # Returns the number of codepoints in the string def size self.class.u_unpack(@wrapped_string).size end alias_method :length, :size - + # Reverses all characters in the string. # # Example: @@ -332,7 +323,7 @@ module ActiveSupport #:nodoc: def reverse chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*')) end - + # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that # character. # @@ -647,7 +638,7 @@ module ActiveSupport #:nodoc: string.split(//u).map do |c| c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding) - if !UTF8_PAT.match(c) + if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c) n = c.unpack('C')[0] n < 128 ? n.chr : n < 160 ? [UCD.cp1252[n] || n].pack('U') : diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb new file mode 100644 index 0000000000..8e47763d39 --- /dev/null +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -0,0 +1,61 @@ +# encoding: utf-8 + +module ActiveSupport #:nodoc: + module Multibyte #:nodoc: + if Kernel.const_defined?(:Encoding) + # Returns a regular expression that matches valid characters in the current encoding + def self.valid_character + VALID_CHARACTER[Encoding.default_external.to_s] + end + else + def self.valid_character + case $KCODE + when 'UTF8' + VALID_CHARACTER['UTF-8'] + when 'SJIS' + VALID_CHARACTER['Shift_JIS'] + end + end + end + + if 'string'.respond_to?(:valid_encoding?) + # Verifies the encoding of a string + def self.verify(string) + string.valid_encoding? + end + else + def self.verify(string) + if expression = valid_character + for c in string.split(//) + return false unless expression.match(c) + end + end + true + end + end + + # Verifies the encoding of the string and raises an exception when it's not valid + def self.verify!(string) + raise EncodingError.new("Found characters with invalid encoding") unless verify(string) + end + + if 'string'.respond_to?(:force_encoding) + # Removes all invalid characters from the string. + # + # Note: this method is a no-op in Ruby 1.9 + def self.clean(string) + string + end + else + def self.clean(string) + if expression = valid_character + stripped = []; for c in string.split(//) + stripped << c if expression.match(c) + end; stripped.join + else + string + end + end + end + end +end diff --git a/activesupport/lib/active_support/new_callbacks.rb b/activesupport/lib/active_support/new_callbacks.rb index 56b510d52e..2f0853d84a 100644 --- a/activesupport/lib/active_support/new_callbacks.rb +++ b/activesupport/lib/active_support/new_callbacks.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/kernel/reporting' module ActiveSupport # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic @@ -15,17 +16,17 @@ module ActiveSupport # end # # class ConfigStorage < Storage - # save_callback :before, :saving_message + # set_callback :save, :before, :saving_message # def saving_message # puts "saving..." # end # - # save_callback :after do |object| + # set_callback :save, :after do |object| # puts "saved" # end # # def save - # _run_save_callbacks do + # _run_set_callback :save,s do # puts "- save" # end # end @@ -47,24 +48,24 @@ module ActiveSupport # # define_callbacks :save # - # save_callback :before, :prepare + # set_callback :save, :before, :prepare # def prepare # puts "preparing save" # end # end # # class ConfigStorage < Storage - # save_callback :before, :saving_message + # set_callback :save, :before, :saving_message # def saving_message # puts "saving..." # end # - # save_callback :after do |object| + # set_callback :save, :after do |object| # puts "saved" # end # # def save - # _run_save_callbacks do + # _run_set_callback :save,s do # puts "- save" # end # end @@ -78,22 +79,23 @@ module ActiveSupport # saving... # - save # saved + # module NewCallbacks def self.included(klass) klass.extend ClassMethods end - + def run_callbacks(kind, options = {}, &blk) send("_run_#{kind}_callbacks", &blk) end - + class Callback @@_callback_sequence = 0 - - attr_accessor :filter, :kind, :name, :options, :per_key, :klass - def initialize(filter, kind, options, klass) - @kind, @klass = kind, klass - + + attr_accessor :chain, :filter, :kind, :options, :per_key, :klass + + def initialize(chain, filter, kind, options, klass) + @chain, @kind, @klass = chain, kind, klass normalize_options!(options) @per_key = options.delete(:per_key) @@ -104,9 +106,10 @@ module ActiveSupport _compile_per_key_options end - - def clone(klass) + + def clone(chain, klass) obj = super() + obj.chain = chain obj.klass = klass obj.per_key = @per_key.dup obj.options = @options.dup @@ -116,7 +119,7 @@ module ActiveSupport obj.options[:unless] = @options[:unless].dup obj end - + def normalize_options!(options) options[:if] = Array.wrap(options[:if]) options[:unless] = Array.wrap(options[:unless]) @@ -125,25 +128,28 @@ module ActiveSupport options[:per_key][:if] = Array.wrap(options[:per_key][:if]) options[:per_key][:unless] = Array.wrap(options[:per_key][:unless]) end - + + def name + chain.name + end + def next_id @@_callback_sequence += 1 end - + def matches?(_kind, _filter) - @kind == _kind && - @filter == _filter + @kind == _kind && @filter == _filter end def _update_filter(filter_options, new_options) filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless) filter_options[:unless].push(new_options[:if]) if new_options.key?(:if) end - + def recompile!(_options, _per_key) _update_filter(self.options, _options) _update_filter(self.per_key, _per_key) - + @callback_id = next_id @filter = _compile_filter(@raw_filter) @compiled_options = _compile_options(@options) @@ -162,16 +168,12 @@ module ActiveSupport # This will supply contents for before and around filters, and no # contents for after filters (for the forward pass). - def start(key = nil, options = {}) - object, terminator = (options || {}).values_at(:object, :terminator) - + def start(key=nil, object=nil) return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?") - - terminator ||= false - + # options[0] is the compiled form of supplied conditions # options[1] is the "end" for the conditional - + # if @kind == :before || @kind == :around if @kind == :before # if condition # before_save :filter_name, :if => :condition @@ -180,10 +182,10 @@ module ActiveSupport filter = <<-RUBY_EVAL unless halted result = #{@filter} - halted = (#{terminator}) + halted = (#{chain.config[:terminator]}) end RUBY_EVAL - + [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n") else # Compile around filters with conditions into proxy methods @@ -200,7 +202,7 @@ module ActiveSupport # yield self # end # end - + # name = "_conditional_callback_#{@kind}_#{next_id}" txt, line = <<-RUBY_EVAL, __LINE__ + 1 def #{name}(halted) @@ -221,11 +223,9 @@ module ActiveSupport # This will supply contents for around and after filters, but not # before filters (for the backward pass). - def end(key = nil, options = {}) - object = (options || {})[:object] - + def end(key=nil, object=nil) return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?") - + if @kind == :around || @kind == :after # if condition # after_save :filter_name, :if => :condition # filter_name @@ -237,28 +237,30 @@ module ActiveSupport end end end - + private + # Options support the same options as filters themselves (and support # symbols, string, procs, and objects), so compile a conditional # expression based on the options - def _compile_options(options) + def _compile_options(options) return [] if options[:if].empty? && options[:unless].empty? - + conditions = [] - + unless options[:if].empty? conditions << Array.wrap(_compile_filter(options[:if])) end - + unless options[:unless].empty? conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"} end - + ["if #{conditions.flatten.join(" && ")}", "end"] end - + # Filters support: + # # Arrays:: Used in conditions. This is used to specify # multiple conditions. Used internally to # merge conditions from skip_* filters @@ -269,6 +271,7 @@ module ActiveSupport # # All of these objects are compiled into methods and handled # the same after this point: + # # Arrays:: Merged together into a single filter # Symbols:: Already methods # Strings:: class_eval'ed into methods @@ -276,6 +279,7 @@ module ActiveSupport # Objects:: # a method is created that calls the before_foo method # on the object. + # def _compile_filter(filter) method_name = "_callback_#{@kind}_#{next_id}" case filter @@ -287,17 +291,19 @@ module ActiveSupport "(#{filter})" when Proc @klass.send(:define_method, method_name, &filter) - return method_name if filter.arity == 0 + return method_name if filter.arity <= 0 method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") else @klass.send(:define_method, "#{method_name}_object") { filter } _normalize_legacy_filter(kind, filter) + scopes = Array.wrap(chain.config[:scope]) + method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_") @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{method_name}(&blk) - #{method_name}_object.send(:#{kind}, self, &blk) + #{method_name}_object.send(:#{method_to_call}, self, &blk) end RUBY_EVAL @@ -318,59 +324,82 @@ module ActiveSupport end end end - end # An Array with a compile method class CallbackChain < Array - def initialize(symbol) - @symbol = symbol + attr_reader :name, :config + + def initialize(name, config) + @name = name + @config = { + :terminator => "false", + :rescuable => false, + :scope => [ :kind ] + }.merge(config) end - - def compile(key = nil, options = {}) + + def compile(key=nil, object=nil) method = [] + method << "value = nil" method << "halted = false" + each do |callback| - method << callback.start(key, options) + method << callback.start(key, object) + end + + if config[:rescuable] + method << "rescued_error = nil" + method << "begin" + end + + method << "value = yield if block_given? && !halted" + + if config[:rescuable] + method << "rescue Exception => e" + method << "rescued_error = e" + method << "end" end - method << "yield self if block_given? && !halted" + reverse_each do |callback| - method << callback.end(key, options) + method << callback.end(key, object) end + + method << "raise rescued_error if rescued_error" if config[:rescuable] + method << "halted ? false : (block_given? ? value : true)" method.compact.join("\n") end - + def clone(klass) - chain = CallbackChain.new(@symbol) - chain.push(*map {|c| c.clone(klass)}) + chain = CallbackChain.new(@name, @config.dup) + callbacks = map { |c| c.clone(chain, klass) } + chain.push(*callbacks) end end - + module ClassMethods - CHAINS = {:before => :before, :around => :before, :after => :after} - - # Make the _run_save_callbacks method. The generated method takes + # Make the _run_set_callback :save method. The generated method takes # a block that it'll yield to. It'll call the before and around filters # in order, yield the block, and then run the after filters. # - # _run_save_callbacks do + # _run_set_callback :save do # save # end # - # The _run_save_callbacks method can optionally take a key, which + # The _run_set_callback :save method can optionally take a key, which # will be used to compile an optimized callback method for each # key. See #define_callbacks for more information. - def _define_runner(symbol) - body = send("_#{symbol}_callbacks"). - compile(nil, :terminator => send("_#{symbol}_terminator")) + # + def __define_runner(symbol) #:nodoc: + body = send("_#{symbol}_callbacks").compile(nil) body, line = <<-RUBY_EVAL, __LINE__ def _run_#{symbol}_callbacks(key = nil, &blk) if key name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks" - + unless respond_to?(name) - self.class._create_keyed_callback(name, :#{symbol}, self, &blk) + self.class.__create_keyed_callback(name, :#{symbol}, self, &blk) end send(name, &blk) @@ -379,40 +408,49 @@ module ActiveSupport end end RUBY_EVAL - - undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") - class_eval body, __FILE__, line + + silence_warnings do + undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") + class_eval body, __FILE__, line + end end # This is called the first time a callback is called with a particular # key. It creates a new callback method for the key, calculating # which callbacks can be omitted because of per_key conditions. - def _create_keyed_callback(name, kind, obj, &blk) + # + def __create_keyed_callback(name, kind, object, &blk) #:nodoc: @_keyed_callbacks ||= {} @_keyed_callbacks[name] ||= begin - str = send("_#{kind}_callbacks"). - compile(name, :object => obj, :terminator => send("_#{kind}_terminator")) - + str = send("_#{kind}_callbacks").compile(name, object) class_eval "def #{name}() #{str} end", __FILE__, __LINE__ - true end end - - # Define callbacks. + + # This is used internally to append, prepend and skip callbacks to the + # CallbackChain. # - # Creates a <name>_callback method that you can use to add callbacks. + def __update_callbacks(name, filters = [], block = nil) #:nodoc: + type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before + options = filters.last.is_a?(Hash) ? filters.pop : {} + filters.unshift(block) if block + + chain = send("_#{name}_callbacks") + yield chain, type, filters, options if block_given? + + __define_runner(name) + end + + # Set callbacks for a previously defined callback. # # Syntax: - # save_callback :before, :before_meth - # save_callback :after, :after_meth, :if => :condition - # save_callback :around {|r| stuff; yield; stuff } - # - # The <name>_callback method also updates the _run_<name>_callbacks - # method, which is the public API to run the callbacks. + # set_callback :save, :before, :before_meth + # set_callback :save, :after, :after_meth, :if => :condition + # set_callback :save, :around, lambda { |r| stuff; yield; stuff } # - # Also creates a skip_<name>_callback method that you can use to skip - # callbacks. + # It also updates the _run_<name>_callbacks method, which is the public + # API to run the callbacks. Use skip_callback to skip any defined one. # # When creating or skipping callbacks, you can specify conditions that # are always the same for a given key. For instance, in ActionPack, @@ -425,62 +463,99 @@ module ActiveSupport # Per-Key conditions are evaluated only once per use of a given key. # In the case of the above example, you would do: # - # run_dispatch_callbacks(action_name) { ... dispatch stuff ... } + # _run_dispatch_callbacks(action_name) { ... dispatch stuff ... } # # In that case, each action_name would get its own compiled callback # method that took into consideration the per_key conditions. This # is a speed improvement for ActionPack. - def _update_callbacks(name, filters = CallbackChain.new(name), block = nil) - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before - options = filters.last.is_a?(Hash) ? filters.pop : {} - filters.unshift(block) if block - - callbacks = send("_#{name}_callbacks") - yield callbacks, type, filters, options if block_given? - - _define_runner(name) - end - - alias_method :_reset_callbacks, :_update_callbacks - + # def set_callback(name, *filters, &block) - _update_callbacks(name, filters, block) do |callbacks, type, filters, options| + __update_callbacks(name, filters, block) do |chain, type, filters, options| filters.map! do |filter| - # overrides parent class - callbacks.delete_if {|c| c.matches?(type, filter) } - Callback.new(filter, type, options.dup, self) + chain.delete_if {|c| c.matches?(type, filter) } + Callback.new(chain, filter, type, options.dup, self) end - options[:prepend] ? callbacks.unshift(*filters) : callbacks.push(*filters) + options[:prepend] ? chain.unshift(*filters) : chain.push(*filters) end end + # Skip a previously defined callback for a given type. + # def skip_callback(name, *filters, &block) - _update_callbacks(name, filters, block) do |callbacks, type, filters, options| - filters.each do |filter| - callbacks = send("_#{name}_callbacks=", callbacks.clone(self)) + __update_callbacks(name, filters, block) do |chain, type, filters, options| + chain = send("_#{name}_callbacks=", chain.clone(self)) - filter = callbacks.find {|c| c.matches?(type, filter) } + filters.each do |filter| + filter = chain.find {|c| c.matches?(type, filter) } if filter && options.any? filter.recompile!(options, options[:per_key] || {}) else - callbacks.delete(filter) + chain.delete(filter) end end end end + # Reset callbacks for a given type. + # + def reset_callbacks(symbol) + send("_#{symbol}_callbacks").clear + __define_runner(symbol) + end + + # Define callbacks types. + # + # ==== Example + # + # define_callbacks :validate + # + # ==== Options + # + # * <tt>:terminator</tt> - Indicates when a before filter is considered + # to be halted. + # + # define_callbacks :validate, :terminator => "result == false" + # + # In the example above, if any before validate callbacks returns false, + # other callbacks are not executed. Defaults to "false". + # + # * <tt>:rescuable</tt> - By default, after filters are not executed if + # the given block or an before_filter raises an error. Supply :rescuable => true + # to change this behavior. + # + # * <tt>:scope</tt> - Show which methods should be executed when a class + # is giben as callback: + # + # define_callbacks :filters, :scope => [ :kind ] + # + # When a class is given: + # + # before_filter MyFilter + # + # It will call the type of the filter in the given class, which in this + # case, is "before". + # + # If, for instance, you supply the given scope: + # + # define_callbacks :validate, :scope => [ :kind, :name ] + # + # It will call "#{kind}_#{name}" in the given class. So "before_validate" + # will be called in the class below: + # + # before_validate MyValidation + # + # Defaults to :kind. + # def define_callbacks(*symbols) - terminator = symbols.pop if symbols.last.is_a?(String) + config = symbols.last.is_a?(Hash) ? symbols.pop : {} symbols.each do |symbol| - extlib_inheritable_accessor("_#{symbol}_terminator") { terminator } - extlib_inheritable_accessor("_#{symbol}_callbacks") do - CallbackChain.new(symbol) + CallbackChain.new(symbol, config) end - _define_runner(symbol) + __define_runner(symbol) end end end diff --git a/activesupport/lib/active_support/orchestra.rb b/activesupport/lib/active_support/orchestra.rb new file mode 100644 index 0000000000..efe30669d8 --- /dev/null +++ b/activesupport/lib/active_support/orchestra.rb @@ -0,0 +1,103 @@ +require 'thread' + +module ActiveSupport + # Orchestra provides an instrumentation API for Ruby. To instrument an action + # in Ruby you just need to: + # + # ActiveSupport::Orchestra.instrument(:render, :extra => :information) do + # render :text => "Foo" + # end + # + # Those actions are consumed by listeners. A listener is anything that responds + # to push. You can even register an array: + # + # @listener = [] + # ActiveSupport::Orchestra.register @listener + # + # ActiveSupport::Orchestra.instrument(:render, :extra => :information) do + # render :text => "Foo" + # end + # + # event #=> ActiveSupport::Orchestra::Event + # event.name #=> :render + # event.duration #=> 10 (in miliseconds) + # event.result #=> "Foo" + # event.payload #=> { :extra => :information } + # + # Orchestra ships with a default listener implementation which puts events in + # a stream and consume them in a Thread. This implementation is thread safe + # and is available at ActiveSupport::Orchestra::Listener. + # + module Orchestra + @stacked_events = Hash.new { |h,k| h[k] = [] } + @listeners = [] + + def self.instrument(name, payload=nil) + stack = @stacked_events[Thread.current.object_id] + event = Event.new(name, stack.last, payload) + stack << event + event.result = yield + event + ensure + event.finish! + stack.delete(event) + @listeners.each { |s| s.push(event) } + end + + def self.register(listener) + @listeners << listener + end + + def self.unregister(listener) + @listeners.delete(listener) + end + + class Event + attr_reader :name, :time, :duration, :parent, :thread_id, :payload + attr_accessor :result + + def initialize(name, parent=nil, payload=nil) + @name = name + @time = Time.now + @thread_id = Thread.current.object_id + @parent = parent + @payload = payload + end + + def finish! + @duration = 1000 * (Time.now.to_f - @time.to_f) + end + end + + class Listener + attr_reader :mutex, :signaler, :thread + + def initialize + @mutex, @signaler = Mutex.new, ConditionVariable.new + @stream = [] + @thread = Thread.new do + loop do + (event = @stream.shift) ? consume(event) : wait + end + end + end + + def wait + @mutex.synchronize do + @signaler.wait(@mutex) + end + end + + def push(event) + @mutex.synchronize do + @stream.push(event) + @signaler.broadcast + end + end + + def consume(event) + raise NotImplementedError + end + end + end +end diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index c915bf799d..1646891e00 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -7,7 +7,6 @@ require 'active_support/testing/pending' require 'active_support/testing/isolation' begin - gem 'mocha', ">= 0.9.7" require 'mocha' rescue LoadError # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh. diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 30e194536b..cdd6d5f49b 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -31,10 +31,15 @@ module ActiveSupport @_result = result - proxy = run_in_isolation do |proxy| - super(proxy) { } + serialized = run_in_isolation do |proxy| + begin + super(proxy) { } + rescue Exception => e + proxy.add_error(Test::Unit::Error.new(name, e)) + end end + proxy = Marshal.load(serialized) proxy.__replay__(@_result) yield(Test::Unit::TestCase::FINISHED, name) @@ -55,7 +60,7 @@ module ActiveSupport write.close result = read.read Process.wait2(pid) - Marshal.load(result.unpack("m")[0]) + return result.unpack("m")[0] end end @@ -83,7 +88,7 @@ module ActiveSupport ENV.delete("ISOLATION_TEST") ENV.delete("ISOLATION_OUTPUT") - return Marshal.load(tmpfile.read.unpack("m")[0]) + return tmpfile.read.unpack("m")[0] end end end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index f8d12e82b3..ab34f975f6 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,5 +1,3 @@ -require 'rubygems' -gem 'ruby-prof', '>= 0.6.1' require 'ruby-prof' require 'fileutils' diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index ca64c5ebe3..9f464c8246 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -1,27 +1,16 @@ -# Prefer gems to the bundled libs. -require 'rubygems' - -begin - gem 'builder', '~> 2.1.2' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2" -end - -begin - gem 'memcache-client', '>= 1.6.5' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.6.5" -end - -begin - gem 'tzinfo', '~> 0.3.13' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.13" +def ActiveSupport.requirable?(file) + $LOAD_PATH.any? { |p| Dir.glob("#{p}/#{file}.*").any? } end -begin - gem 'i18n', '~> 0.1.3' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib" - require 'i18n' +[%w(builder 2.1.2), %w(i18n 0.1.3), %w(memcache-client 1.7.5), %w(tzinfo 0.3.13)].each do |lib, version| + # If the lib is not already requirable + unless ActiveSupport.requirable? lib + # Try to activate a gem ~> satisfying the requested version first. + begin + gem lib, "~> #{version}" + # Use the vendored lib if the gem's missing or we aren't using RubyGems. + rescue LoadError, NoMethodError + $LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/vendor/#{lib}-#{version}/lib") + end + end end diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/blankslate.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/blankslate.rb index da6034d9c4..da6034d9c4 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/blankslate.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/blankslate.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder.rb index 9719277669..9719277669 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/blankslate.rb index 2935b6f1d1..2935b6f1d1 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/blankslate.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/css.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/css.rb index e086a1b132..e086a1b132 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/css.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/css.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xchar.rb index 8bdbd05899..8bdbd05899 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xchar.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlbase.rb index ace4b56d59..ace4b56d59 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlbase.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlevents.rb index b373e4da3c..b373e4da3c 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlevents.rb diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlmarkup.rb index ec59dddc36..ec59dddc36 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlmarkup.rb diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.7.5/lib/memcache.rb index 4d594c2089..3fcc388ba0 100644 --- a/activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb +++ b/activesupport/lib/active_support/vendor/memcache-client-1.7.5/lib/memcache.rb @@ -2,9 +2,9 @@ $TESTING = defined?($TESTING) && $TESTING require 'socket' require 'thread' -require 'timeout' require 'zlib' require 'digest/sha1' +require 'net/protocol' ## # A Ruby client library for memcached. @@ -15,18 +15,22 @@ class MemCache ## # The version of MemCache you are using. - VERSION = '1.6.4.99' + VERSION = '1.7.5' ## # Default options for the cache object. DEFAULT_OPTIONS = { - :namespace => nil, - :readonly => false, - :multithread => true, - :failover => true, - :timeout => 0.5, - :logger => nil, + :namespace => nil, + :readonly => false, + :multithread => true, + :failover => true, + :timeout => 0.5, + :logger => nil, + :no_reply => false, + :check_size => true, + :autofix_keys => false, + :namespace_separator => ':', } ## @@ -50,6 +54,19 @@ class MemCache attr_reader :multithread ## + # Whether to try to fix keys that are too long and will be truncated by + # using their SHA1 hash instead. + # The hash is only used on keys longer than 250 characters, or containing spaces, + # to avoid impacting performance unnecesarily. + # + # In theory, your code should generate correct keys when calling memcache, + # so it's your responsibility and you should try to fix this problem at its source. + # + # But if that's not possible, enable this option and memcache-client will give you a hand. + + attr_reader :autofix_keys + + ## # The servers this client talks to. Play at your own peril. attr_reader :servers @@ -72,19 +89,32 @@ class MemCache attr_reader :logger ## + # Don't send or look for a reply from the memcached server for write operations. + # Please note this feature only works in memcached 1.2.5 and later. Earlier + # versions will reply with "ERROR". + attr_reader :no_reply + + ## # Accepts a list of +servers+ and a list of +opts+. +servers+ may be # omitted. See +servers=+ for acceptable server list arguments. # # Valid options for +opts+ are: # - # [:namespace] Prepends this value to all keys added or retrieved. - # [:readonly] Raises an exception on cache writes when true. - # [:multithread] Wraps cache access in a Mutex for thread safety. - # [:failover] Should the client try to failover to another server if the - # first server is down? Defaults to true. - # [:timeout] Time to use as the socket read timeout. Defaults to 0.5 sec, - # set to nil to disable timeouts (this is a major performance penalty in Ruby 1.8). - # [:logger] Logger to use for info/debug output, defaults to nil + # [:namespace] Prepends this value to all keys added or retrieved. + # [:readonly] Raises an exception on cache writes when true. + # [:multithread] Wraps cache access in a Mutex for thread safety. Defaults to true. + # [:failover] Should the client try to failover to another server if the + # first server is down? Defaults to true. + # [:timeout] Time to use as the socket read timeout. Defaults to 0.5 sec, + # set to nil to disable timeouts. + # [:logger] Logger to use for info/debug output, defaults to nil + # [:no_reply] Don't bother looking for a reply for write operations (i.e. they + # become 'fire and forget'), memcached 1.2.5 and later only, speeds up + # set/add/delete/incr/decr significantly. + # [:check_size] Raises a MemCacheError if the value to be set is greater than 1 MB, which + # is the maximum key size for the standard memcached server. Defaults to true. + # [:autofix_keys] If a key is longer than 250 characters or contains spaces, + # use an SHA1 hash instead, to prevent collisions on truncated keys. # Other options are ignored. def initialize(*args) @@ -108,13 +138,17 @@ class MemCache end opts = DEFAULT_OPTIONS.merge opts - @namespace = opts[:namespace] - @readonly = opts[:readonly] - @multithread = opts[:multithread] - @timeout = opts[:timeout] - @failover = opts[:failover] - @logger = opts[:logger] - @mutex = Mutex.new if @multithread + @namespace = opts[:namespace] + @readonly = opts[:readonly] + @multithread = opts[:multithread] + @autofix_keys = opts[:autofix_keys] + @timeout = opts[:timeout] + @failover = opts[:failover] + @logger = opts[:logger] + @no_reply = opts[:no_reply] + @check_size = opts[:check_size] + @namespace_separator = opts[:namespace_separator] + @mutex = Mutex.new if @multithread logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger @@ -192,8 +226,8 @@ class MemCache def get(key, raw = false) with_server(key) do |server, cache_key| + logger.debug { "get #{key} from #{server.inspect}" } if logger value = cache_get server, cache_key - logger.debug { "GET #{key} from #{server.inspect}: #{value ? value.to_s.size : 'nil'}" } if logger return nil if value.nil? value = Marshal.load value unless raw return value @@ -203,6 +237,25 @@ class MemCache end ## + # Performs a +get+ with the given +key+. If + # the value does not exist and a block was given, + # the block will be called and the result saved via +add+. + # + # If you do not provide a block, using this + # method is the same as using +get+. + # + def fetch(key, expiry = 0, raw = false) + value = get(key, raw) + + if value.nil? && block_given? + value = yield + add(key, value, expiry, raw) + end + + value + end + + ## # Retrieves multiple values from memcached in parallel, if possible. # # The memcached protocol supports the ability to retrieve multiple @@ -280,18 +333,64 @@ class MemCache def set(key, value, expiry = 0, raw = false) raise MemCacheError, "Update of readonly cache" if @readonly + + value = Marshal.dump value unless raw with_server(key) do |server, cache_key| + logger.debug { "set #{key} to #{server.inspect}: #{value.to_s.size}" } if logger - value = Marshal.dump value unless raw - logger.debug { "SET #{key} to #{server.inspect}: #{value ? value.to_s.size : 'nil'}" } if logger + if @check_size && value.to_s.size > ONE_MB + raise MemCacheError, "Value too large, memcached can only store 1MB of data per key" + end - data = value.to_s - raise MemCacheError, "Value too large, memcached can only store 1MB of data per key" if data.size > ONE_MB + command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" - command = "set #{cache_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n" + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + + if result.nil? + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + result + end + end + end + + ## + # "cas" is a check and set operation which means "store this data but + # only if no one else has updated since I last fetched it." This can + # be used as a form of optimistic locking. + # + # Works in block form like so: + # cache.cas('some-key') do |value| + # value + 1 + # end + # + # Returns: + # +nil+ if the value was not found on the memcached server. + # +STORED+ if the value was updated successfully + # +EXISTS+ if the value was updated by someone else since last fetch + + def cas(key, expiry=0, raw=false) + raise MemCacheError, "Update of readonly cache" if @readonly + raise MemCacheError, "A block is required" unless block_given? + + (value, token) = gets(key, raw) + return nil unless value + updated = yield value + value = Marshal.dump updated unless raw + + with_server(key) do |server, cache_key| + logger.debug { "cas #{key} to #{server.inspect}: #{value.to_s.size}" } if logger + command = "cas #{cache_key} 0 #{expiry} #{value.to_s.size} #{token}#{noreply}\r\n#{value}\r\n" with_socket_management(server) do |socket| socket.write command + break nil if @no_reply result = socket.gets raise_on_error_response! result @@ -311,17 +410,79 @@ class MemCache # If +raw+ is true, +value+ will not be Marshalled. # # Readers should call this method in the event of a cache miss, not - # MemCache#set or MemCache#[]=. + # MemCache#set. def add(key, value, expiry = 0, raw = false) raise MemCacheError, "Update of readonly cache" if @readonly + value = Marshal.dump value unless raw + with_server(key) do |server, cache_key| + logger.debug { "add #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Add +key+ to the cache with value +value+ that expires in +expiry+ + # seconds, but only if +key+ already exists in the cache. + # If +raw+ is true, +value+ will not be Marshalled. + def replace(key, value, expiry = 0, raw = false) + raise MemCacheError, "Update of readonly cache" if @readonly + value = Marshal.dump value unless raw + with_server(key) do |server, cache_key| + logger.debug { "replace #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "replace #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Append - 'add this data to an existing key after existing data' + # Please note the value is always passed to memcached as raw since it + # doesn't make a lot of sense to concatenate marshalled data together. + def append(key, value) + raise MemCacheError, "Update of readonly cache" if @readonly with_server(key) do |server, cache_key| - value = Marshal.dump value unless raw - logger.debug { "ADD #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger - command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}\r\n#{value}\r\n" + logger.debug { "append #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "append #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n" with_socket_management(server) do |socket| socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Prepend - 'add this data to an existing key before existing data' + # Please note the value is always passed to memcached as raw since it + # doesn't make a lot of sense to concatenate marshalled data together. + def prepend(key, value) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + logger.debug { "prepend #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "prepend #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply result = socket.gets raise_on_error_response! result result @@ -336,7 +497,9 @@ class MemCache raise MemCacheError, "Update of readonly cache" if @readonly with_server(key) do |server, cache_key| with_socket_management(server) do |socket| - socket.write "delete #{cache_key} #{expiry}\r\n" + logger.debug { "delete #{cache_key} on #{server}" } if logger + socket.write "delete #{cache_key} #{expiry}#{noreply}\r\n" + break nil if @no_reply result = socket.gets raise_on_error_response! result result @@ -346,19 +509,33 @@ class MemCache ## # Flush the cache from all memcache servers. - - def flush_all + # A non-zero value for +delay+ will ensure that the flush + # is propogated slowly through your memcached server farm. + # The Nth server will be flushed N*delay seconds from now, + # asynchronously so this method returns quickly. + # This prevents a huge database spike due to a total + # flush all at once. + + def flush_all(delay=0) raise MemCacheError, 'No active servers' unless active? raise MemCacheError, "Update of readonly cache" if @readonly begin + delay_time = 0 @servers.each do |server| with_socket_management(server) do |socket| - socket.write "flush_all\r\n" + logger.debug { "flush_all #{delay_time} on #{server}" } if logger + if delay == 0 # older versions of memcached will fail silently otherwise + socket.write "flush_all#{noreply}\r\n" + else + socket.write "flush_all #{delay_time}#{noreply}\r\n" + end + break nil if @no_reply result = socket.gets raise_on_error_response! result result end + delay_time += delay end rescue IndexError => err handle_error nil, err @@ -466,10 +643,14 @@ class MemCache # requested. def make_cache_key(key) + if @autofix_keys and (key =~ /\s/ or (key.length + (namespace.nil? ? 0 : namespace.length)) > 250) + key = "#{Digest::SHA1.hexdigest(key)}-autofixed" + end + if namespace.nil? then key else - "#{@namespace}:#{key}" + "#{@namespace}#{@namespace_separator}#{key}" end end @@ -500,7 +681,7 @@ class MemCache break unless failover hkey = hash_for "#{try}#{key}" end - + raise MemCacheError, "No servers available" end @@ -510,7 +691,8 @@ class MemCache def cache_decr(server, cache_key, amount) with_socket_management(server) do |socket| - socket.write "decr #{cache_key} #{amount}\r\n" + socket.write "decr #{cache_key} #{amount}#{noreply}\r\n" + break nil if @no_reply text = socket.gets raise_on_error_response! text return nil if text == "NOT_FOUND\r\n" @@ -546,6 +728,38 @@ class MemCache end end + def gets(key, raw = false) + with_server(key) do |server, cache_key| + logger.debug { "gets #{key} from #{server.inspect}" } if logger + result = with_socket_management(server) do |socket| + socket.write "gets #{cache_key}\r\n" + keyline = socket.gets # "VALUE <key> <flags> <bytes> <cas token>\r\n" + + if keyline.nil? then + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + raise_on_error_response! keyline + return nil if keyline == "END\r\n" + + unless keyline =~ /(\d+) (\w+)\r/ then + server.close + raise MemCacheError, "unexpected response #{keyline.inspect}" + end + value = socket.read $1.to_i + socket.read 2 # "\r\n" + socket.gets # "END\r\n" + [value, $2] + end + result[0] = Marshal.load result[0] unless raw + result + end + rescue TypeError => err + handle_error nil, err + end + + ## # Fetches +cache_keys+ from +server+ using a multi-get. @@ -579,7 +793,8 @@ class MemCache def cache_incr(server, cache_key, amount) with_socket_management(server) do |socket| - socket.write "incr #{cache_key} #{amount}\r\n" + socket.write "incr #{cache_key} #{amount}#{noreply}\r\n" + break nil if @no_reply text = socket.gets raise_on_error_response! text return nil if text == "NOT_FOUND\r\n" @@ -617,7 +832,7 @@ class MemCache block.call(socket) - rescue SocketError => err + rescue SocketError, Errno::EAGAIN, Timeout::Error => err logger.warn { "Socket failure: #{err.message}" } if logger server.mark_dead(err) handle_error(server, err) @@ -659,6 +874,10 @@ class MemCache raise new_error end + def noreply + @no_reply ? ' noreply' : '' + end + ## # Performs setup for making a request with +key+ from memcached. Returns # the server to fetch the key from and the complete key to use. @@ -713,13 +932,6 @@ class MemCache class Server ## - # The amount of time to wait to establish a connection with a memcached - # server. If a connection cannot be established within this time limit, - # the server will be marked as down. - - CONNECT_TIMEOUT = 0.25 - - ## # The amount of time to wait before attempting to re-establish a # connection with a server that is marked dead. @@ -802,14 +1014,11 @@ class MemCache # Attempt to connect if not already connected. begin - @sock = @timeout ? TCPTimeoutSocket.new(@host, @port, @timeout) : TCPSocket.new(@host, @port) - - if Socket.constants.include? 'TCP_NODELAY' then - @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 - end + @sock = connect_to(@host, @port, @timeout) + @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 @retry = nil @status = 'CONNECTED' - rescue SocketError, SystemCallError, IOError, Timeout::Error => err + rescue SocketError, SystemCallError, IOError => err logger.warn { "Unable to open socket: #{err.class.name}, #{err.message}" } if logger mark_dead err end @@ -817,6 +1026,12 @@ class MemCache return @sock end + def connect_to(host, port, timeout=nil) + io = MemCache::BufferedIO.new(TCPSocket.new(host, port)) + io.read_timeout = timeout + io + end + ## # Close the connection to the memcached server targeted by this # object. The server is not considered dead. @@ -848,51 +1063,33 @@ class MemCache class MemCacheError < RuntimeError; end -end - -# TCPSocket facade class which implements timeouts. -class TCPTimeoutSocket - - def initialize(host, port, timeout) - Timeout::timeout(MemCache::Server::CONNECT_TIMEOUT, SocketError) do - @sock = TCPSocket.new(host, port) - @len = timeout - end - end - - def write(*args) - Timeout::timeout(@len, SocketError) do - @sock.write(*args) + class BufferedIO < Net::BufferedIO # :nodoc: + BUFSIZE = 1024 * 16 + + if RUBY_VERSION < '1.9.1' + def rbuf_fill + begin + @rbuf << @io.read_nonblock(BUFSIZE) + rescue Errno::EWOULDBLOCK + retry unless @read_timeout + if IO.select([@io], nil, nil, @read_timeout) + retry + else + raise Timeout::Error, 'IO timeout' + end + end + end end - end - def gets(*args) - Timeout::timeout(@len, SocketError) do - @sock.gets(*args) + def setsockopt(*args) + @io.setsockopt(*args) end - end - def read(*args) - Timeout::timeout(@len, SocketError) do - @sock.read(*args) + def gets + readuntil("\n") end end - def _socket - @sock - end - - def method_missing(meth, *args) - @sock.__send__(meth, *args) - end - - def closed? - @sock.closed? - end - - def close - @sock.close - end end module Continuum @@ -932,4 +1129,5 @@ module Continuum "<#{value}, #{server.host}:#{server.port}>" end end + end diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo.rb index c8bdbeec5d..c8bdbeec5d 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/data_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone.rb index 5eccbdf0db..5eccbdf0db 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/data_timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/data_timezone_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone_info.rb index a45d94554b..a45d94554b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/data_timezone_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone_info.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Algiers.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Algiers.rb index 8c5f25577f..8c5f25577f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Algiers.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Algiers.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Cairo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Cairo.rb index 6e6daf3522..6e6daf3522 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Cairo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Cairo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Casablanca.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Casablanca.rb index 18d73c93a0..18d73c93a0 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Casablanca.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Casablanca.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Harare.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Harare.rb index 070c95ae0f..070c95ae0f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Harare.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Harare.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Johannesburg.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Johannesburg.rb index f0af0d8e33..f0af0d8e33 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Johannesburg.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Johannesburg.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Monrovia.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Monrovia.rb index 40e711fa44..40e711fa44 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Monrovia.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Monrovia.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Nairobi.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Nairobi.rb index 7b0a2f43be..7b0a2f43be 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Africa/Nairobi.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Nairobi.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Argentina/Buenos_Aires.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/Buenos_Aires.rb index 8f4dd31dbb..8f4dd31dbb 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/Buenos_Aires.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Argentina/San_Juan.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/San_Juan.rb index ba8be4705f..ba8be4705f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Argentina/San_Juan.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/San_Juan.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Bogota.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Bogota.rb index ef96435c6a..ef96435c6a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Bogota.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Bogota.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Caracas.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Caracas.rb index 27392a540a..27392a540a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Caracas.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Caracas.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Chicago.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chicago.rb index 0996857cf0..0996857cf0 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Chicago.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chicago.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Chihuahua.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chihuahua.rb index 1710b57c79..1710b57c79 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Chihuahua.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chihuahua.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Denver.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Denver.rb index 1c1efb5ff3..1c1efb5ff3 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Denver.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Denver.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Godthab.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Godthab.rb index 1e05518b0d..1e05518b0d 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Godthab.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Godthab.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Guatemala.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Guatemala.rb index a2bf73401c..a2bf73401c 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Guatemala.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Guatemala.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Halifax.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Halifax.rb index d25ae775b3..d25ae775b3 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Halifax.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Halifax.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Indiana/Indianapolis.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Indiana/Indianapolis.rb index f1430f6c24..f1430f6c24 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Indiana/Indianapolis.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Indiana/Indianapolis.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Juneau.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Juneau.rb index f646f3f54a..f646f3f54a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Juneau.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Juneau.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/La_Paz.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/La_Paz.rb index 45c907899f..45c907899f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/La_Paz.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/La_Paz.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Lima.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Lima.rb index af68ac29f7..af68ac29f7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Lima.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Lima.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Los_Angeles.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Los_Angeles.rb index 16007fd675..16007fd675 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Los_Angeles.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Los_Angeles.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Mazatlan.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mazatlan.rb index ba9e6efcf1..ba9e6efcf1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Mazatlan.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mazatlan.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Mexico_City.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mexico_City.rb index 2347fce647..2347fce647 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Mexico_City.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mexico_City.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Monterrey.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Monterrey.rb index 5816a9eab1..5816a9eab1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Monterrey.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Monterrey.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/New_York.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/New_York.rb index 7d802bd2de..7d802bd2de 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/New_York.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/New_York.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Phoenix.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Phoenix.rb index b514e0c0f9..b514e0c0f9 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Phoenix.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Phoenix.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Regina.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Regina.rb index ebdb68814a..ebdb68814a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Regina.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Regina.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Santiago.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Santiago.rb index 0287c9ebc4..0287c9ebc4 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Santiago.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Santiago.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Sao_Paulo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Sao_Paulo.rb index 0524f81c04..0524f81c04 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Sao_Paulo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Sao_Paulo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/St_Johns.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/St_Johns.rb index e4a3599d35..e4a3599d35 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/St_Johns.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/St_Johns.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Tijuana.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Tijuana.rb index 423059da46..423059da46 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/America/Tijuana.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Tijuana.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Almaty.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Almaty.rb index 9ee18970f1..9ee18970f1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Almaty.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Almaty.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Baghdad.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baghdad.rb index 774dca1587..774dca1587 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Baghdad.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baghdad.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Baku.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baku.rb index e86340ebfa..e86340ebfa 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Baku.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baku.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Bangkok.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Bangkok.rb index 139194e5e5..139194e5e5 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Bangkok.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Bangkok.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Chongqing.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Chongqing.rb index 8c94b4ba86..8c94b4ba86 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Chongqing.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Chongqing.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Colombo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Colombo.rb index f6531fa819..f6531fa819 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Colombo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Colombo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Dhaka.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Dhaka.rb index ccd0265503..ccd0265503 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Dhaka.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Dhaka.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Hong_Kong.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Hong_Kong.rb index f1edd75ac8..f1edd75ac8 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Hong_Kong.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Hong_Kong.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Irkutsk.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Irkutsk.rb index 2d47d9580b..2d47d9580b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Irkutsk.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Irkutsk.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Jakarta.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jakarta.rb index cc58fa173b..cc58fa173b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Jakarta.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jakarta.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Jerusalem.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jerusalem.rb index 9b737b899e..9b737b899e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Jerusalem.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jerusalem.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kabul.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kabul.rb index 669c09790a..669c09790a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kabul.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kabul.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kamchatka.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kamchatka.rb index 2f1690b3a9..2f1690b3a9 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kamchatka.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kamchatka.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Karachi.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Karachi.rb index 4f187b4ad4..4f187b4ad4 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Karachi.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Karachi.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kathmandu.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kathmandu.rb index 37b241612e..37b241612e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kathmandu.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kathmandu.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kolkata.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kolkata.rb index 1b6ffbd59d..1b6ffbd59d 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kolkata.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kolkata.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Krasnoyarsk.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Krasnoyarsk.rb index d6c503c155..d6c503c155 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Krasnoyarsk.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Krasnoyarsk.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kuala_Lumpur.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuala_Lumpur.rb index 77a0c206fa..77a0c206fa 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kuala_Lumpur.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuala_Lumpur.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kuwait.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuwait.rb index 5bd5283197..5bd5283197 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Kuwait.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuwait.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Magadan.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Magadan.rb index 302093693e..302093693e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Magadan.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Magadan.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Muscat.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Muscat.rb index 604f651dfa..604f651dfa 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Muscat.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Muscat.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Novosibirsk.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Novosibirsk.rb index a4e7796e75..a4e7796e75 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Novosibirsk.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Novosibirsk.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Rangoon.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Rangoon.rb index 759b82d77a..759b82d77a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Rangoon.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Rangoon.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Riyadh.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Riyadh.rb index 7add410620..7add410620 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Riyadh.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Riyadh.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Seoul.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Seoul.rb index 795d2a75df..795d2a75df 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Seoul.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Seoul.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Shanghai.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Shanghai.rb index 34b13d59ae..34b13d59ae 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Shanghai.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Shanghai.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Singapore.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Singapore.rb index b323a78f74..b323a78f74 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Singapore.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Singapore.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Taipei.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Taipei.rb index 3ba12108fb..3ba12108fb 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Taipei.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Taipei.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tashkent.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tashkent.rb index c205c7934d..c205c7934d 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tashkent.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tashkent.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tbilisi.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tbilisi.rb index 15792a5651..15792a5651 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tbilisi.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tbilisi.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tehran.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tehran.rb index d8df964a46..d8df964a46 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tehran.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tehran.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tokyo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tokyo.rb index 51c9e16421..51c9e16421 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Tokyo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tokyo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Ulaanbaatar.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Ulaanbaatar.rb index 2854f5c5fd..2854f5c5fd 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Ulaanbaatar.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Ulaanbaatar.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Urumqi.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Urumqi.rb index d793ff1341..d793ff1341 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Urumqi.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Urumqi.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Vladivostok.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Vladivostok.rb index bd9e3d60ec..bd9e3d60ec 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Vladivostok.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Vladivostok.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yakutsk.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yakutsk.rb index 56435a788f..56435a788f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yakutsk.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yakutsk.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yekaterinburg.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yekaterinburg.rb index 8ef8df4a41..8ef8df4a41 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yekaterinburg.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yekaterinburg.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yerevan.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yerevan.rb index e7f160861f..e7f160861f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Asia/Yerevan.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yerevan.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/Azores.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Azores.rb index 1bd16a75ac..1bd16a75ac 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/Azores.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Azores.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/Cape_Verde.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Cape_Verde.rb index 61c8c15043..61c8c15043 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/Cape_Verde.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Cape_Verde.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/South_Georgia.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/South_Georgia.rb index 6a4cbafb9f..6a4cbafb9f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Atlantic/South_Georgia.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/South_Georgia.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Adelaide.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Adelaide.rb index c5d561cc1e..c5d561cc1e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Adelaide.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Adelaide.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Brisbane.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Brisbane.rb index dd85ddae94..dd85ddae94 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Brisbane.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Brisbane.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Darwin.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Darwin.rb index 17de88124d..17de88124d 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Darwin.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Darwin.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Hobart.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Hobart.rb index 11384b9840..11384b9840 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Hobart.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Hobart.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Melbourne.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Melbourne.rb index c1304488ea..c1304488ea 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Melbourne.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Melbourne.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Perth.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Perth.rb index d9e66f14a8..d9e66f14a8 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Perth.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Perth.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Sydney.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Sydney.rb index 9062bd7c3c..9062bd7c3c 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Australia/Sydney.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Sydney.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Etc/UTC.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Etc/UTC.rb index 28b2c6a04c..28b2c6a04c 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Etc/UTC.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Etc/UTC.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Amsterdam.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Amsterdam.rb index 2d0c95c4bc..2d0c95c4bc 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Amsterdam.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Amsterdam.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Athens.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Athens.rb index 4e21e535ca..4e21e535ca 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Athens.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Athens.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Belgrade.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Belgrade.rb index 4dbd893d75..4dbd893d75 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Belgrade.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Belgrade.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Berlin.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Berlin.rb index 721054236c..721054236c 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Berlin.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Berlin.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Bratislava.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bratislava.rb index 7a731a0b6a..7a731a0b6a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Bratislava.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bratislava.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Brussels.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Brussels.rb index 6b0a242944..6b0a242944 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Brussels.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Brussels.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Bucharest.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bucharest.rb index 521c3c932e..521c3c932e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Bucharest.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bucharest.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Budapest.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Budapest.rb index 1f3a9738b7..1f3a9738b7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Budapest.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Budapest.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Copenhagen.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Copenhagen.rb index 47cbaf14a7..47cbaf14a7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Copenhagen.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Copenhagen.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Dublin.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Dublin.rb index 0560bb5436..0560bb5436 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Dublin.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Dublin.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Helsinki.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Helsinki.rb index 13a806bcc7..13a806bcc7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Helsinki.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Helsinki.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Istanbul.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Istanbul.rb index 8306c47536..8306c47536 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Istanbul.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Istanbul.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Kiev.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Kiev.rb index 513d3308be..513d3308be 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Kiev.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Kiev.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Lisbon.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Lisbon.rb index 1c6d2a3d30..1c6d2a3d30 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Lisbon.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Lisbon.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Ljubljana.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Ljubljana.rb index a9828e6ef8..a9828e6ef8 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Ljubljana.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Ljubljana.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/London.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/London.rb index 64ce41e900..64ce41e900 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/London.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/London.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Madrid.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Madrid.rb index 1fb568239a..1fb568239a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Madrid.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Madrid.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Minsk.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Minsk.rb index fa15816cc8..fa15816cc8 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Minsk.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Minsk.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Moscow.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Moscow.rb index ef269b675b..ef269b675b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Moscow.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Moscow.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Paris.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Paris.rb index e3236c0ba1..e3236c0ba1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Paris.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Paris.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Prague.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Prague.rb index bcabee96c1..bcabee96c1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Prague.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Prague.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Riga.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Riga.rb index 784837f758..784837f758 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Riga.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Riga.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Rome.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Rome.rb index aa7b43d9d2..aa7b43d9d2 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Rome.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Rome.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Sarajevo.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sarajevo.rb index 068c5fe6ad..068c5fe6ad 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Sarajevo.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sarajevo.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Skopje.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Skopje.rb index 10b71f285e..10b71f285e 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Skopje.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Skopje.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Sofia.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sofia.rb index 38a70eceb9..38a70eceb9 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Sofia.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sofia.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Stockholm.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Stockholm.rb index 43db70fa61..43db70fa61 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Stockholm.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Stockholm.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Tallinn.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Tallinn.rb index de5a8569f3..de5a8569f3 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Tallinn.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Tallinn.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Vienna.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vienna.rb index 990aabab66..990aabab66 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Vienna.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vienna.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Vilnius.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vilnius.rb index d89d095a75..d89d095a75 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Vilnius.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vilnius.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Warsaw.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Warsaw.rb index 7fa51c2691..7fa51c2691 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Warsaw.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Warsaw.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Zagreb.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Zagreb.rb index ecdd903d28..ecdd903d28 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Europe/Zagreb.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Zagreb.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Auckland.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Auckland.rb index a524fd6b6b..a524fd6b6b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Auckland.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Auckland.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Fiji.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Fiji.rb index 5fe9bbd9a6..5fe9bbd9a6 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Fiji.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Fiji.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Guam.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Guam.rb index d4c1a0a682..d4c1a0a682 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Guam.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Guam.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Honolulu.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Honolulu.rb index 204b226537..204b226537 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Honolulu.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Honolulu.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Majuro.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Majuro.rb index 32adad92c1..32adad92c1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Majuro.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Majuro.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Midway.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Midway.rb index 97784fcc10..97784fcc10 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Midway.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Midway.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Noumea.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Noumea.rb index 70173db8ab..70173db8ab 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Noumea.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Noumea.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Pago_Pago.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Pago_Pago.rb index c8fcd7d527..c8fcd7d527 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Pago_Pago.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Pago_Pago.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Port_Moresby.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Port_Moresby.rb index f06cf6d54f..f06cf6d54f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Port_Moresby.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Port_Moresby.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Tongatapu.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Tongatapu.rb index 7578d92f38..7578d92f38 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/definitions/Pacific/Tongatapu.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Tongatapu.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/info_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/info_timezone.rb index 001303c594..001303c594 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/info_timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/info_timezone.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/linked_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone.rb index f8ec4fca87..f8ec4fca87 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/linked_timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/linked_timezone_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone_info.rb index 8197ff3e81..8197ff3e81 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/linked_timezone_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone_info.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/offset_rationals.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/offset_rationals.rb index b1f10b2b63..b1f10b2b63 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/offset_rationals.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/offset_rationals.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/ruby_core_support.rb index b65eeaaae7..b65eeaaae7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/ruby_core_support.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/time_or_datetime.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/time_or_datetime.rb index 264517f3ee..264517f3ee 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/time_or_datetime.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/time_or_datetime.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone.rb index ef4ecd8ae1..ef4ecd8ae1 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_definition.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_definition.rb index 39ca8bfa53..39ca8bfa53 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_definition.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_definition.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_info.rb index 68e38c35fb..68e38c35fb 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_info.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_offset_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_offset_info.rb index 6a0bbca46f..6a0bbca46f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_offset_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_offset_info.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_period.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_period.rb index 00888fcfdc..00888fcfdc 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_period.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_period.rb diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_transition_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_transition_info.rb index 6b0669cc4a..6b0669cc4a 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/timezone_transition_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_transition_info.rb diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 847ab0152b..17bacd8441 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -18,7 +18,7 @@ module ActiveSupport {} else data.ungetc(char) - doc = Nokogiri::XML(data) + doc = Nokogiri::XML(data) { |cfg| cfg.noblanks } raise doc.errors.first if doc.errors.length > 0 doc.to_hash end @@ -39,33 +39,25 @@ module ActiveSupport # hash:: # Hash to merge the converted element into. def to_hash(hash = {}) - hash[name] ||= attributes_as_hash + attributes = attributes_as_hash + if hash[name] + hash[name] = [hash[name]].flatten + hash[name] << attributes + else + hash[name] ||= attributes + end - walker = lambda { |memo, parent, child, callback| - next if child.blank? && 'file' != parent['type'] + children.each { |child| + next if child.blank? && 'file' != self['type'] - if child.text? - (memo[CONTENT_ROOT] ||= '') << child.content + if child.text? || child.cdata? + (attributes[CONTENT_ROOT] ||= '') << child.content next end - name = child.name - - child_hash = child.attributes_as_hash - if memo[name] - memo[name] = [memo[name]].flatten - memo[name] << child_hash - else - memo[name] = child_hash - end - - # Recursively walk children - child.children.each { |c| - callback.call(child_hash, child, c, callback) - } + child.to_hash attributes } - children.each { |c| walker.call(hash[name], self, c, walker) } hash end diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index aaf4bb6bfd..3db48ce5a3 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/object/blank' # = XmlMini ReXML implementation @@ -24,7 +25,7 @@ module ActiveSupport {} else data.ungetc(char) - require 'rexml/document' unless defined?(REXML::Document) + silence_warnings { require 'rexml/document' } unless defined?(REXML::Document) doc = REXML::Document.new(data) merge_element!({}, doc.root) end diff --git a/activesupport/lib/activesupport.rb b/activesupport/lib/activesupport.rb deleted file mode 100644 index 69f36f7934..0000000000 --- a/activesupport/lib/activesupport.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_support' diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 61914231ef..af9656615c 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -1,13 +1,23 @@ ORIG_ARGV = ARGV.dup -require 'rubygems' require 'test/unit' +begin + require 'mocha' +rescue LoadError + $stderr.puts 'Loading rubygems' + require 'rubygems' + require 'mocha' +end + ENV['NO_RELOAD'] = '1' $:.unshift "#{File.dirname(__FILE__)}/../lib" require 'active_support' require 'active_support/test_case' +# Include shims until we get off 1.8.6 +require 'active_support/ruby/shim' + def uses_memcached(test_name) require 'memcache' begin diff --git a/activesupport/test/core_ext/boolean_ext_test.rb b/activesupport/test/core_ext/boolean_ext_test.rb index 751f703745..9439716efb 100644 --- a/activesupport/test/core_ext/boolean_ext_test.rb +++ b/activesupport/test/core_ext/boolean_ext_test.rb @@ -1,3 +1,6 @@ +require 'abstract_unit' +require 'active_support/core_ext/boolean/conversions' + class BooleanExtAccessTests < Test::Unit::TestCase def test_to_param_on_true assert_equal true, true.to_param @@ -6,4 +9,4 @@ class BooleanExtAccessTests < Test::Unit::TestCase def test_to_param_on_false assert_equal false, false.to_param end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 8a7bae5fc6..18422d68bc 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -251,6 +251,12 @@ class DateExtCalculationsTest < Test::Unit::TestCase Time.zone_default = nil end + def test_date_advance_should_not_change_passed_options_hash + options = { :years => 3, :months => 11, :days => 2 } + Date.new(2005,2,28).advance(options) + assert_equal({ :years => 3, :months => 11, :days => 2 }, options) + 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/nil_ext_test.rb b/activesupport/test/core_ext/nil_ext_test.rb index 945d3af239..1062676d65 100644 --- a/activesupport/test/core_ext/nil_ext_test.rb +++ b/activesupport/test/core_ext/nil_ext_test.rb @@ -1,5 +1,8 @@ +require 'abstract_unit' +require 'active_support/core_ext/nil/conversions' + class NilExtAccessTests < Test::Unit::TestCase def test_to_param assert_nil nil.to_param end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/object_ext_test.rb b/activesupport/test/core_ext/object_ext_test.rb index 72e3bffa4c..484eecaab6 100644 --- a/activesupport/test/core_ext/object_ext_test.rb +++ b/activesupport/test/core_ext/object_ext_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/object/conversions' class ObjectExtTest < Test::Unit::TestCase def test_tap_yields_and_returns_self diff --git a/activesupport/test/core_ext/regexp_ext_test.rb b/activesupport/test/core_ext/regexp_ext_test.rb index e2d9140bca..cc3f07d5c5 100644 --- a/activesupport/test/core_ext/regexp_ext_test.rb +++ b/activesupport/test/core_ext/regexp_ext_test.rb @@ -1,3 +1,6 @@ +require 'abstract_unit' +require 'active_support/core_ext/regexp' + class RegexpExtAccessTests < Test::Unit::TestCase def test_number_of_captures assert_equal 0, //.number_of_captures @@ -23,4 +26,4 @@ class RegexpExtAccessTests < Test::Unit::TestCase assert_equal "foo", Regexp.unoptionalize("(?:foo)?") assert_equal "", Regexp.unoptionalize("") end -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 1005a7e7ad..db9073e298 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -316,8 +316,12 @@ class TestGetTextString < Test::Unit::TestCase end def test_no_placeholder - assert_equal("aaa", "aaa" % {:num => 1}) - assert_equal("bbb", "bbb" % [1]) + # Causes a "too many arguments for format string" warning + # on 1.8.7 and 1.9 but we still want to make sure the behavior works + silence_warnings do + assert_equal("aaa", "aaa" % {:num => 1}) + assert_equal("bbb", "bbb" % [1]) + end end def test_sprintf_ruby19_style diff --git a/activesupport/test/flush_cache_on_private_memoization_test.rb b/activesupport/test/flush_cache_on_private_memoization_test.rb index ddbd05b0e0..1cd313ec81 100644 --- a/activesupport/test/flush_cache_on_private_memoization_test.rb +++ b/activesupport/test/flush_cache_on_private_memoization_test.rb @@ -1,5 +1,4 @@ -require 'rubygems' -require 'activesupport' +require 'active_support' require 'test/unit' class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase @@ -41,4 +40,4 @@ class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase end end -end
\ No newline at end of file +end diff --git a/activesupport/test/isolation_test.rb b/activesupport/test/isolation_test.rb index 7aecdb8009..20e11df1dd 100644 --- a/activesupport/test/isolation_test.rb +++ b/activesupport/test/isolation_test.rb @@ -1,158 +1,162 @@ require 'abstract_unit' +require 'rbconfig' -# Does awesome -if defined?(MiniTest) - $stderr.puts "Umm, MiniTest not supported yet, mmkay?" -elsif ENV['CHILD'] - class ChildIsolationTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation +if defined?(MiniTest) || defined?(Test::Unit::TestResultFailureSupport) + $stderr.puts "Isolation tests can test test-unit 1 only" - def self.setup - File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "a") do |f| - f.puts "hello" +else + # Does awesome + if ENV['CHILD'] + class ChildIsolationTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def self.setup + File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "a") do |f| + f.puts "hello" + end end - end - def setup - @instance = "HELLO" - end + def setup + @instance = "HELLO" + end - def teardown - raise if @boom - end + def teardown + raise if @boom + end - test "runs the test" do - assert true - end + test "runs the test" do + assert true + end - test "captures errors" do - raise - end + test "captures errors" do + raise + end - test "captures failures" do - assert false - end + test "captures failures" do + assert false + end - test "first runs in isolation" do - assert_nil $x - $x = 1 - end + test "first runs in isolation" do + assert_nil $x + $x = 1 + end - test "second runs in isolation" do - assert_nil $x - $x = 2 - end + test "second runs in isolation" do + assert_nil $x + $x = 2 + end - test "runs with slow tests" do - sleep 0.3 - assert true - sleep 0.2 - end + test "runs with slow tests" do + sleep 0.3 + assert true + sleep 0.2 + end - test "runs setup" do - assert "HELLO", @instance - end + test "runs setup" do + assert "HELLO", @instance + end - test "runs teardown" do - @boom = true - end + test "runs teardown" do + @boom = true + end - test "resets requires one" do - assert !defined?(OmgOmg) - assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/omgomg/).size - require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "omgomg")) - end + test "resets requires one" do + assert !defined?(OmgOmg) + assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/omgomg/).size + require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "omgomg")) + end - test "resets requires two" do - assert !defined?(OmgOmg) - assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/omgomg/).size - require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "omgomg")) + test "resets requires two" do + assert !defined?(OmgOmg) + assert_equal 0, $LOADED_FEATURES.grep(/fixtures\/omgomg/).size + require File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "omgomg")) + end end - end -else - class ParentIsolationTest < ActiveSupport::TestCase - - File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "w") {} - - ENV["CHILD"] = "1" - OUTPUT = `#{Gem.ruby} -I#{File.dirname(__FILE__)} "#{File.expand_path(__FILE__)}" -v` - ENV.delete("CHILD") - - def setup - # Extract the results - @results = {} - OUTPUT[/Started\n\s*(.*)\s*\nFinished/mi, 1].split(/\s*\n\s*/).each do |result| - result =~ %r'^(\w+)\(\w+\):\s*(\.|E|F)$' - @results[$1] = { 'E' => :error, '.' => :success, 'F' => :failure }[$2] + else + class ParentIsolationTest < ActiveSupport::TestCase + + File.open(File.join(File.dirname(__FILE__), "fixtures", "isolation_test"), "w") {} + + ENV["CHILD"] = "1" + OUTPUT = `#{RbConfig::CONFIG["bindir"]}/#{RbConfig::CONFIG["ruby_install_name"]} -I#{File.dirname(__FILE__)} "#{File.expand_path(__FILE__)}" -v` + ENV.delete("CHILD") + + def setup + # Extract the results + @results = {} + OUTPUT[/Started\n\s*(.*)\s*\nFinished/mi, 1].to_s.split(/\s*\n\s*/).each do |result| + result =~ %r'^(\w+)\(\w+\):\s*(\.|E|F)$' + @results[$1] = { 'E' => :error, '.' => :success, 'F' => :failure }[$2] + end + + # Extract the backtraces + @backtraces = {} + OUTPUT.scan(/^\s*\d+\).*?\n\n/m).each do |backtrace| + # \n 1) Error:\ntest_captures_errors(ChildIsolationTest): + backtrace =~ %r'\s*\d+\)\s*(Error|Failure):\n(\w+)'i + @backtraces[$2] = { :type => $1, :output => backtrace } + end end - # Extract the backtraces - @backtraces = {} - OUTPUT.scan(/^\s*\d+\).*?\n\n/m).each do |backtrace| - # \n 1) Error:\ntest_captures_errors(ChildIsolationTest): - backtrace =~ %r'\s*\d+\)\s*(Error|Failure):\n(\w+)'i - @backtraces[$2] = { :type => $1, :output => backtrace } + def assert_failing(name) + assert_equal :failure, @results[name.to_s], "Test #{name} did not fail" end - end - def assert_failing(name) - assert_equal :failure, @results[name.to_s], "Test #{name} did not fail" - end + def assert_passing(name) + assert_equal :success, @results[name.to_s], "Test #{name} did not pass" + end - def assert_passing(name) - assert_equal :success, @results[name.to_s], "Test #{name} did not pass" - end + def assert_erroring(name) + assert_equal :error, @results[name.to_s], "Test #{name} did not error" + end - def assert_erroring(name) - assert_equal :error, @results[name.to_s], "Test #{name} did not error" - end + test "has all tests" do + assert_equal 10, @results.length + end - test "has all tests" do - assert_equal 10, @results.length - end + test "passing tests are still reported" do + assert_passing :test_runs_the_test + assert_passing :test_runs_with_slow_tests + end - test "passing tests are still reported" do - assert_passing :test_runs_the_test - assert_passing :test_runs_with_slow_tests - end + test "resets global variables" do + assert_passing :test_first_runs_in_isolation + assert_passing :test_second_runs_in_isolation + end - test "resets global variables" do - assert_passing :test_first_runs_in_isolation - assert_passing :test_second_runs_in_isolation - end + test "resets requires" do + assert_passing :test_resets_requires_one + assert_passing :test_resets_requires_two + end - test "resets requires" do - assert_passing :test_resets_requires_one - assert_passing :test_resets_requires_two - end + test "erroring tests are still reported" do + assert_erroring :test_captures_errors + end - test "erroring tests are still reported" do - assert_erroring :test_captures_errors - end + test "runs setup and teardown methods" do + assert_passing :test_runs_setup + assert_erroring :test_runs_teardown + end - test "runs setup and teardown methods" do - assert_passing :test_runs_setup - assert_erroring :test_runs_teardown - end + test "correct tests fail" do + assert_failing :test_captures_failures + end - test "correct tests fail" do - assert_failing :test_captures_failures - end + test "backtrace is printed for errors" do + assert_equal 'Error', @backtraces["test_captures_errors"][:type] + assert_match %r{isolation_test.rb:\d+:in `test_captures_errors'}, @backtraces["test_captures_errors"][:output] + end - test "backtrace is printed for errors" do - assert_equal 'Error', @backtraces["test_captures_errors"][:type] - assert_match %r{isolation_test.rb:\d+:in `test_captures_errors'}, @backtraces["test_captures_errors"][:output] - end + test "backtrace is printed for failures" do + assert_equal 'Failure', @backtraces["test_captures_failures"][:type] + assert_match %r{isolation_test.rb:\d+:in `test_captures_failures'}, @backtraces["test_captures_failures"][:output] + end - test "backtrace is printed for failures" do - assert_equal 'Failure', @backtraces["test_captures_failures"][:type] - assert_match %r{isolation_test.rb:\d+:in `test_captures_failures'}, @backtraces["test_captures_failures"][:output] - end + test "self.setup is run only once" do + text = File.read(File.join(File.dirname(__FILE__), "fixtures", "isolation_test")) + assert_equal "hello\n", text + end - test "self.setup is run only once" do - text = File.read(File.join(File.dirname(__FILE__), "fixtures", "isolation_test")) - assert_equal "hello\n", text end - end end diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 05e420ae36..7b5a4d0416 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -47,13 +47,7 @@ class TestJSONDecoding < ActiveSupport::TestCase ActiveSupport::JSON.backend backends = %w(Yaml) - begin - gem 'json', '>= 1.1' - require 'json' - backends << "JSONGem" - rescue Gem::LoadError - # Skip JSON gem tests - end + backends << "JSONGem" if defined?(::JSON) backends.each do |backend| TESTS.each do |json, expected| @@ -81,7 +75,7 @@ class TestJSONDecoding < ActiveSupport::TestCase end def test_failed_json_decoding - assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) } + assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) } end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 214e243aa5..195e3eaa42 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -4,12 +4,13 @@ class MemoizableTest < ActiveSupport::TestCase class Person extend ActiveSupport::Memoizable - attr_reader :name_calls, :age_calls, :is_developer_calls + attr_reader :name_calls, :age_calls, :is_developer_calls, :name_query_calls def initialize @name_calls = 0 @age_calls = 0 @is_developer_calls = 0 + @name_query_calls = 0 end def name @@ -18,6 +19,7 @@ class MemoizableTest < ActiveSupport::TestCase end def name? + @name_query_calls += 1 true end memoize :name? @@ -116,6 +118,13 @@ class MemoizableTest < ActiveSupport::TestCase end end + def test_memoization_flush_with_punctuation + assert_equal true, @person.name? + @person.flush_cache(:name?) + 3.times { assert_equal true, @person.name? } + assert_equal 2, @person.name_query_calls + end + def test_memoization_with_nil_value assert_equal nil, @person.age assert_equal 1, @person.age_calls @@ -124,16 +133,6 @@ class MemoizableTest < ActiveSupport::TestCase assert_equal 1, @person.age_calls end - def test_memorized_results_are_immutable - # This is purely a performance enhancement that we can revisit once the rest of - # the code is in place. Ideally, we'd be able to do memoization in a freeze-friendly - # way without amc hacks - pending do - assert_equal "Josh", @person.name - assert_raise(ActiveSupport::FrozenObjectError) { @person.name.gsub!("Josh", "Gosh") } - end - end - def test_reloadable counter = @calculator.counter assert_equal 1, @calculator.counter diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 57c4ce841e..4f8837ba4e 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -1,25 +1,34 @@ require 'abstract_unit' +begin + require 'openssl' + OpenSSL::Digest::SHA1 +rescue LoadError, NameError + $stderr.puts "Skipping MessageVerifier test: broken OpenSSL install" +else + class MessageVerifierTest < Test::Unit::TestCase def setup @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!") @data = {:some=>"data", :now=>Time.now} end - + def test_simple_round_tripping message = @verifier.generate(@data) assert_equal @data, @verifier.verify(message) end - + def test_tampered_data_raises data, hash = @verifier.generate(@data).split("--") assert_not_verified("#{data.reverse}--#{hash}") assert_not_verified("#{data}--#{hash.reverse}") end - + def assert_not_verified(message) assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do @verifier.verify(message) end end end + +end diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index ed37a1a0da..680936ded5 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -390,7 +390,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase assert_equal 'ちわ', @chars.slice(2..10) assert_equal '', @chars.slice(4..10) assert_equal 'に', @chars.slice(/に/u) - assert_equal 'にち', @chars.slice(/に\w/u) + assert_equal 'にち', @chars.slice(/に./u) assert_equal nil, @chars.slice(/unknown/u) assert_equal 'にち', @chars.slice(/(にち)/u, 1) assert_equal nil, @chars.slice(/(にち)/u, 2) diff --git a/activesupport/test/multibyte_utils_test.rb b/activesupport/test/multibyte_utils_test.rb new file mode 100644 index 0000000000..0a2f20d282 --- /dev/null +++ b/activesupport/test/multibyte_utils_test.rb @@ -0,0 +1,137 @@ +# encoding: utf-8 + +require 'abstract_unit' +require 'multibyte_test_helpers' + +class MultibyteUtilsTest < ActiveSupport::TestCase + include MultibyteTestHelpers + + test "valid_character returns an expression for the current encoding" do + with_encoding('None') do + assert_nil ActiveSupport::Multibyte.valid_character + end + with_encoding('UTF8') do + assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'], ActiveSupport::Multibyte.valid_character + end + with_encoding('SJIS') do + assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['Shift_JIS'], ActiveSupport::Multibyte.valid_character + end + end + + test "verify verifies ASCII strings are properly encoded" do + with_encoding('None') do + examples.each do |example| + assert ActiveSupport::Multibyte.verify(example) + end + end + end + + test "verify verifies UTF-8 strings are properly encoded" do + with_encoding('UTF8') do + assert ActiveSupport::Multibyte.verify(example('valid UTF-8')) + assert !ActiveSupport::Multibyte.verify(example('invalid UTF-8')) + end + end + + test "verify verifies Shift-JIS strings are properly encoded" do + with_encoding('SJIS') do + assert ActiveSupport::Multibyte.verify(example('valid Shift-JIS')) + assert !ActiveSupport::Multibyte.verify(example('invalid Shift-JIS')) + end + end + + test "verify! raises an exception when it finds an invalid character" do + with_encoding('UTF8') do + assert_raises(ActiveSupport::Multibyte::EncodingError) do + ActiveSupport::Multibyte.verify!(example('invalid UTF-8')) + end + end + end + + test "verify! doesn't raise an exception when the encoding is valid" do + with_encoding('UTF8') do + assert_nothing_raised do + ActiveSupport::Multibyte.verify!(example('valid UTF-8')) + end + end + end + + if RUBY_VERSION < '1.9' + test "clean leaves ASCII strings intact" do + with_encoding('None') do + [ + 'word', "\270\236\010\210\245" + ].each do |string| + assert_equal string, ActiveSupport::Multibyte.clean(string) + end + end + end + + test "clean cleans invalid characters from UTF-8 encoded strings" do + with_encoding('UTF8') do + cleaned_utf8 = [8].pack('C*') + assert_equal example('valid UTF-8'), ActiveSupport::Multibyte.clean(example('valid UTF-8')) + assert_equal cleaned_utf8, ActiveSupport::Multibyte.clean(example('invalid UTF-8')) + end + end + + test "clean cleans invalid characters from Shift-JIS encoded strings" do + with_encoding('SJIS') do + cleaned_sjis = [184, 0, 136, 165].pack('C*') + assert_equal example('valid Shift-JIS'), ActiveSupport::Multibyte.clean(example('valid Shift-JIS')) + assert_equal cleaned_sjis, ActiveSupport::Multibyte.clean(example('invalid Shift-JIS')) + end + end + else + test "clean is a no-op" do + with_encoding('UTF8') do + assert_equal example('invalid Shift-JIS'), ActiveSupport::Multibyte.clean(example('invalid Shift-JIS')) + end + end + end + + private + + STRINGS = { + 'valid ASCII' => [65, 83, 67, 73, 73].pack('C*'), + 'invalid ASCII' => [128].pack('C*'), + 'valid UTF-8' => [227, 129, 147, 227, 129, 171, 227, 129, 161, 227, 130, 143].pack('C*'), + 'invalid UTF-8' => [184, 158, 8, 136, 165].pack('C*'), + 'valid Shift-JIS' => [131, 122, 129, 91, 131, 128].pack('C*'), + 'invalid Shift-JIS' => [184, 158, 8, 0, 255, 136, 165].pack('C*') + } + + if Kernel.const_defined?(:Encoding) + def example(key) + STRINGS[key].force_encoding(Encoding.default_external) + end + + def examples + STRINGS.values.map { |s| s.force_encoding(Encoding.default_external) } + end + else + def example(key) + STRINGS[key] + end + + def examples + STRINGS.values + end + end + + if 'string'.respond_to?(:encoding) + KCODE_TO_ENCODING = Hash.new(Encoding::BINARY). + update('UTF8' => Encoding::UTF_8, 'SJIS' => Encoding::Shift_JIS) + + def with_encoding(enc) + before = Encoding.default_external + silence_warnings { Encoding.default_external = KCODE_TO_ENCODING[enc] } + + yield + + silence_warnings { Encoding.default_external = before } + end + else + alias with_encoding with_kcode + end +end diff --git a/activesupport/test/new_callback_inheritance_test.rb b/activesupport/test/new_callback_inheritance_test.rb index da0c19eaea..fe316ae3da 100644 --- a/activesupport/test/new_callback_inheritance_test.rb +++ b/activesupport/test/new_callback_inheritance_test.rb @@ -112,4 +112,4 @@ class InheritedCallbacksTest2 < Test::Unit::TestCase def test_crazy_mix_off assert_equal %w(before1 before2 update after2 after1), @update2.log end -end
\ No newline at end of file +end diff --git a/activesupport/test/new_callbacks_test.rb b/activesupport/test/new_callbacks_test.rb index 7e092b5f63..04db376fc6 100644 --- a/activesupport/test/new_callbacks_test.rb +++ b/activesupport/test/new_callbacks_test.rb @@ -180,6 +180,10 @@ module NewCallbacksTest end end + class CleanPerson < ConditionalPerson + reset_callbacks :save + end + class MySuper include ActiveSupport::NewCallbacks define_callbacks :save @@ -349,10 +353,18 @@ module NewCallbacksTest end end + class ResetCallbackTest < Test::Unit::TestCase + def test_save_conditional_person + person = CleanPerson.new + person.save + assert_equal [], person.history + end + end + class CallbackTerminator include ActiveSupport::NewCallbacks - define_callbacks :save, "result == :halt" + define_callbacks :save, :terminator => "result == :halt" set_callback :save, :before, :first set_callback :save, :before, :second @@ -400,7 +412,11 @@ module NewCallbacksTest def before(caller) caller.record << "before" end - + + def before_save(caller) + caller.record << "before save" + end + def around(caller) caller.record << "around before" yield @@ -410,15 +426,15 @@ module NewCallbacksTest class UsingObjectBefore include ActiveSupport::NewCallbacks - + define_callbacks :save set_callback :save, :before, CallbackObject.new - + attr_accessor :record def initialize @record = [] end - + def save _run_save_callbacks do @record << "yielded" @@ -443,19 +459,49 @@ module NewCallbacksTest end end end - + + class CustomScopeObject + include ActiveSupport::NewCallbacks + + define_callbacks :save, :scope => [:kind, :name] + set_callback :save, :before, CallbackObject.new + + attr_accessor :record + def initialize + @record = [] + end + + def save + _run_save_callbacks do + @record << "yielded" + "CallbackResult" + end + end + end + class UsingObjectTest < Test::Unit::TestCase def test_before_object u = UsingObjectBefore.new u.save assert_equal ["before", "yielded"], u.record end - + def test_around_object u = UsingObjectAround.new u.save assert_equal ["around before", "yielded", "around after"], u.record - end + end + + def test_customized_object + u = CustomScopeObject.new + u.save + assert_equal ["before save", "yielded"], u.record + end + + def test_block_result_is_returned + u = CustomScopeObject.new + assert_equal "CallbackResult", u.save + end end class CallbackTerminatorTest < Test::Unit::TestCase @@ -469,7 +515,7 @@ module NewCallbacksTest obj = CallbackTerminator.new obj.save assert !obj.saved - end + end end class HyphenatedKeyTest < Test::Unit::TestCase @@ -477,6 +523,6 @@ module NewCallbacksTest obj = HyphenatedCallbacks.new obj.save assert_equal obj.stuff, "OMG" - end - end + end + end end diff --git a/activesupport/test/orchestra_test.rb b/activesupport/test/orchestra_test.rb new file mode 100644 index 0000000000..683cc36f6a --- /dev/null +++ b/activesupport/test/orchestra_test.rb @@ -0,0 +1,161 @@ +require 'abstract_unit' + +class OrchestraEventTest < Test::Unit::TestCase + def setup + @parent = ActiveSupport::Orchestra::Event.new(:parent) + end + + def test_initialization_with_name_and_parent_and_payload + event = ActiveSupport::Orchestra::Event.new(:awesome, @parent, :payload => "orchestra") + assert_equal(:awesome, event.name) + assert_equal(@parent, event.parent) + assert_equal({ :payload => "orchestra" }, event.payload) + end + + def test_thread_id_is_set_on_initialization + event = ActiveSupport::Orchestra::Event.new(:awesome) + assert_equal Thread.current.object_id, event.thread_id + end + + def test_current_time_is_set_on_initialization + previous_time = Time.now.utc + event = ActiveSupport::Orchestra::Event.new(:awesome) + assert_kind_of Time, event.time + assert event.time.to_f >= previous_time.to_f + end + + def test_duration_is_set_when_event_finishes + event = ActiveSupport::Orchestra::Event.new(:awesome) + sleep(0.1) + event.finish! + assert_in_delta 100, event.duration, 30 + end +end + +class OrchestraMainTest < Test::Unit::TestCase + def setup + @listener = [] + ActiveSupport::Orchestra.register @listener + end + + def teardown + ActiveSupport::Orchestra.unregister @listener + end + + def test_orchestra_allows_any_action_to_be_instrumented + event = ActiveSupport::Orchestra.instrument(:awesome, "orchestra") do + sleep(0.1) + end + + assert_equal :awesome, event.name + assert_equal "orchestra", event.payload + assert_in_delta 100, event.duration, 30 + end + + def test_block_result_is_stored + event = ActiveSupport::Orchestra.instrument(:awesome, "orchestra") do + 1 + 1 + end + + assert_equal 2, event.result + end + + def test_events_are_published_to_a_listener + event = ActiveSupport::Orchestra.instrument(:awesome, "orchestra") do + 1 + 1 + end + + assert_equal 1, @listener.size + assert_equal :awesome, @listener.last.name + assert_equal "orchestra", @listener.last.payload + end + + def test_nested_events_can_be_instrumented + ActiveSupport::Orchestra.instrument(:awesome, "orchestra") do + ActiveSupport::Orchestra.instrument(:wot, "child") do + sleep(0.1) + end + + assert_equal 1, @listener.size + assert_equal :wot, @listener.first.name + assert_equal "child", @listener.first.payload + + assert_nil @listener.first.parent.duration + assert_in_delta 100, @listener.first.duration, 30 + end + + assert_equal 2, @listener.size + assert_equal :awesome, @listener.last.name + assert_equal "orchestra", @listener.last.payload + assert_in_delta 100, @listener.first.parent.duration, 30 + end + + def test_event_is_pushed_even_if_block_fails + ActiveSupport::Orchestra.instrument(:awesome, "orchestra") do + raise "OMG" + end rescue RuntimeError + + assert_equal 1, @listener.size + assert_equal :awesome, @listener.last.name + assert_equal "orchestra", @listener.last.payload + end +end + +class OrchestraListenerTest < Test::Unit::TestCase + class MyListener < ActiveSupport::Orchestra::Listener + attr_reader :consumed + + def consume(event) + @consumed ||= [] + @consumed << event + end + end + + def setup + @listener = MyListener.new + ActiveSupport::Orchestra.register @listener + end + + def teardown + ActiveSupport::Orchestra.unregister @listener + end + + def test_thread_is_exposed_by_listener + assert_kind_of Thread, @listener.thread + end + + def test_event_is_consumed_when_an_action_is_instrumented + ActiveSupport::Orchestra.instrument(:sum) do + 1 + 1 + end + sleep 0.1 + assert_equal 1, @listener.consumed.size + assert_equal :sum, @listener.consumed.first.name + assert_equal 2, @listener.consumed.first.result + end + + def test_with_sevaral_consumers_and_several_events + @another = MyListener.new + ActiveSupport::Orchestra.register @another + + 1.upto(100) do |i| + ActiveSupport::Orchestra.instrument(:value) do + i + end + end + + sleep 0.1 + + assert_equal 100, @listener.consumed.size + assert_equal :value, @listener.consumed.first.name + assert_equal 1, @listener.consumed.first.result + assert_equal 100, @listener.consumed.last.result + + assert_equal 100, @another.consumed.size + assert_equal :value, @another.consumed.first.name + assert_equal 1, @another.consumed.first.result + assert_equal 100, @another.consumed.last.result + ensure + ActiveSupport::Orchestra.unregister @another + end +end diff --git a/activesupport/test/ts_isolated.rb b/activesupport/test/ts_isolated.rb new file mode 100644 index 0000000000..9378a13766 --- /dev/null +++ b/activesupport/test/ts_isolated.rb @@ -0,0 +1,15 @@ +require 'test/unit' +require 'rbconfig' +require 'active_support/core_ext/kernel/reporting' + +class TestIsolated < Test::Unit::TestCase + ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) + + Dir["#{File.dirname(__FILE__)}/**/*_test.rb"].each do |file| + define_method("test #{file}") do + command = "#{ruby} -Ilib:test #{file}" + silence_stderr { `#{command}` } + assert_equal 0, $?.to_i, command + end + end +end diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index 7c3a591e63..e16f36acee 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -3,13 +3,11 @@ require 'active_support/xml_mini' require 'active_support/core_ext/hash/conversions' begin - gem 'nokogiri', '>= 1.1.1' -rescue Gem::LoadError + require 'nokogiri' +rescue LoadError # Skip nokogiri tests else -require 'nokogiri' - class NokogiriEngineTest < Test::Unit::TestCase include ActiveSupport @@ -161,6 +159,17 @@ class NokogiriEngineTest < Test::Unit::TestCase XmlMini.parse(io) end + def test_children_with_cdata + assert_equal_rexml(<<-eoxml) + <root> + <products> + hello <![CDATA[everyone]]> + morning + </products> + </root> + eoxml + end + private def assert_equal_rexml(xml) hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } diff --git a/ci/ci_build.rb b/ci/ci_build.rb index 06e513f907..b9c321efef 100755 --- a/ci/ci_build.rb +++ b/ci/ci_build.rb @@ -21,6 +21,7 @@ cd "#{root_dir}/activesupport" do puts "[CruiseControl] Building ActiveSupport" puts build_results[:activesupport] = system 'rake' + build_results[:activesupport_isolated] = system 'rake test:isolated' end rm_f "#{root_dir}/activerecord/debug.log" @@ -64,7 +65,8 @@ cd "#{root_dir}/actionpack" do puts puts "[CruiseControl] Building ActionPack" puts - build_results[:actionpack] = system 'rake' + build_results[:actionpack] = system 'gem bundle && rake' + build_results[:actionpack_isolated] = system 'rake test:isolated' end cd "#{root_dir}/actionmailer" do diff --git a/ci/ci_setup_notes.txt b/ci/ci_setup_notes.txt index 780277c939..b61e893b8e 100644 --- a/ci/ci_setup_notes.txt +++ b/ci/ci_setup_notes.txt @@ -17,9 +17,9 @@ root:*:14001:0:99999:7::: * Change Hostname: $ sudo vi /etc/hostname -change to 'ci' +change to correct hostname $ sudo vi /etc/hosts -replace old hostname with 'ci' +replace old hostname with the correct hostname # reboot to use new hostname (and test reboot) $ sudo shutdown -r now @@ -104,8 +104,8 @@ end $ sudo aptitude install mysql-server-5.0 libmysqlclient-dev # no password for mysql root user -* setup sqlite -$ sudo aptitude install sqlite sqlite3 libsqlite-dev libsqlite3-dev +* setup sqlite 3 +$ sudo aptitude install sqlite3 libsqlite3-dev # Note: there's some installation bugs with sqlite3-ruby 1.2.2 gem file permissions: # http://www.icoretech.org/2008/07/06/no-such-file-to-load-sqlite3-database # cd /usr/local/lib/ruby/gems/1.8/gems/sqlite3-ruby-1.2.2 && sudo find . -perm 0662 -exec chmod 664 {} \; diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb index 2247a1b6ea..9c7fa98a70 100644 --- a/ci/cruise_config.rb +++ b/ci/cruise_config.rb @@ -1,6 +1,9 @@ Project.configure do |project| - project.build_command = 'sudo update_rubygems && ruby ci/ci_build.rb' - project.email_notifier.emails = ['thewoolleyman@gmail.com'] -# project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com'] - project.email_notifier.from = 'thewoolleyman+railsci@gmail.com' + # Send email notifications about broken and fixed builds to core mailing list + if Socket.gethostname =~ /ci.rubyonrails.org/ && ENV['ENABLE_RAILS_CI_EMAILS'] == 'true' + project.email_notifier.emails = ['rubyonrails-core@googlegroups.com'] + end + + project.build_command = 'sudo gem update --system && ruby ci/ci_build.rb' + project.email_notifier.from = 'thewoolleyman@gmail.com' end diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index fad9e7d786..8dde704e79 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -11,14 +11,16 @@ gems: - name: mysql #version: >= 2.7 version: = 2.7 +- name: nokogiri + version: >= 1.3.3 - name: pg version: >= 0.8.0 - name: rack version: '~> 1.0.0' +- name: rack-test + version: >= 0.5.0 - name: rake version: >= 0.8.1 -- name: sqlite-ruby - version: >= 2.2.3 - name: sqlite3-ruby version: >= 1.2.2 - name: rubygems-update diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 782afd5aa4..d6311f77a0 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs] + * Ruby 1.9: use UTF-8 for default internal and external encodings. [Jeremy Kemper] * Added db/seeds.rb as a default file for storing seed data for the database. Can be loaded with rake db:seed (or created alongside the db with db:setup). (This is also known as the "Stop Putting Gawd Damn Seed Data In Your Migrations" feature) [DHH] diff --git a/railties/Rakefile b/railties/Rakefile index 3212bf3a4f..e36930af4f 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -9,7 +9,7 @@ require 'rbconfig' require File.join(File.dirname(__FILE__), 'lib/rails', 'version') PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' -PKG_NAME = 'rails' +PKG_NAME = ENV['PKG_NAME'] || 'rails' PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}" @@ -81,7 +81,7 @@ end task :create_rails do require File.join(File.dirname(__FILE__), 'lib', 'generators') - require 'generators/rails/app/app_generator' + require 'rails/generators/rails/app/app_generator' Rails::Generators::AppGenerator.start [ File.basename(PKG_DESTINATION), "--quiet" ], :destination_root => File.expand_path(File.dirname(PKG_DESTINATION)) end @@ -137,45 +137,7 @@ Rake::RDocTask.new { |rdoc| # Generate GEM ---------------------------------------------------------------------------- -PKG_FILES = FileList[ - '[a-zA-Z]*', - 'bin/**/*', - 'builtin/**/*', - 'guides/**/*', - 'lib/**/*' -] - [ 'test' ] - -spec = Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = 'rails' - s.version = PKG_VERSION - s.summary = "Web-application framework with template engine, control-flow layer, and ORM." - 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' + PKG_BUILD) - s.add_dependency('activerecord', '= 3.0.pre' + PKG_BUILD) - s.add_dependency('actionpack', '= 3.0.pre' + PKG_BUILD) - s.add_dependency('actionmailer', '= 3.0.pre' + PKG_BUILD) - s.add_dependency('activeresource', '= 3.0.pre' + PKG_BUILD) - - s.rdoc_options << '--exclude' << '.' - s.has_rdoc = false - - s.files = PKG_FILES.to_a.delete_if {|f| f =~ %r{\.svn|guides/output}} - s.require_path = 'lib' - s.bindir = "bin" # Use these for applications. - 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 +spec = eval(File.read('rails.gemspec')) Rake::GemPackageTask.new(spec) do |pkg| pkg.gem_spec = spec diff --git a/railties/bin/rails b/railties/bin/rails index 538d0cbc84..e743aa83f1 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -1,7 +1,17 @@ -require File.dirname(__FILE__) + '/../lib/ruby_version_check' +begin + require 'rails/ruby_version_check' +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 +end + Signal.trap("INT") { puts; exit } -require File.dirname(__FILE__) + '/../lib/rails/version' +require 'rails/version' if %w(--version -v).include? ARGV.first puts "Rails #{Rails::VERSION::STRING}" exit(0) @@ -9,7 +19,7 @@ end ARGV << "--help" if ARGV.empty? -require File.dirname(__FILE__) + '/../lib/generators' -require 'generators/rails/app/app_generator' +require 'rails/generators' +require 'rails/generators/rails/app/app_generator' Rails::Generators::AppGenerator.start diff --git a/railties/builtin/rails_info/rails/info.rb b/railties/builtin/rails_info/rails/info.rb index fdacc469f5..48d89ad06a 100644 --- a/railties/builtin/rails_info/rails/info.rb +++ b/railties/builtin/rails_info/rails/info.rb @@ -29,8 +29,10 @@ module Rails end def framework_version(framework) - require "#{framework}/version" - "#{framework.classify}::VERSION::STRING".constantize + if Object.const_defined?(framework.classify) + require "#{framework}/version" + "#{framework.classify}::VERSION::STRING".constantize + end end def edge_rails_revision(info = git_info) @@ -112,7 +114,7 @@ module Rails end property 'Middleware' do - ActionController::Dispatcher.middleware.active.map {|middle| middle.inspect } + Rails.configuration.middleware.active.map { |middle| middle.inspect } end # The Rails Git revision, if it's checked out into vendor/rails. diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile index 545aaa18e0..1ad45f1cca 100644 --- a/railties/guides/source/rails_on_rack.textile +++ b/railties/guides/source/rails_on_rack.textile @@ -40,11 +40,8 @@ Here's how +script/server+ creates an instance of +Rack::Builder+ app = Rack::Builder.new { use Rails::Rack::LogTailer unless options[:detach] use Rails::Rack::Debugger if options[:debugger] - - map "/" do - use Rails::Rack::Static - run ActionController::Dispatcher.new - end + use ActionDispatch::Static + run ActionController::Dispatcher.new }.to_app </ruby> @@ -52,7 +49,7 @@ Middlewares used in the code above are primarily useful only in the development |_.Middleware|_.Purpose| |+Rails::Rack::LogTailer+|Appends log file output to console| -|+Rails::Rack::Static+|Serves static files inside +RAILS_ROOT/public+ directory| +|+ActionDispatch::Static+|Serves static files inside +RAILS_ROOT/public+ directory| |+Rails::Rack::Debugger+|Starts Debugger| h4. +rackup+ @@ -64,7 +61,7 @@ To use +rackup+ instead of Rails' +script/server+, you can put the following ins require "config/environment" use Rails::Rack::LogTailer -use Rails::Rack::Static +use ActionDispatch::Static run ActionController::Dispatcher.new </ruby> diff --git a/railties/lib/generators/erb/scaffold/templates/new.html.erb b/railties/lib/generators/erb/scaffold/templates/new.html.erb deleted file mode 100644 index 96c89fc50e..0000000000 --- a/railties/lib/generators/erb/scaffold/templates/new.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<h1>New <%= singular_name %></h1> - -<%% form_for(@<%= singular_name %>) do |f| %> - <%%= f.error_messages %> - -<% for attribute in attributes -%> - <p> - <%%= f.label :<%= attribute.name %> %><br /> - <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> - </p> -<% end -%> - <p> - <%%= f.submit 'Create' %> - </p> -<%% end %> - -<%%= link_to 'Back', <%= plural_name %>_path %>
\ No newline at end of file diff --git a/railties/lib/generators/rails/app/templates/dispatchers/config.ru b/railties/lib/generators/rails/app/templates/dispatchers/config.ru deleted file mode 100644 index acbfe4e9ae..0000000000 --- a/railties/lib/generators/rails/app/templates/dispatchers/config.ru +++ /dev/null @@ -1,7 +0,0 @@ -# Rack Dispatcher - -# Require your environment file to bootstrap Rails -require File.dirname(__FILE__) + '/config/environment' - -# Dispatch the request -run ActionController::Dispatcher.new diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb new file mode 100644 index 0000000000..43ece14a49 --- /dev/null +++ b/railties/lib/rails.rb @@ -0,0 +1 @@ +require 'rails/initializer' diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb new file mode 100644 index 0000000000..6139e20e95 --- /dev/null +++ b/railties/lib/rails/application.rb @@ -0,0 +1,29 @@ +module Rails + class Application + + def self.config + @config ||= Configuration.new + end + + def self.config=(config) + @config = config + end + + def config + self.class.config + end + + def routes + ActionController::Routing::Routes + end + + def middleware + config.middleware + end + + def call(env) + @app ||= middleware.build(routes) + @app.call(env) + end + end +end diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb index 1605429e8b..9ff8367807 100644 --- a/railties/lib/rails/backtrace_cleaner.rb +++ b/railties/lib/rails/backtrace_cleaner.rb @@ -32,7 +32,7 @@ module Rails private def add_gem_filters - Gem.path.each do |path| + (Gem.path + [Gem.default_dir]).uniq.each do |path| # http://gist.github.com/30430 add_filter { |line| line.sub(/(#{path})\/gems\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) \4')} end diff --git a/railties/lib/code_statistics.rb b/railties/lib/rails/code_statistics.rb index 740d8a1767..740d8a1767 100644 --- a/railties/lib/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb diff --git a/railties/lib/commands.rb b/railties/lib/rails/commands.rb index 841e98a0dc..841e98a0dc 100644 --- a/railties/lib/commands.rb +++ b/railties/lib/rails/commands.rb diff --git a/railties/lib/commands/about.rb b/railties/lib/rails/commands/about.rb index bc2cfcb948..bc2cfcb948 100644 --- a/railties/lib/commands/about.rb +++ b/railties/lib/rails/commands/about.rb diff --git a/railties/lib/commands/console.rb b/railties/lib/rails/commands/console.rb index 63df834639..31448bdf1a 100644 --- a/railties/lib/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -13,9 +13,9 @@ end libs = " -r irb/completion" libs << %( -r "#{RAILS_ROOT}/config/environment") -libs << " -r console_app" -libs << " -r console_sandbox" if options[:sandbox] -libs << " -r console_with_helpers" +libs << " -r rails/console_app" +libs << " -r rails/console_sandbox" if options[:sandbox] +libs << " -r rails/console_with_helpers" if options[:debugger] begin diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index e6f11a45db..e6f11a45db 100644 --- a/railties/lib/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb diff --git a/railties/lib/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb index 5013d30b83..5013d30b83 100644 --- a/railties/lib/commands/destroy.rb +++ b/railties/lib/rails/commands/destroy.rb diff --git a/railties/lib/commands/generate.rb b/railties/lib/rails/commands/generate.rb index 32cabcab10..32cabcab10 100755 --- a/railties/lib/commands/generate.rb +++ b/railties/lib/rails/commands/generate.rb diff --git a/railties/lib/commands/ncgi/listener b/railties/lib/rails/commands/ncgi/listener index 7079ef78a6..7079ef78a6 100755 --- a/railties/lib/commands/ncgi/listener +++ b/railties/lib/rails/commands/ncgi/listener diff --git a/railties/lib/commands/ncgi/tracker b/railties/lib/rails/commands/ncgi/tracker index 4ca12d779b..4ca12d779b 100755 --- a/railties/lib/commands/ncgi/tracker +++ b/railties/lib/rails/commands/ncgi/tracker diff --git a/railties/lib/commands/performance/benchmarker.rb b/railties/lib/rails/commands/performance/benchmarker.rb index e8804fe1bf..e8804fe1bf 100644 --- a/railties/lib/commands/performance/benchmarker.rb +++ b/railties/lib/rails/commands/performance/benchmarker.rb diff --git a/railties/lib/commands/performance/profiler.rb b/railties/lib/rails/commands/performance/profiler.rb index 7df840f197..7df840f197 100644 --- a/railties/lib/commands/performance/profiler.rb +++ b/railties/lib/rails/commands/performance/profiler.rb diff --git a/railties/lib/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb index 3d76bcc73f..159db707e7 100644 --- a/railties/lib/commands/plugin.rb +++ b/railties/lib/rails/commands/plugin.rb @@ -232,7 +232,7 @@ class Plugin def install_using_git(options = {}) root = rails_env.root - install_path = mkdir_p "#{root}/vendor/plugins/#{name}" + mkdir_p(install_path = "#{root}/vendor/plugins/#{name}") Dir.chdir install_path do init_cmd = "git init" init_cmd += " -q" if options[:quiet] and not $verbose diff --git a/railties/lib/commands/runner.rb b/railties/lib/rails/commands/runner.rb index 510128318a..510128318a 100644 --- a/railties/lib/commands/runner.rb +++ b/railties/lib/rails/commands/runner.rb diff --git a/railties/lib/commands/server.rb b/railties/lib/rails/commands/server.rb index 823916b1dc..c138cbc9bf 100644 --- a/railties/lib/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -9,8 +9,7 @@ options = { :environment => (ENV['RAILS_ENV'] || "development").dup, :config => RAILS_ROOT + "/config.ru", :detach => false, - :debugger => false, - :path => nil + :debugger => false } ARGV.clone.options do |opts| @@ -25,7 +24,6 @@ ARGV.clone.options do |opts| opts.on("-e", "--environment=name", String, "Specifies the environment to run this server under (test/development/production).", "Default: #{options[:environment]}") { |v| options[:environment] = v } - opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: #{options[:path]}") { |v| options[:path] = v } opts.separator "" @@ -44,11 +42,7 @@ unless server end puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" -puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}" - -%w(cache pids sessions sockets).each do |dir_to_make| - FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) -end +puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}}" if options[:detach] Process.daemon @@ -60,37 +54,10 @@ end ENV["RAILS_ENV"] = options[:environment] RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) -if File.exist?(options[:config]) - config = options[:config] - 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 -else - require RAILS_ROOT + "/config/environment" - inner_app = ActionController::Dispatcher.new -end - -if options[:path].nil? - map_path = "/" -else - ActionController::Base.relative_url_root = options[:path] - map_path = options[:path] -end - app = Rack::Builder.new { use Rails::Rack::LogTailer unless options[:detach] use Rails::Rack::Debugger if options[:debugger] - map map_path do - use Rails::Rack::Static - run inner_app - end + run ActionDispatch::Utils.parse_config(options[:config]) }.to_app puts "=> Call with -d to detach" diff --git a/railties/lib/commands/update.rb b/railties/lib/rails/commands/update.rb index f3b3ad0775..f3b3ad0775 100644 --- a/railties/lib/commands/update.rb +++ b/railties/lib/rails/commands/update.rb diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 5cc4f80684..4a70a4800e 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -10,7 +10,7 @@ module Rails :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 + :eager_load_paths, :dependency_loading, :paths, :serve_static_assets def initialize set_root_path! @@ -35,6 +35,7 @@ module Rails @controller_paths = default_controller_paths @routes_configuration_file = default_routes_configuration_file @database_configuration_file = default_database_configuration_file + @serve_static_assets = default_serve_static_assets for framework in default_frameworks self.send("#{framework}=", Rails::OrderedOptions.new) @@ -107,9 +108,9 @@ module Rails defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails" end - # TODO: Fix this when there is an application object def middleware - ActionController::Dispatcher.middleware + require 'action_dispatch' + @middleware ||= ActionDispatch::MiddlewareStack.new end # Loads and returns the contents of the #database_configuration_file. The @@ -224,6 +225,10 @@ module Rails i18n end + def default_serve_static_assets + true + end + # Adds a single Gem dependency to the rails application. By default, it will require # the library with the same name as the gem. Use :lib to specify a different name. # diff --git a/railties/lib/console_app.rb b/railties/lib/rails/console_app.rb index 75e6f11ea3..1ad62e5058 100644 --- a/railties/lib/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -17,7 +17,8 @@ end # create a new session. If a block is given, the new session will be yielded # to the block before being returned. def new_session - session = ActionController::Integration::Session.new + app = ActionController::Dispatcher.new + session = ActionController::Integration::Session.new(app) yield session if block_given? session end @@ -25,7 +26,7 @@ end #reloads the environment def reload! puts "Reloading..." - ActionController::Dispatcher.new - ActionController::Dispatcher.router.reload + ActionDispatch::Callbacks.new(lambda {}, true) + ActionController::Routing::Routes.reload true end diff --git a/railties/lib/console_sandbox.rb b/railties/lib/rails/console_sandbox.rb index 65a3d68619..65a3d68619 100644 --- a/railties/lib/console_sandbox.rb +++ b/railties/lib/rails/console_sandbox.rb diff --git a/railties/lib/console_with_helpers.rb b/railties/lib/rails/console_with_helpers.rb index 039db667c4..039db667c4 100644 --- a/railties/lib/console_with_helpers.rb +++ b/railties/lib/rails/console_with_helpers.rb diff --git a/railties/lib/rails/core.rb b/railties/lib/rails/core.rb index 33695a27b9..4be90de792 100644 --- a/railties/lib/rails/core.rb +++ b/railties/lib/rails/core.rb @@ -5,6 +5,14 @@ module Rails # --- # TODO: w0t? class << self + def application + @@application + end + + def application=(application) + @@application = application + end + # The Configuration instance used to configure the Rails environment def configuration @@configuration diff --git a/railties/lib/dispatcher.rb b/railties/lib/rails/dispatcher.rb index 7f9a6221d9..7f9a6221d9 100644 --- a/railties/lib/dispatcher.rb +++ b/railties/lib/rails/dispatcher.rb diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/rails/fcgi_handler.rb index 0cd2dc51c6..ef6f3b094c 100644 --- a/railties/lib/fcgi_handler.rb +++ b/railties/lib/rails/fcgi_handler.rb @@ -1,6 +1,6 @@ require 'fcgi' require 'logger' -require 'dispatcher' +require 'rails/dispatcher' require 'rbconfig' class RailsFCGIHandler diff --git a/railties/lib/generators.rb b/railties/lib/rails/generators.rb index a2f462ae81..0419a4e36c 100644 --- a/railties/lib/generators.rb +++ b/railties/lib/rails/generators.rb @@ -1,19 +1,17 @@ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) - -begin - require 'active_support/mini' -rescue LoadError - require 'rubygems' - gem 'activesupport' - require 'active_support/mini' -end - -$:.unshift(File.dirname(__FILE__)) - -require 'vendor/thor-0.11.5/lib/thor' -require 'generators/base' -require 'generators/named_base' +$LOAD_PATH.unshift(activesupport_path) if File.directory?(activesupport_path) +require 'active_support' +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/array' +require 'active_support/core_ext/hash' +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.11.6/lib") +require 'rails/generators/base' +require 'rails/generators/named_base' module Rails module Generators @@ -45,7 +43,6 @@ module Rails }, :erb => { - :form => false, :layout => true }, diff --git a/railties/lib/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 03d0d11a07..c4552dd399 100644 --- a/railties/lib/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -89,13 +89,11 @@ module Rails # git :add => "onefile.rb", :rm => "badfile.cxx" # def git(command={}) - in_root do - if command.is_a?(Symbol) - run "git #{command}" - else - command.each do |command, options| - run "git #{command} #{options}" - end + if command.is_a?(Symbol) + run "git #{command}" + else + command.each do |command, options| + run "git #{command} #{options}" end end end diff --git a/railties/lib/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb index 1a849a0e02..1a849a0e02 100644 --- a/railties/lib/generators/active_model.rb +++ b/railties/lib/rails/generators/active_model.rb diff --git a/railties/lib/generators/active_record.rb b/railties/lib/rails/generators/active_record.rb index ff3093f356..c03ea59c1b 100644 --- a/railties/lib/generators/active_record.rb +++ b/railties/lib/rails/generators/active_record.rb @@ -1,6 +1,6 @@ -require 'generators/named_base' -require 'generators/migration' -require 'generators/active_model' +require 'rails/generators/named_base' +require 'rails/generators/migration' +require 'rails/generators/active_model' module ActiveRecord module Generators diff --git a/railties/lib/generators/active_record/migration/migration_generator.rb b/railties/lib/rails/generators/active_record/migration/migration_generator.rb index 7939977f72..f6159deeeb 100644 --- a/railties/lib/generators/active_record/migration/migration_generator.rb +++ b/railties/lib/rails/generators/active_record/migration/migration_generator.rb @@ -1,4 +1,4 @@ -require 'generators/active_record' +require 'rails/generators/active_record' module ActiveRecord module Generators diff --git a/railties/lib/generators/active_record/migration/templates/migration.rb b/railties/lib/rails/generators/active_record/migration/templates/migration.rb index bbb7c53d86..bbb7c53d86 100644 --- a/railties/lib/generators/active_record/migration/templates/migration.rb +++ b/railties/lib/rails/generators/active_record/migration/templates/migration.rb diff --git a/railties/lib/generators/active_record/model/model_generator.rb b/railties/lib/rails/generators/active_record/model/model_generator.rb index 2641083e0d..3e72fbeca8 100644 --- a/railties/lib/generators/active_record/model/model_generator.rb +++ b/railties/lib/rails/generators/active_record/model/model_generator.rb @@ -1,4 +1,4 @@ -require 'generators/active_record' +require 'rails/generators/active_record' module ActiveRecord module Generators diff --git a/railties/lib/generators/active_record/model/templates/migration.rb b/railties/lib/rails/generators/active_record/model/templates/migration.rb index 1f68487304..1f68487304 100644 --- a/railties/lib/generators/active_record/model/templates/migration.rb +++ b/railties/lib/rails/generators/active_record/model/templates/migration.rb diff --git a/railties/lib/generators/active_record/model/templates/model.rb b/railties/lib/rails/generators/active_record/model/templates/model.rb index 21ae29e9f2..21ae29e9f2 100644 --- a/railties/lib/generators/active_record/model/templates/model.rb +++ b/railties/lib/rails/generators/active_record/model/templates/model.rb diff --git a/railties/lib/generators/active_record/observer/observer_generator.rb b/railties/lib/rails/generators/active_record/observer/observer_generator.rb index a6b57423b8..c1c0e3f25b 100644 --- a/railties/lib/generators/active_record/observer/observer_generator.rb +++ b/railties/lib/rails/generators/active_record/observer/observer_generator.rb @@ -1,4 +1,4 @@ -require 'generators/active_record' +require 'rails/generators/active_record' module ActiveRecord module Generators diff --git a/railties/lib/generators/active_record/observer/templates/observer.rb b/railties/lib/rails/generators/active_record/observer/templates/observer.rb index b9a3004161..b9a3004161 100644 --- a/railties/lib/generators/active_record/observer/templates/observer.rb +++ b/railties/lib/rails/generators/active_record/observer/templates/observer.rb diff --git a/railties/lib/generators/active_record/session_migration/session_migration_generator.rb b/railties/lib/rails/generators/active_record/session_migration/session_migration_generator.rb index 59c4792066..afcda2a98a 100644 --- a/railties/lib/generators/active_record/session_migration/session_migration_generator.rb +++ b/railties/lib/rails/generators/active_record/session_migration/session_migration_generator.rb @@ -1,4 +1,4 @@ -require 'generators/active_record' +require 'rails/generators/active_record' module ActiveRecord module Generators diff --git a/railties/lib/generators/active_record/session_migration/templates/migration.rb b/railties/lib/rails/generators/active_record/session_migration/templates/migration.rb index 919822af7b..919822af7b 100644 --- a/railties/lib/generators/active_record/session_migration/templates/migration.rb +++ b/railties/lib/rails/generators/active_record/session_migration/templates/migration.rb diff --git a/railties/lib/generators/base.rb b/railties/lib/rails/generators/base.rb index cbe9c0a49d..720caa5b3f 100644 --- a/railties/lib/generators/base.rb +++ b/railties/lib/rails/generators/base.rb @@ -1,4 +1,5 @@ -require 'generators/actions' +require 'thor' +require 'rails/generators/actions' module Rails module Generators @@ -9,6 +10,8 @@ module Rails include Thor::Actions include Rails::Generators::Actions + add_runtime_options! + # Automatically sets the source root based on the class name. # def self.source_root @@ -45,8 +48,10 @@ module Rails # # ==== Examples # - # class ControllerGenerator < Rails::Generators::Base - # hook_for :test_framework, :aliases => "-t" + # module Rails::Generators + # class ControllerGenerator < Base + # hook_for :test_framework, :aliases => "-t" + # end # end # # The example above will create a test framework option and will invoke @@ -64,7 +69,49 @@ module Rails # invoked. This allows any test framework to hook into Rails as long as it # provides any of the hooks above. # - # Finally, if the user don't want to use any test framework, he can do: + # ==== Options + # + # This lookup can be customized with two options: :base and :as. The first + # is the root module value and in the example above defaults to "rails". + # The later defaults to the generator name, without the "Generator" ending. + # + # Let's suppose you are creating a generator that needs to invoke the + # controller generator from test unit. Your first attempt is: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework + # end + # + # The lookup in this case for test_unit as input is: + # + # "test_unit:generators:awesome", "test_unit" + # + # Which is not the desired the lookup. You can change it by providing the + # :as option: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework, :as => :controller + # end + # + # And now it will lookup at: + # + # "test_unit:generators:awesome", "test_unit" + # + # Similarly, if you want it to also lookup in the rails namespace, you just + # need to provide the :base value: + # + # class AwesomeGenerator < Rails::Generators::Base + # hook_for :test_framework, :base => :rails, :as => :controller + # end + # + # And the lookup is exactly the same as previously: + # + # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit" + # + # ==== Switches + # + # All hooks come with switches for user interface. If the user don't want + # to use any test framework, he can do: # # ruby script/generate controller Account --skip-test-framework # diff --git a/railties/lib/generators/erb.rb b/railties/lib/rails/generators/erb.rb index 43d47109c7..d468d012dc 100644 --- a/railties/lib/generators/erb.rb +++ b/railties/lib/rails/generators/erb.rb @@ -1,4 +1,4 @@ -require 'generators/named_base' +require 'rails/generators/named_base' module Erb module Generators diff --git a/railties/lib/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb index ab7b273662..f8780d9c33 100644 --- a/railties/lib/generators/erb/controller/controller_generator.rb +++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb @@ -1,4 +1,4 @@ -require 'generators/erb' +require 'rails/generators/erb' module Erb module Generators diff --git a/railties/lib/generators/erb/controller/templates/view.html.erb b/railties/lib/rails/generators/erb/controller/templates/view.html.erb index cd54d13d83..cd54d13d83 100644 --- a/railties/lib/generators/erb/controller/templates/view.html.erb +++ b/railties/lib/rails/generators/erb/controller/templates/view.html.erb diff --git a/railties/lib/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index 4ec2f4c9f4..5266259e2f 100644 --- a/railties/lib/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -1,4 +1,4 @@ -require 'generators/erb' +require 'rails/generators/erb' module Erb module Generators diff --git a/railties/lib/generators/erb/mailer/templates/view.erb b/railties/lib/rails/generators/erb/mailer/templates/view.erb index fcce7bd805..fcce7bd805 100644 --- a/railties/lib/generators/erb/mailer/templates/view.erb +++ b/railties/lib/rails/generators/erb/mailer/templates/view.erb diff --git a/railties/lib/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb index 955f22192a..de5b0e9c5d 100644 --- a/railties/lib/generators/erb/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb @@ -1,13 +1,13 @@ -require 'generators/erb' +require 'rails/generators/erb' +require 'rails/generators/resource_helpers' module Erb module Generators class ScaffoldGenerator < Base - include Rails::Generators::ScaffoldBase + include Rails::Generators::ResourceHelpers argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" - class_option :form, :type => :boolean class_option :layout, :type => :boolean class_option :singleton, :type => :boolean, :desc => "Supply to skip index view" @@ -33,7 +33,6 @@ module Erb end def copy_form_file - return unless options[:form] copy_view :_form end diff --git a/railties/lib/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb index cca1d61c68..d02028d983 100644 --- a/railties/lib/generators/erb/scaffold/templates/edit.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb @@ -1,18 +1,17 @@ -<h1>Editing <%= singular_name %></h1> - <%% form_for(@<%= singular_name %>) do |f| %> <%%= f.error_messages %> <% for attribute in attributes -%> - <p> + <div class="field"> <%%= f.label :<%= attribute.name %> %><br /> <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> - </p> + </div> <% end -%> - <p> - <%%= f.submit 'Update' %> - </p> + <div class="actions"> + <%% if @<%= singular_name %>.new_record? %> + <%%= f.submit 'Create' %> + <%% else %> + <%%= f.submit 'Update' %> + <%% end %> + </div> <%% end %> - -<%%= link_to 'Show', @<%= singular_name %> %> | -<%%= link_to 'Back', <%= plural_name %>_path %>
\ No newline at end of file diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb new file mode 100644 index 0000000000..5bc507ffc8 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb @@ -0,0 +1,6 @@ +<h1>Editing <%= singular_name %></h1> + +<%%= render 'form' %> + +<%%= link_to 'Show', @<%= singular_name %> %> | +<%%= link_to 'Back', <%= plural_name %>_path %> diff --git a/railties/lib/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb index 5e6a4af9e0..5e6a4af9e0 100644 --- a/railties/lib/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb diff --git a/railties/lib/generators/erb/scaffold/templates/layout.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb index aacfbe4a8f..6460e5b599 100644 --- a/railties/lib/generators/erb/scaffold/templates/layout.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb @@ -7,7 +7,7 @@ </head> <body> -<p style="color: green"><%%= flash[:notice] %></p> +<p class="notice"><%%= flash[:notice] %></p> <%%= yield %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb new file mode 100644 index 0000000000..9a1c489331 --- /dev/null +++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb @@ -0,0 +1,5 @@ +<h1>New <%= singular_name %></h1> + +<%%= render 'form' %> + +<%%= link_to 'Back', <%= plural_name %>_path %> diff --git a/railties/lib/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb index 25567957be..25567957be 100644 --- a/railties/lib/generators/erb/scaffold/templates/show.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb diff --git a/railties/lib/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb index e962308585..e962308585 100644 --- a/railties/lib/generators/generated_attribute.rb +++ b/railties/lib/rails/generators/generated_attribute.rb diff --git a/railties/lib/generators/migration.rb b/railties/lib/rails/generators/migration.rb index 0a9151ecdf..0a9151ecdf 100644 --- a/railties/lib/generators/migration.rb +++ b/railties/lib/rails/generators/migration.rb diff --git a/railties/lib/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index cd7aa61b50..0e5976f915 100644 --- a/railties/lib/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -1,5 +1,5 @@ -require 'generators/base' -require 'generators/generated_attribute' +require 'rails/generators/base' +require 'rails/generators/generated_attribute' module Rails module Generators @@ -97,67 +97,5 @@ module Rails end end end - - # Deal with controller names on scaffold. Also provide helpers to deal with - # ActionORM. - # - module ScaffoldBase - def self.included(base) #:nodoc: - base.send :attr_reader, :controller_name, :controller_class_name, :controller_file_name, - :controller_class_path, :controller_file_path - end - - # Set controller variables on initialization. - # - def initialize(*args) #:nodoc: - super - @controller_name = name.pluralize - - base_name, @controller_class_path, @controller_file_path, class_nesting, class_nesting_depth = extract_modules(@controller_name) - class_name_without_nesting, @controller_file_name, controller_plural_name = inflect_names(base_name) - - @controller_class_name = if class_nesting.empty? - class_name_without_nesting - else - "#{class_nesting}::#{class_name_without_nesting}" - end - end - - protected - - # Loads the ORM::Generators::ActiveModel class. This class is responsable - # to tell scaffold entities how to generate an specific method for the - # ORM. Check Rails::Generators::ActiveModel for more information. - # - def orm_class - @orm_class ||= begin - # Raise an error if the class_option :orm was not defined. - unless self.class.class_options[:orm] - raise "You need to have :orm as class option to invoke orm_class and orm_instance" - end - - action_orm = "#{options[:orm].to_s.classify}::Generators::ActiveModel" - - # If the orm was not loaded, try to load it at "generators/orm", - # for example "generators/active_record" or "generators/sequel". - begin - klass = action_orm.constantize - rescue NameError - require "generators/#{options[:orm]}" - end - - # Try once again after loading the file with success. - klass ||= action_orm.constantize - rescue Exception => e - raise Error, "Could not load #{action_orm}, skipping controller. Error: #{e.message}." - end - end - - # Initialize ORM::Generators::ActiveModel to access instance methods. - # - def orm_instance(name=file_name) - @orm_instance ||= @orm_class.new(name) - end - end end end diff --git a/railties/lib/generators/rails/app/USAGE b/railties/lib/rails/generators/rails/app/USAGE index 36d6061a59..36d6061a59 100644 --- a/railties/lib/generators/rails/app/USAGE +++ b/railties/lib/rails/generators/rails/app/USAGE diff --git a/railties/lib/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 24c9a969f9..b2322f90b4 100644 --- a/railties/lib/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -56,6 +56,7 @@ module Rails::Generators def create_root_files copy_file "Rakefile" copy_file "README" + copy_file "config.ru" end def create_app_files @@ -114,7 +115,6 @@ module Rails::Generators def create_dispatch_files return unless options[:with_dispatchers] - copy_file "dispatchers/config.ru", "config.ru" template "dispatchers/dispatch.rb", "public/dispatch.rb" chmod "public/dispatch.rb", 0755, :verbose => false diff --git a/railties/lib/generators/rails/app/templates/README b/railties/lib/rails/generators/rails/app/templates/README index 37ec8ea211..37ec8ea211 100644 --- a/railties/lib/generators/rails/app/templates/README +++ b/railties/lib/rails/generators/rails/app/templates/README diff --git a/railties/lib/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile index 3bb0e8592a..bd4dec5389 100755 --- a/railties/lib/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 'tasks/rails' +require 'rails/tasks' diff --git a/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb index 6635a3f487..6635a3f487 100644 --- a/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb diff --git a/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb index 22a7940eb2..22a7940eb2 100644 --- a/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb diff --git a/railties/lib/generators/rails/app/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/app/models/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory diff --git a/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/.empty_directory diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru new file mode 100644 index 0000000000..50ee033d44 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -0,0 +1,5 @@ +# Require your environment file to bootstrap Rails +require ::File.dirname(__FILE__) + '/config/environment' + +# Dispatch the request +run Rails.application.new diff --git a/railties/lib/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb index dd5e3b6916..52086fbc7d 100644 --- a/railties/lib/generators/rails/app/templates/config/boot.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb @@ -4,6 +4,11 @@ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) module Rails + # Mark the version of Rails that generated the boot.rb file. This is + # a temporary solution and will most likely be removed as Rails 3.0 + # comes closer. + BOOTSTRAP_VERSION = "3.0" + class << self def boot! unless booted? @@ -36,23 +41,57 @@ module Rails class Boot def run load_initializer - Rails::Initializer.run(:set_load_path) + set_load_paths + end + + def set_load_paths + %w( + railties + railties/lib + activesupport/lib + actionpack/lib + activerecord/lib + actionmailer/lib + activeresource/lib + actionwebservice/lib + ).reverse_each do |path| + path = "#{framework_root_path}/#{path}" + $LOAD_PATH.unshift(path) if File.directory?(path) + $LOAD_PATH.uniq! + end + end + + def framework_root_path + defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{RAILS_ROOT}/vendor/rails" end end class VendorBoot < Boot def load_initializer - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" - Rails::Initializer.run(:install_gem_spec_stubs) + $:.unshift("#{framework_root_path}/railties/lib") + require "rails" + install_gem_spec_stubs Rails::GemDependency.add_frozen_gem_path end + + def install_gem_spec_stubs + begin; require "rubygems"; rescue LoadError; return; end + + %w(rails activesupport activerecord actionpack actionmailer activeresource).each do |stub| + Gem.loaded_specs[stub] ||= Gem::Specification.new do |s| + s.name = stub + s.version = Rails::VERSION::STRING + s.loaded_from = "" + end + end + end end class GemBoot < Boot def load_initializer self.class.load_rubygems load_rails_gem - require 'initializer' + require 'rails' end def load_rails_gem diff --git a/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml index c0c3588be1..c0c3588be1 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml diff --git a/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml index a9716ddb44..a9716ddb44 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml diff --git a/railties/lib/generators/rails/app/templates/config/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml index 6bf2f7b1fd..6bf2f7b1fd 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml diff --git a/railties/lib/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml index a1883f6256..a1883f6256 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/oracle.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml diff --git a/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml index f600e054cf..f600e054cf 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml diff --git a/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml index 025d62a8d8..025d62a8d8 100644 --- a/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml diff --git a/railties/lib/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb index adb3a3060a..adb3a3060a 100644 --- a/railties/lib/generators/rails/app/templates/config/environment.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb diff --git a/railties/lib/generators/rails/app/templates/config/environments/development.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb index 85c9a6080e..85c9a6080e 100644 --- a/railties/lib/generators/rails/app/templates/config/environments/development.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb diff --git a/railties/lib/generators/rails/app/templates/config/environments/production.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb index 1fc9f6b923..377b9207c7 100644 --- a/railties/lib/generators/rails/app/templates/config/environments/production.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb @@ -17,6 +17,10 @@ config.action_controller.perform_caching = true # Use a different cache store in production # config.cache_store = :mem_cache_store +# Disable Rails's static asset server +# In production, Apache or nginx will already do this +config.serve_static_assets = false + # Enable serving of images, stylesheets, and javascripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" @@ -24,4 +28,4 @@ config.action_controller.perform_caching = true # config.action_mailer.raise_delivery_errors = false # Enable threaded mode -# config.threadsafe!
\ No newline at end of file +# config.threadsafe! diff --git a/railties/lib/generators/rails/app/templates/config/environments/test.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb index 496eb9572b..496eb9572b 100644 --- a/railties/lib/generators/rails/app/templates/config/environments/test.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb diff --git a/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb index 839d4cde19..839d4cde19 100644 --- a/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb diff --git a/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb index d531b8bb82..d531b8bb82 100644 --- a/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb diff --git a/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb index 72aca7e441..72aca7e441 100644 --- a/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb diff --git a/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_rails_defaults.rb index 8ec3186c84..8ec3186c84 100644 --- a/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_rails_defaults.rb diff --git a/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt index 4499ab84b6..4499ab84b6 100644 --- a/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt diff --git a/railties/lib/generators/rails/app/templates/config/locales/en.yml b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml index f265c068d8..f265c068d8 100644 --- a/railties/lib/generators/rails/app/templates/config/locales/en.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml diff --git a/railties/lib/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index ea14ce1bfc..ea14ce1bfc 100644 --- a/railties/lib/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb diff --git a/railties/lib/generators/rails/app/templates/db/seeds.rb b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb index bc8695e6f0..bc8695e6f0 100644 --- a/railties/lib/generators/rails/app/templates/db/seeds.rb +++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb diff --git a/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi b/railties/lib/rails/generators/rails/app/templates/dispatchers/dispatch.fcgi index f5b3b71875..f5b3b71875 100755 --- a/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi +++ b/railties/lib/rails/generators/rails/app/templates/dispatchers/dispatch.fcgi diff --git a/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb b/railties/lib/rails/generators/rails/app/templates/dispatchers/dispatch.rb index 48e888113a..48e888113a 100755 --- a/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb +++ b/railties/lib/rails/generators/rails/app/templates/dispatchers/dispatch.rb diff --git a/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi b/railties/lib/rails/generators/rails/app/templates/dispatchers/gateway.cgi index bdc1055a22..bdc1055a22 100755 --- a/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi +++ b/railties/lib/rails/generators/rails/app/templates/dispatchers/gateway.cgi diff --git a/railties/lib/generators/rails/app/templates/doc/README_FOR_APP b/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP index fe41f5cc24..fe41f5cc24 100644 --- a/railties/lib/generators/rails/app/templates/doc/README_FOR_APP +++ b/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP diff --git a/railties/lib/generators/rails/app/templates/public/404.html b/railties/lib/rails/generators/rails/app/templates/public/404.html index 88ee108e90..88ee108e90 100644 --- a/railties/lib/generators/rails/app/templates/public/404.html +++ b/railties/lib/rails/generators/rails/app/templates/public/404.html diff --git a/railties/lib/generators/rails/app/templates/public/422.html b/railties/lib/rails/generators/rails/app/templates/public/422.html index 9c3c96670b..9c3c96670b 100644 --- a/railties/lib/generators/rails/app/templates/public/422.html +++ b/railties/lib/rails/generators/rails/app/templates/public/422.html diff --git a/railties/lib/generators/rails/app/templates/public/500.html b/railties/lib/rails/generators/rails/app/templates/public/500.html index f71c86e652..f71c86e652 100644 --- a/railties/lib/generators/rails/app/templates/public/500.html +++ b/railties/lib/rails/generators/rails/app/templates/public/500.html diff --git a/railties/lib/generators/rails/app/templates/public/favicon.ico b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/public/favicon.ico +++ b/railties/lib/rails/generators/rails/app/templates/public/favicon.ico diff --git a/railties/lib/generators/rails/app/templates/public/images/rails.png b/railties/lib/rails/generators/rails/app/templates/public/images/rails.png Binary files differindex d5edc04e65..d5edc04e65 100644 --- a/railties/lib/generators/rails/app/templates/public/images/rails.png +++ b/railties/lib/rails/generators/rails/app/templates/public/images/rails.png diff --git a/railties/lib/generators/rails/app/templates/public/index.html b/railties/lib/rails/generators/rails/app/templates/public/index.html index ff2dfd3193..ff2dfd3193 100644 --- a/railties/lib/generators/rails/app/templates/public/index.html +++ b/railties/lib/rails/generators/rails/app/templates/public/index.html diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/application.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js index fe4577696b..fe4577696b 100644 --- a/railties/lib/generators/rails/app/templates/public/javascripts/application.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/controls.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js index ca29aefdd1..ca29aefdd1 100644 --- a/railties/lib/generators/rails/app/templates/public/javascripts/controls.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js index 07229f986f..07229f986f 100644 --- a/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/effects.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js index 5a639d2dea..5a639d2dea 100644 --- a/railties/lib/generators/rails/app/templates/public/javascripts/effects.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js index dfe8ab4e13..dfe8ab4e13 100644 --- a/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js +++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js diff --git a/railties/lib/generators/rails/app/templates/public/robots.txt b/railties/lib/rails/generators/rails/app/templates/public/robots.txt index 085187fa58..085187fa58 100644 --- a/railties/lib/generators/rails/app/templates/public/robots.txt +++ b/railties/lib/rails/generators/rails/app/templates/public/robots.txt diff --git a/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory b/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory diff --git a/railties/lib/generators/rails/app/templates/script/about.tt b/railties/lib/rails/generators/rails/app/templates/script/about.tt index afbab8b533..1220676c08 100755 --- a/railties/lib/generators/rails/app/templates/script/about.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/about.tt @@ -1,4 +1,4 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) $LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" -require 'commands/about' +require 'rails/commands/about' diff --git a/railties/lib/generators/rails/app/templates/script/console.tt b/railties/lib/rails/generators/rails/app/templates/script/console.tt index 2f8b95c923..5aec193853 100755 --- a/railties/lib/generators/rails/app/templates/script/console.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/console.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/console' +require 'rails/commands/console' diff --git a/railties/lib/generators/rails/app/templates/script/dbconsole.tt b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt index 7dce6d16dd..632563f470 100755 --- a/railties/lib/generators/rails/app/templates/script/dbconsole.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/dbconsole' +require 'rails/commands/dbconsole' diff --git a/railties/lib/generators/rails/app/templates/script/destroy.tt b/railties/lib/rails/generators/rails/app/templates/script/destroy.tt index 053d3dea39..c5f94862c3 100755 --- a/railties/lib/generators/rails/app/templates/script/destroy.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/destroy.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/destroy' +require 'rails/commands/destroy' diff --git a/railties/lib/generators/rails/app/templates/script/generate.tt b/railties/lib/rails/generators/rails/app/templates/script/generate.tt index ea4a0efd43..d466c94767 100755 --- a/railties/lib/generators/rails/app/templates/script/generate.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/generate.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/generate' +require 'rails/commands/generate' diff --git a/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt b/railties/lib/rails/generators/rails/app/templates/script/performance/benchmarker.tt index da9df7fab9..3e03aaa767 100755 --- a/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/performance/benchmarker.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../../config/boot', __FILE__) -require 'commands/performance/benchmarker' +require 'rails/commands/performance/benchmarker' diff --git a/railties/lib/generators/rails/app/templates/script/performance/profiler.tt b/railties/lib/rails/generators/rails/app/templates/script/performance/profiler.tt index 5a0e2b0c28..deada2f561 100755 --- a/railties/lib/generators/rails/app/templates/script/performance/profiler.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/performance/profiler.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../../config/boot', __FILE__) -require 'commands/performance/profiler' +require 'rails/commands/performance/profiler' diff --git a/railties/lib/generators/rails/app/templates/script/plugin.tt b/railties/lib/rails/generators/rails/app/templates/script/plugin.tt index 68b2148bba..f4081f56b6 100755 --- a/railties/lib/generators/rails/app/templates/script/plugin.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/plugin.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/plugin' +require 'rails/commands/plugin' diff --git a/railties/lib/generators/rails/app/templates/script/runner.tt b/railties/lib/rails/generators/rails/app/templates/script/runner.tt index a2b313fa32..60c8c0bba1 100755 --- a/railties/lib/generators/rails/app/templates/script/runner.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/runner.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/runner' +require 'rails/commands/runner' diff --git a/railties/lib/generators/rails/app/templates/script/server.tt b/railties/lib/rails/generators/rails/app/templates/script/server.tt index c8868155f3..54e3346bec 100755 --- a/railties/lib/generators/rails/app/templates/script/server.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/server.tt @@ -1,3 +1,3 @@ <%= shebang %> require File.expand_path('../../config/boot', __FILE__) -require 'commands/server' +require 'rails/commands/server' diff --git a/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory diff --git a/railties/lib/generators/rails/app/templates/test/functional/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/test/functional/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory diff --git a/railties/lib/generators/rails/app/templates/test/integration/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/test/integration/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory diff --git a/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb index 4b60558b43..4b60558b43 100644 --- a/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb diff --git a/railties/lib/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb index b9fe2517c8..a16f587d8b 100644 --- a/railties/lib/generators/rails/app/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb @@ -1,6 +1,6 @@ ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") -require 'test_help' +require 'rails/test_help' class ActiveSupport::TestCase # Transactional fixtures accelerate your tests by wrapping each test method diff --git a/railties/lib/generators/rails/app/templates/test/unit/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/app/templates/test/unit/.empty_directory +++ b/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory diff --git a/railties/lib/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE index 6ed4b2edfc..6ed4b2edfc 100644 --- a/railties/lib/generators/rails/controller/USAGE +++ b/railties/lib/rails/generators/rails/controller/USAGE diff --git a/railties/lib/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index 91470be833..91470be833 100644 --- a/railties/lib/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb diff --git a/railties/lib/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb index cda2659e69..cda2659e69 100644 --- a/railties/lib/generators/rails/controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb diff --git a/railties/lib/generators/rails/generator/USAGE b/railties/lib/rails/generators/rails/generator/USAGE index ca7d3f62d0..ca7d3f62d0 100644 --- a/railties/lib/generators/rails/generator/USAGE +++ b/railties/lib/rails/generators/rails/generator/USAGE diff --git a/railties/lib/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb index 2fc97b20d3..5b5d1884bc 100644 --- a/railties/lib/generators/rails/generator/generator_generator.rb +++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb @@ -6,7 +6,7 @@ module Rails class_option :namespace, :type => :boolean, :default => true, :desc => "Namespace generator under lib/generators/name" - def craete_generator_files + def create_generator_files directory '.', generator_dir end diff --git a/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt index 675f00043f..675f00043f 100644 --- a/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt +++ b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt diff --git a/railties/lib/generators/rails/generator/templates/USAGE.tt b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt index ea9f4f12cc..ea9f4f12cc 100644 --- a/railties/lib/generators/rails/generator/templates/USAGE.tt +++ b/railties/lib/rails/generators/rails/generator/templates/USAGE.tt diff --git a/railties/lib/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/generators/rails/generator/templates/templates/.empty_directory +++ b/railties/lib/rails/generators/rails/generator/templates/templates/.empty_directory diff --git a/railties/lib/generators/rails/helper/USAGE b/railties/lib/rails/generators/rails/helper/USAGE index 531c9b390a..531c9b390a 100644 --- a/railties/lib/generators/rails/helper/USAGE +++ b/railties/lib/rails/generators/rails/helper/USAGE diff --git a/railties/lib/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb index ad66388591..ad66388591 100644 --- a/railties/lib/generators/rails/helper/helper_generator.rb +++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb diff --git a/railties/lib/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb index 3fe2ecdc74..3fe2ecdc74 100644 --- a/railties/lib/generators/rails/helper/templates/helper.rb +++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb diff --git a/railties/lib/generators/rails/integration_test/USAGE b/railties/lib/rails/generators/rails/integration_test/USAGE index b76c35a702..b76c35a702 100644 --- a/railties/lib/generators/rails/integration_test/USAGE +++ b/railties/lib/rails/generators/rails/integration_test/USAGE diff --git a/railties/lib/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb index 363a327fcb..363a327fcb 100644 --- a/railties/lib/generators/rails/integration_test/integration_test_generator.rb +++ b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb diff --git a/railties/lib/generators/rails/mailer/USAGE b/railties/lib/rails/generators/rails/mailer/USAGE index c56095b2c8..c56095b2c8 100644 --- a/railties/lib/generators/rails/mailer/USAGE +++ b/railties/lib/rails/generators/rails/mailer/USAGE diff --git a/railties/lib/generators/rails/mailer/mailer_generator.rb b/railties/lib/rails/generators/rails/mailer/mailer_generator.rb index 33f1665b83..33f1665b83 100644 --- a/railties/lib/generators/rails/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/rails/mailer/mailer_generator.rb diff --git a/railties/lib/generators/rails/mailer/templates/mailer.rb b/railties/lib/rails/generators/rails/mailer/templates/mailer.rb index 90e0b712d6..90e0b712d6 100644 --- a/railties/lib/generators/rails/mailer/templates/mailer.rb +++ b/railties/lib/rails/generators/rails/mailer/templates/mailer.rb diff --git a/railties/lib/generators/rails/metal/USAGE b/railties/lib/rails/generators/rails/metal/USAGE index 123ec6c03f..123ec6c03f 100644 --- a/railties/lib/generators/rails/metal/USAGE +++ b/railties/lib/rails/generators/rails/metal/USAGE diff --git a/railties/lib/generators/rails/metal/metal_generator.rb b/railties/lib/rails/generators/rails/metal/metal_generator.rb index fe4f945cad..fe4f945cad 100644 --- a/railties/lib/generators/rails/metal/metal_generator.rb +++ b/railties/lib/rails/generators/rails/metal/metal_generator.rb diff --git a/railties/lib/generators/rails/metal/templates/metal.rb b/railties/lib/rails/generators/rails/metal/templates/metal.rb index e94982b69a..e94982b69a 100644 --- a/railties/lib/generators/rails/metal/templates/metal.rb +++ b/railties/lib/rails/generators/rails/metal/templates/metal.rb diff --git a/railties/lib/generators/rails/migration/USAGE b/railties/lib/rails/generators/rails/migration/USAGE index d91127aac3..d91127aac3 100644 --- a/railties/lib/generators/rails/migration/USAGE +++ b/railties/lib/rails/generators/rails/migration/USAGE diff --git a/railties/lib/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb index 39fa5b63b1..39fa5b63b1 100644 --- a/railties/lib/generators/rails/migration/migration_generator.rb +++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb diff --git a/railties/lib/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE index b056d5df8b..b056d5df8b 100644 --- a/railties/lib/generators/rails/model/USAGE +++ b/railties/lib/rails/generators/rails/model/USAGE diff --git a/railties/lib/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb index 629d5eed3f..629d5eed3f 100644 --- a/railties/lib/generators/rails/model/model_generator.rb +++ b/railties/lib/rails/generators/rails/model/model_generator.rb diff --git a/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb b/railties/lib/rails/generators/rails/model_subclass/model_subclass_generator.rb index 4649709780..4649709780 100644 --- a/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb +++ b/railties/lib/rails/generators/rails/model_subclass/model_subclass_generator.rb diff --git a/railties/lib/generators/rails/observer/USAGE b/railties/lib/rails/generators/rails/observer/USAGE index 9a20f55a89..9a20f55a89 100644 --- a/railties/lib/generators/rails/observer/USAGE +++ b/railties/lib/rails/generators/rails/observer/USAGE diff --git a/railties/lib/generators/rails/observer/observer_generator.rb b/railties/lib/rails/generators/rails/observer/observer_generator.rb index f5cedee91f..f5cedee91f 100644 --- a/railties/lib/generators/rails/observer/observer_generator.rb +++ b/railties/lib/rails/generators/rails/observer/observer_generator.rb diff --git a/railties/lib/generators/rails/performance_test/USAGE b/railties/lib/rails/generators/rails/performance_test/USAGE index ee82578cdb..ee82578cdb 100644 --- a/railties/lib/generators/rails/performance_test/USAGE +++ b/railties/lib/rails/generators/rails/performance_test/USAGE diff --git a/railties/lib/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb index d1c71ab8ed..d1c71ab8ed 100644 --- a/railties/lib/generators/rails/performance_test/performance_test_generator.rb +++ b/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb diff --git a/railties/lib/generators/rails/plugin/USAGE b/railties/lib/rails/generators/rails/plugin/USAGE index 8a17fa4dec..8a17fa4dec 100644 --- a/railties/lib/generators/rails/plugin/USAGE +++ b/railties/lib/rails/generators/rails/plugin/USAGE diff --git a/railties/lib/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index ee785caf7d..bc614bc5d3 100644 --- a/railties/lib/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -1,4 +1,4 @@ -require 'generators/rails/generator/generator_generator' +require 'rails/generators/rails/generator/generator_generator' module Rails module Generators diff --git a/railties/lib/generators/rails/plugin/templates/MIT-LICENSE b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE index 8717df053d..8717df053d 100644 --- a/railties/lib/generators/rails/plugin/templates/MIT-LICENSE +++ b/railties/lib/rails/generators/rails/plugin/templates/MIT-LICENSE diff --git a/railties/lib/generators/rails/plugin/templates/README b/railties/lib/rails/generators/rails/plugin/templates/README index 702db07cb1..702db07cb1 100644 --- a/railties/lib/generators/rails/plugin/templates/README +++ b/railties/lib/rails/generators/rails/plugin/templates/README diff --git a/railties/lib/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile index 85e8ff1834..85e8ff1834 100644 --- a/railties/lib/generators/rails/plugin/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile diff --git a/railties/lib/generators/rails/plugin/templates/init.rb b/railties/lib/rails/generators/rails/plugin/templates/init.rb index 3c19a743c9..3c19a743c9 100644 --- a/railties/lib/generators/rails/plugin/templates/init.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/init.rb diff --git a/railties/lib/generators/rails/plugin/templates/install.rb b/railties/lib/rails/generators/rails/plugin/templates/install.rb index f7732d3796..f7732d3796 100644 --- a/railties/lib/generators/rails/plugin/templates/install.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/install.rb diff --git a/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt b/railties/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt index d8d908a959..d8d908a959 100644 --- a/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt diff --git a/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt b/railties/lib/rails/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt index 72920a9d3a..72920a9d3a 100644 --- a/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt +++ b/railties/lib/rails/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt diff --git a/railties/lib/generators/rails/plugin/templates/uninstall.rb b/railties/lib/rails/generators/rails/plugin/templates/uninstall.rb index 9738333463..9738333463 100644 --- a/railties/lib/generators/rails/plugin/templates/uninstall.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/uninstall.rb diff --git a/railties/lib/generators/rails/resource/USAGE b/railties/lib/rails/generators/rails/resource/USAGE index 936619b0db..936619b0db 100644 --- a/railties/lib/generators/rails/resource/USAGE +++ b/railties/lib/rails/generators/rails/resource/USAGE diff --git a/railties/lib/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index 70babc0550..e49f9aea1b 100644 --- a/railties/lib/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -1,25 +1,19 @@ -require 'generators/rails/model/model_generator' +require 'rails/generators/rails/model/model_generator' +require 'rails/generators/resource_helpers' module Rails module Generators class ResourceGenerator < ModelGenerator #metagenerator + include ResourceHelpers + hook_for :resource_controller, :required => true do |base, controller| - base.invoke controller, [ base.name.pluralize, base.options[:actions] ] + base.invoke controller, [ base.controller_name, base.options[:actions] ] end class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [], :desc => "Actions for the resource controller" - class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" - class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName" - - def initialize(*args) - super - if name == name.pluralize && !options[:force_plural] - say "Plural version of the model detected, using singularized version. Override with --force-plural." - name.replace name.singularize - end - end + 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)}" diff --git a/railties/lib/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE index 71edd2f469..71edd2f469 100644 --- a/railties/lib/generators/rails/scaffold/USAGE +++ b/railties/lib/rails/generators/rails/scaffold/USAGE diff --git a/railties/lib/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb index af44c8ba65..779f933785 100644 --- a/railties/lib/generators/rails/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -1,9 +1,10 @@ -require 'generators/rails/resource/resource_generator' +require 'rails/generators/rails/resource/resource_generator' module Rails module Generators class ScaffoldGenerator < ResourceGenerator #metagenerator - remove_hook_for :actions, :resource_controller + remove_hook_for :resource_controller + remove_class_option :actions hook_for :scaffold_controller, :required => true hook_for :stylesheets diff --git a/railties/lib/generators/rails/scaffold_controller/USAGE b/railties/lib/rails/generators/rails/scaffold_controller/USAGE index d60a3c3680..d60a3c3680 100644 --- a/railties/lib/generators/rails/scaffold_controller/USAGE +++ b/railties/lib/rails/generators/rails/scaffold_controller/USAGE diff --git a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb index 972be5a33b..e544e29892 100644 --- a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -1,8 +1,9 @@ +require 'rails/generators/resource_helpers' + module Rails module Generators class ScaffoldControllerGenerator < NamedBase - # Add controller methods and ActionORM settings. - include ScaffoldBase + include ResourceHelpers check_class_collision :suffix => "Controller" diff --git a/railties/lib/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index 3cc8bbf8e7..3cc8bbf8e7 100644 --- a/railties/lib/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb diff --git a/railties/lib/generators/rails/session_migration/USAGE b/railties/lib/rails/generators/rails/session_migration/USAGE index e106f6ecc8..e106f6ecc8 100644 --- a/railties/lib/generators/rails/session_migration/USAGE +++ b/railties/lib/rails/generators/rails/session_migration/USAGE diff --git a/railties/lib/generators/rails/session_migration/session_migration_generator.rb b/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb index 258cc5b4a0..258cc5b4a0 100644 --- a/railties/lib/generators/rails/session_migration/session_migration_generator.rb +++ b/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb diff --git a/railties/lib/generators/rails/stylesheets/USAGE b/railties/lib/rails/generators/rails/stylesheets/USAGE index d6a81e51d0..d6a81e51d0 100644 --- a/railties/lib/generators/rails/stylesheets/USAGE +++ b/railties/lib/rails/generators/rails/stylesheets/USAGE diff --git a/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb index ce68443c39..ce68443c39 100644 --- a/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb +++ b/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb diff --git a/railties/lib/generators/rails/stylesheets/templates/scaffold.css b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css index 093c20994d..d9fa2cf2dc 100644 --- a/railties/lib/generators/rails/stylesheets/templates/scaffold.css +++ b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css @@ -16,6 +16,14 @@ a { color: #000; } a:visited { color: #666; } a:hover { color: #fff; background-color:#000; } +div.field, div.actions { + margin-bottom: 10px; +} + +.notice { + color: green; +} + .fieldWithErrors { padding: 2px; background-color: red; diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb new file mode 100644 index 0000000000..d4b0d4b945 --- /dev/null +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -0,0 +1,74 @@ +module Rails + module Generators + # Deal with controller names on scaffold and add some helpers to deal with + # ActiveModel. + # + module ResourceHelpers + def self.included(base) #:nodoc: + base.send :attr_reader, :controller_name, :controller_class_name, :controller_file_name, + :controller_class_path, :controller_file_path + + base.send :class_option, :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName" + end + + # Set controller variables on initialization. + # + def initialize(*args) #:nodoc: + super + + if name == name.pluralize && !options[:force_plural] + say "Plural version of the model detected, using singularized version. Override with --force-plural." + name.replace name.singularize + assign_names!(self.name) + end + + @controller_name = name.pluralize + + base_name, @controller_class_path, @controller_file_path, class_nesting, class_nesting_depth = extract_modules(@controller_name) + class_name_without_nesting, @controller_file_name, controller_plural_name = inflect_names(base_name) + + @controller_class_name = if class_nesting.empty? + class_name_without_nesting + else + "#{class_nesting}::#{class_name_without_nesting}" + end + end + + protected + + # Loads the ORM::Generators::ActiveModel class. This class is responsable + # to tell scaffold entities how to generate an specific method for the + # ORM. Check Rails::Generators::ActiveModel for more information. + # + def orm_class + @orm_class ||= begin + # Raise an error if the class_option :orm was not defined. + unless self.class.class_options[:orm] + raise "You need to have :orm as class option to invoke orm_class and orm_instance" + end + + active_model = "#{options[:orm].to_s.classify}::Generators::ActiveModel" + + # If the orm was not loaded, try to load it at "generators/orm", + # for example "generators/active_record" or "generators/sequel". + begin + klass = active_model.constantize + rescue NameError + require "rails/generators/#{options[:orm]}" + end + + # Try once again after loading the file with success. + klass ||= active_model.constantize + rescue Exception => e + raise Error, "Could not load #{active_model}, skipping controller. Error: #{e.message}." + end + end + + # Initialize ORM::Generators::ActiveModel to access instance methods. + # + def orm_instance(name=file_name) + @orm_instance ||= @orm_class.new(name) + end + end + end +end diff --git a/railties/lib/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb index 2763feb017..62b9afaa2c 100644 --- a/railties/lib/generators/test_unit.rb +++ b/railties/lib/rails/generators/test_unit.rb @@ -1,4 +1,4 @@ -require 'generators/named_base' +require 'rails/generators/named_base' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb index b57a6e794f..39816d9990 100644 --- a/railties/lib/generators/test_unit/controller/controller_generator.rb +++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb index 62fa5d86fd..62fa5d86fd 100644 --- a/railties/lib/generators/test_unit/controller/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb diff --git a/railties/lib/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb index 9ecfaa45ab..4ea80bf7be 100644 --- a/railties/lib/generators/test_unit/helper/helper_generator.rb +++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/helper/templates/helper_test.rb b/railties/lib/rails/generators/test_unit/helper/templates/helper_test.rb index 591e40900e..591e40900e 100644 --- a/railties/lib/generators/test_unit/helper/templates/helper_test.rb +++ b/railties/lib/rails/generators/test_unit/helper/templates/helper_test.rb diff --git a/railties/lib/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb index d9d9b3bf1d..32d0fac029 100644 --- a/railties/lib/generators/test_unit/integration/integration_generator.rb +++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb index 2c57158b1c..2c57158b1c 100644 --- a/railties/lib/generators/test_unit/integration/templates/integration_test.rb +++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb diff --git a/railties/lib/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb index ef350a6224..7353e5d61a 100644 --- a/railties/lib/generators/test_unit/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/mailer/templates/fixture b/railties/lib/rails/generators/test_unit/mailer/templates/fixture index fcce7bd805..fcce7bd805 100644 --- a/railties/lib/generators/test_unit/mailer/templates/fixture +++ b/railties/lib/rails/generators/test_unit/mailer/templates/fixture diff --git a/railties/lib/generators/test_unit/mailer/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/unit_test.rb index 4de94076e9..4de94076e9 100644 --- a/railties/lib/generators/test_unit/mailer/templates/unit_test.rb +++ b/railties/lib/rails/generators/test_unit/mailer/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb index 469306e6c5..609b815683 100644 --- a/railties/lib/generators/test_unit/model/model_generator.rb +++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml index c21035113e..c21035113e 100644 --- a/railties/lib/generators/test_unit/model/templates/fixtures.yml +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml diff --git a/railties/lib/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb index 3e0bc29d3a..3e0bc29d3a 100644 --- a/railties/lib/generators/test_unit/model/templates/unit_test.rb +++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/observer/observer_generator.rb b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb index 14181f4e49..6cc1158c21 100644 --- a/railties/lib/generators/test_unit/observer/observer_generator.rb +++ b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/observer/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb index 03f6d5666e..03f6d5666e 100644 --- a/railties/lib/generators/test_unit/observer/templates/unit_test.rb +++ b/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/performance/performance_generator.rb b/railties/lib/rails/generators/test_unit/performance/performance_generator.rb index 0d9c646b26..99edda5461 100644 --- a/railties/lib/generators/test_unit/performance/performance_generator.rb +++ b/railties/lib/rails/generators/test_unit/performance/performance_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb index 27c91b0fca..27c91b0fca 100644 --- a/railties/lib/generators/test_unit/performance/templates/performance_test.rb +++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb diff --git a/railties/lib/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb index 05adf58c4f..4d65cd7d89 100644 --- a/railties/lib/generators/test_unit/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb @@ -1,4 +1,4 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' module TestUnit module Generators diff --git a/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt index 3e0bc29d3a..3e0bc29d3a 100644 --- a/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +++ b/railties/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt diff --git a/railties/lib/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb index 348ec33582..348ec33582 100644 --- a/railties/lib/generators/test_unit/plugin/templates/test_helper.rb +++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb diff --git a/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb index 78fcea1e9c..c0315c7fe6 100644 --- a/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -1,9 +1,10 @@ -require 'generators/test_unit' +require 'rails/generators/test_unit' +require 'rails/generators/resource_helpers' module TestUnit module Generators class ScaffoldGenerator < Base - include Rails::Generators::ScaffoldBase + include Rails::Generators::ResourceHelpers class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" check_class_collision :suffix => "ControllerTest" diff --git a/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb index e4bf4035da..e4bf4035da 100644 --- a/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb diff --git a/railties/lib/initializer.rb b/railties/lib/rails/initializer.rb index 336bff9534..2d63ac4d39 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/rails/initializer.rb @@ -1,7 +1,7 @@ require "pathname" -$LOAD_PATH.unshift File.dirname(__FILE__) -require 'railties_path' +require 'rails/application' +require 'rails/railties_path' require 'rails/version' require 'rails/gem_dependency' require 'rails/rack' @@ -30,7 +30,7 @@ module Rails end def config - @@config + @@config || Configuration.new end alias configuration config @@ -104,59 +104,39 @@ module Rails end def self.run(initializer = nil, config = nil) - default.config = config if config - default.config ||= Configuration.new - yield default.config if block_given? - default.run(initializer) + # TODO: Clean this all up + if initializer + default.config = config + default.run(initializer) + else + Rails.application = Class.new(Application) + yield Rails.application.config if block_given? + default.config = Rails.application.config + default.run + end end end # 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.default.add :check_ruby_version do - require 'ruby_version_check' + require 'rails/ruby_version_check' end - # If Rails is vendored and RubyGems is available, install stub GemSpecs - # for Rails, Active Support, Active Record, Action Pack, Action Mailer, and - # Active Resource. This allows Gem plugins to depend on Rails even when - # the Gem version of Rails shouldn't be loaded. - Initializer.default.add :install_gem_spec_stubs do - unless Rails.respond_to?(:vendor_rails?) + # Bail if boot.rb is outdated + Initializer.default.add :freak_out_if_boot_rb_is_outdated do + unless defined?(Rails::BOOTSTRAP_VERSION) abort %{Your config/boot.rb is outdated: Run "rake rails:update".} end - - if Rails.vendor_rails? - begin; require "rubygems"; rescue LoadError; return; end - - %w(rails activesupport activerecord actionpack actionmailer activeresource).each do |stub| - Gem.loaded_specs[stub] ||= Gem::Specification.new do |s| - s.name = stub - s.version = Rails::VERSION::STRING - s.loaded_from = "" - end - end - end end # Set the <tt>$LOAD_PATH</tt> based on the value of # Configuration#load_paths. Duplicates are removed. Initializer.default.add :set_load_path do - # TODO: Think about unifying this with the general Rails paths - configuration.framework_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) } configuration.paths.add_to_load_path $LOAD_PATH.uniq! end - Initializer.default.add :add_gem_load_paths do - require 'rails/gem_dependency' - Rails::GemDependency.add_frozen_gem_path - unless config.gems.empty? - require "rubygems" - config.gems.each { |gem| gem.add_load_paths } - end - end - # Requires all frameworks specified by the Configuration#frameworks # list. By default, all frameworks (Active Record, Active Support, # Action Pack, Action Mailer, and Active Resource) are loaded. @@ -201,6 +181,13 @@ module Rails plugin_loader.add_plugin_load_paths end + # Create tmp directories + Initializer.default.add :ensure_tmp_directories_exist do + %w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(File.join(configuration.root_path, 'tmp', dir_to_make)) + end + end + # Loads the environment specified by Configuration#environment_path, which # is typically one of development, test, or production. Initializer.default.add :load_environment do @@ -221,6 +208,15 @@ module Rails end end + Initializer.default.add :add_gem_load_paths do + require 'rails/gem_dependency' + Rails::GemDependency.add_frozen_gem_path + unless config.gems.empty? + require "rubygems" + config.gems.each { |gem| gem.add_load_paths } + 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. @@ -259,6 +255,25 @@ module Rails end end + # Include middleware to serve up static assets + Initializer.default.add :initialize_static_server do + if configuration.frameworks.include?(:action_controller) && configuration.serve_static_assets + configuration.middleware.use(ActionDispatch::Static, Rails.public_path) + end + end + + Initializer.default.add :initialize_middleware_stack do + if configuration.frameworks.include?(:action_controller) + configuration.middleware.use(::Rack::Lock) unless ActionController::Base.allow_concurrency + configuration.middleware.use(ActionDispatch::ShowExceptions, ActionController::Base.consider_all_requests_local) + configuration.middleware.use(ActionDispatch::Callbacks, ActionController::Dispatcher.prepare_each_request) + configuration.middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) + configuration.middleware.use(ActionDispatch::ParamsParser) + configuration.middleware.use(::Rack::MethodOverride) + configuration.middleware.use(::Rack::Head) + end + end + Initializer.default.add :initialize_cache do unless defined?(RAILS_CACHE) silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) } @@ -446,18 +461,6 @@ Run `rake gems:build` to build the unbuilt gems. plugin_loader.load_plugins end - # - # # pick up any gems that plugins depend on - Initializer.default.add :add_gem_load_paths do - require 'rails/gem_dependency' - # TODO: This seems extraneous - Rails::GemDependency.add_frozen_gem_path - unless config.gems.empty? - require "rubygems" - config.gems.each { |gem| gem.add_load_paths } - end - end - # TODO: Figure out if this needs to run a second time # load_gems @@ -507,7 +510,7 @@ Run `rake gems:install` to install the missing gems. # # Setup database middleware after initializers have run Initializer.default.add :initialize_database_middleware do if configuration.frameworks.include?(:active_record) - if configuration.frameworks.include?(:action_controller) && + if configuration.frameworks.include?(:action_controller) && ActionController::Base.session_store && ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache @@ -523,7 +526,7 @@ Run `rake gems:install` to install the missing gems. # # Prepare dispatcher callbacks and run 'prepare' callbacks Initializer.default.add :prepare_dispatcher do next unless configuration.frameworks.include?(:action_controller) - require 'dispatcher' unless defined?(::Dispatcher) + require 'rails/dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) end diff --git a/railties/lib/initializer_old.rb b/railties/lib/rails/initializer_old.rb index cee5c7bcb6..cee5c7bcb6 100644 --- a/railties/lib/initializer_old.rb +++ b/railties/lib/rails/initializer_old.rb diff --git a/railties/lib/performance_test_help.rb b/railties/lib/rails/performance_test_help.rb index 2aaa21e85f..4ac38981d0 100644 --- a/railties/lib/performance_test_help.rb +++ b/railties/lib/rails/performance_test_help.rb @@ -1,5 +1,3 @@ -require 'action_controller/testing/performance_test' - ActionController::Base.perform_caching = true ActiveSupport::Dependencies.mechanism = :require Rails.logger.level = ActiveSupport::BufferedLogger::INFO diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 49ec5c7fba..1c0af6411a 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -71,6 +71,10 @@ module Rails File.exist?(routing_file) end + # Returns true if there is any localization file in locale_path + def localized? + locale_files.any? + end def view_path File.join(directory, 'app', 'views') @@ -87,6 +91,14 @@ module Rails def routing_file File.join(directory, 'config', 'routes.rb') end + + def locale_path + File.join(directory, 'config', 'locales') + end + + def locale_files + Dir[ File.join(locale_path, '*.{rb,yml}') ] + end private diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb index 7ea9c7c0f3..0d16cbd7c3 100644 --- a/railties/lib/rails/plugin/loader.rb +++ b/railties/lib/rails/plugin/loader.rb @@ -73,6 +73,7 @@ module Rails def configure_engines if engines.any? add_engine_routing_configurations + add_engine_locales add_engine_controller_paths add_engine_view_paths end @@ -84,6 +85,14 @@ module Rails end end + def add_engine_locales + localized_engines = engines.select { |engine| engine.localized? } + + # reverse it such that the last engine can overwrite translations from the first, like with routes + locale_files = localized_engines.collect { |engine| engine.locale_files }.reverse.flatten + I18n.load_path += locale_files - I18n.load_path + end + def add_engine_controller_paths ActionController::Routing.controller_paths += engines.collect {|engine| engine.controller_path } end diff --git a/railties/lib/rails/rack/static.rb b/railties/lib/rails/rack/static.rb index f07c6beb5e..d6b8face27 100644 --- a/railties/lib/rails/rack/static.rb +++ b/railties/lib/rails/rack/static.rb @@ -1,46 +1,5 @@ -require 'rack/utils' +require 'action_dispatch' -module Rails - module Rack - class Static - FILE_METHODS = %w(GET HEAD).freeze - - def initialize(app) - @app = app - @file_server = ::Rack::File.new(File.join(RAILS_ROOT, "public")) - end - - def call(env) - path = env['PATH_INFO'].chomp('/') - method = env['REQUEST_METHOD'] - - if FILE_METHODS.include?(method) - if file_exist?(path) - return @file_server.call(env) - else - cached_path = directory_exist?(path) ? "#{path}/index" : path - cached_path += ::ActionController::Base.page_cache_extension - - if file_exist?(cached_path) - env['PATH_INFO'] = cached_path - return @file_server.call(env) - end - end - end - - @app.call(env) - end - - private - def file_exist?(path) - full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) - File.file?(full_path) && File.readable?(full_path) - end - - def directory_exist?(path) - full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) - File.directory?(full_path) && File.readable?(full_path) - end - end - end +module Rails::Rack + Static = Deprecation::DeprecatedConstantProxy.new('Rails::Rack::Static', ActionDispatch::Static) end diff --git a/railties/lib/railties_path.rb b/railties/lib/rails/railties_path.rb index b729c095c8..e291fc23ea 100644 --- a/railties/lib/railties_path.rb +++ b/railties/lib/rails/railties_path.rb @@ -1 +1 @@ -RAILTIES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..')) +RAILTIES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) diff --git a/railties/lib/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb index 68d3acc876..68d3acc876 100644 --- a/railties/lib/ruby_version_check.rb +++ b/railties/lib/rails/ruby_version_check.rb diff --git a/railties/lib/rubyprof_ext.rb b/railties/lib/rails/rubyprof_ext.rb index f6e90357ce..f6e90357ce 100644 --- a/railties/lib/rubyprof_ext.rb +++ b/railties/lib/rails/rubyprof_ext.rb diff --git a/railties/lib/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 591fd6f6bd..591fd6f6bd 100644 --- a/railties/lib/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb diff --git a/railties/lib/tasks/rails.rb b/railties/lib/rails/tasks.rb index 8c2b7f9bde..aad965306c 100644 --- a/railties/lib/tasks/rails.rb +++ b/railties/lib/rails/tasks.rb @@ -1,8 +1,24 @@ $VERBOSE = nil # Load Rails rakefile extensions -Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext } +%w( + annotations + databases + documentation + framework + gems + log + middleware + misc + routes + statistics + testing + tmp +).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/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake index 48ac40099a..79973b3145 100644 --- a/railties/lib/tasks/annotations.rake +++ b/railties/lib/rails/tasks/annotations.rake @@ -1,4 +1,4 @@ -require 'source_annotation_extractor' +require 'rails/source_annotation_extractor' desc "Enumerate all annotations" task :notes do diff --git a/railties/lib/tasks/databases.rake b/railties/lib/rails/tasks/databases.rake index 687bc00b3c..ed015e7a67 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/rails/tasks/databases.rake @@ -55,7 +55,7 @@ namespace :db do case config['adapter'] when 'mysql' @charset = ENV['CHARSET'] || 'utf8' - @collation = ENV['COLLATION'] || 'utf8_general_ci' + @collation = ENV['COLLATION'] || 'utf8_unicode_ci' creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)} begin ActiveRecord::Base.establish_connection(config.merge('database' => nil)) @@ -426,8 +426,8 @@ namespace :db do desc "Creates a sessions migration for use with ActiveRecord::SessionStore" task :create => :environment do raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations? - require 'generators' - require 'generators/rails/session_migration/session_migration_generator' + require 'rails/generators' + require 'rails/generators/rails/session_migration/session_migration_generator' Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ] end diff --git a/railties/lib/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake index db1939c45f..db1939c45f 100644 --- a/railties/lib/tasks/documentation.rake +++ b/railties/lib/rails/tasks/documentation.rake diff --git a/railties/lib/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index a084af7e11..17e16f26fd 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -93,7 +93,7 @@ namespace :rails do namespace :update do def invoke_from_app_generator(method) require 'generators' - require 'generators/rails/app/app_generator' + require 'rails/generators/rails/app/app_generator' generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true }, :destination_root => RAILS_ROOT diff --git a/railties/lib/tasks/gems.rake b/railties/lib/rails/tasks/gems.rake index f1c34c7cca..f1c34c7cca 100644 --- a/railties/lib/tasks/gems.rake +++ b/railties/lib/rails/tasks/gems.rake diff --git a/railties/lib/tasks/log.rake b/railties/lib/rails/tasks/log.rake index 6e1334692e..6e1334692e 100644 --- a/railties/lib/tasks/log.rake +++ b/railties/lib/rails/tasks/log.rake diff --git a/railties/lib/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake index 05f159184e..e1ab309157 100644 --- a/railties/lib/tasks/middleware.rake +++ b/railties/lib/rails/tasks/middleware.rake @@ -1,7 +1,7 @@ desc 'Prints out your Rack middleware stack' task :middleware => :environment do - ActionController::Dispatcher.middleware.active.each do |middleware| + Rails.configuration.middleware.active.each do |middleware| puts "use #{middleware.inspect}" end - puts "run ActionController::Dispatcher.new" + puts "run ActionController::Routing::Routes" end diff --git a/railties/lib/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake index fb2fc31dc1..fb2fc31dc1 100644 --- a/railties/lib/tasks/misc.rake +++ b/railties/lib/rails/tasks/misc.rake diff --git a/railties/lib/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake index abbf3258c1..abbf3258c1 100644 --- a/railties/lib/tasks/routes.rake +++ b/railties/lib/rails/tasks/routes.rake diff --git a/railties/lib/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake index 5ab27a0f62..2dcc7bdf9d 100644 --- a/railties/lib/tasks/statistics.rake +++ b/railties/lib/rails/tasks/statistics.rake @@ -12,6 +12,6 @@ STATS_DIRECTORIES = [ desc "Report code statistics (KLOCs, etc) from the application" task :stats do - require 'code_statistics' + require 'rails/code_statistics' CodeStatistics.new(*STATS_DIRECTORIES).to_s end diff --git a/railties/lib/tasks/testing.rake b/railties/lib/rails/tasks/testing.rake index fd5e52a05b..fd5e52a05b 100644 --- a/railties/lib/tasks/testing.rake +++ b/railties/lib/rails/tasks/testing.rake diff --git a/railties/lib/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake index fea15058bb..fea15058bb 100644 --- a/railties/lib/tasks/tmp.rake +++ b/railties/lib/rails/tasks/tmp.rake diff --git a/railties/lib/test_help.rb b/railties/lib/rails/test_help.rb index 4318203a42..8bd4475c7b 100644 --- a/railties/lib/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -2,11 +2,13 @@ # so fixtures are loaded to the right database silence_warnings { RAILS_ENV = "test" } +require 'rubygems' +gem "rack", "~> 1.0.0" +gem "rack-test", "~> 0.5.0" + require 'test/unit' require 'active_support/core_ext/kernel/requires' -require 'action_controller/testing/test_case' require 'action_view/test_case' -require 'action_controller/testing/integration' require 'action_mailer/test_case' if defined?(ActionMailer) if defined?(ActiveRecord) diff --git a/railties/lib/vendor/bundler/LICENSE b/railties/lib/rails/vendor/bundler/LICENSE index 41decca113..41decca113 100644 --- a/railties/lib/vendor/bundler/LICENSE +++ b/railties/lib/rails/vendor/bundler/LICENSE diff --git a/railties/lib/rails/vendor/bundler/README.markdown b/railties/lib/rails/vendor/bundler/README.markdown new file mode 100644 index 0000000000..26863e96f2 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/README.markdown @@ -0,0 +1,162 @@ +## Bundler : A gem to bundle gems + + Github: http://github.com/wycats/bundler + Mailing list: http://groups.google.com/group/ruby-bundler + IRC: #carlhuda on freenode + +## Intro + +Bundler is a tool that manages gem dependencies for your ruby application. It +takes a gem manifest file and is able to fetch, download, and install the gems +and all child dependencies specified in this manifest. It can manage any update +to the gem manifest file and update the bundled gems accordingly. It also lets +you run any ruby code in context of the bundled gem environment. + +## Disclaimer + +This project is under rapid development. It is usable today, but there will be +many changes in the near future, including to the Gemfile DSL. We will bump up +versions with changes though. We greatly appreciate feedback. + +## Installation + +Bundler has no dependencies. Just clone the git repository and install the gem +with the following rake task: + + rake install + +## Usage + +Bundler requires a gem manifest file to be created. This should be a file named +`Gemfile` located in the root directory of your application. After the manifest +has been created, in your shell, cd into your application's directory and run +`gem bundle`. This will start the bundling process. + +### Manifest file + +This is where you specify all of your application's dependencies. By default +this should be in a file named `Gemfile` located in your application's root +directory. The following is an example of a potential `Gemfile`. For more +information, please refer to Bundler::ManifestBuilder. + + # Specify a dependency on rails. When the bundler downloads gems, + # it will download rails as well as all of rails' dependencies (such as + # activerecord, actionpack, etc...) + # + # At least one dependency must be specified + gem "rails" + + # Specify a dependency on rack v.1.0.0. The version is optional. If present, + # it can be specified the same way as with rubygems' #gem method. + gem "rack", "1.0.0" + + # Specify a dependency rspec, but only activate that gem in the "testing" + # environment (read more about environments later). :except is also a valid + # option to specify environment restrictions. + gem "rspec", :only => :testing + + # Add http://gems.github.com as a source that the bundler will use + # to find gems listed in the manifest. By default, + # http://gems.rubyforge.org is already added to the list. + # + # This is an optional setting. + source "http://gems.github.com" + + # Specify where the bundled gems should be stashed. This directory will + # be a gem repository where all gems are downloaded to and installed to. + # + # This is an optional setting. + # The default is: vendor/gems + bundle_path "my/bundled/gems" + + # Specify where gem executables should be copied to. + # + # This is an optional setting. + # The default is: bin + bin_path "my/executables" + + # Specify that rubygems should be completely disabled. This means that it + # will be impossible to require it and that available gems will be + # limited exclusively to gems that have been bundled. + # + # The default is to automatically require rubygems. There is also a + # `disable_system_gems` option that will limit available rubygems to + # the ones that have been bundled. + disable_rubygems + +### Running Bundler + +Once a manifest file has been created, the only thing that needs to be done +is to run the `gem bundle` command anywhere in your application. The script +will load the manifest file, resole all the dependencies, download all +needed gems, and install them into the specified directory. + +Every time an update is made to the manifest file, run `gem bundle` again to +get the changes installed. This will only check the remote sources if your +currently installed gems do not satisfy the `Gemfile`. If you want to force +checking for updates on the remote sources, use the `--update` option. + +### Running your application + +The easiest way to run your application is to start it with an executable +copied to the specified bin directory (by default, simply bin). For example, +if the application in question is a rack app, start it with `bin/rackup`. +This will automatically set the gem environment correctly. + +Another way to run arbitrary ruby code in context of the bundled gems is to +run it with the `gem exec` command. For example: + + gem exec ruby my_ruby_script.rb + +Yet another way is to manually require the environment file first. This is +located in `[bundle_path]/environments/default.rb`. For example: + + ruby -r vendor/gems/environment.rb my_ruby_script.rb + +### Using Bundler with Rails today + +It should be possible to use Bundler with Rails today. Here are the steps +to follow. + +* In your rails app, create a Gemfile and specify the gems that your + application depends on. Make sure to specify rails as well: + + gem "rails", "2.1.2" + gem "will_paginate" + + # Optionally, you can disable system gems all together and only + # use bundled gems. + disable_system_gems + +* Run `gem bundle` + +* You can now use rails if you prepend `gem exec` to every call to `script/*` + but that isn't fun. + +* At the top of `config/boot.rb`, add the following line: + + require File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'environment')) + +In theory, this should be enough to get going. + +## To require rubygems or not + +Ideally, no gem would assume the presence of rubygems at runtime. Rubygems provides +enough features so that this isn't necessary. However, there are a number of gems +that require specific rubygem features. + +If the `disable_rubygems` option is used, Bundler will stub out the most common +of these features, but it is possible that things will not go as intended quite +yet. So, if you are brave, try your code without rubygems at runtime. + +## Known Issues + +* When a gem points to a git repository, the git repository will be cloned + every time Bundler does a gem dependency resolve. + +## Reporting bugs + +Please report all bugs on the github issue tracker for the project located +at: + + http://github.com/wycats/bundler/issues/
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/Rakefile b/railties/lib/rails/vendor/bundler/Rakefile new file mode 100644 index 0000000000..dc4c3d6d46 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/Rakefile @@ -0,0 +1,57 @@ +require 'rubygems' unless ENV['NO_RUBYGEMS'] +require 'rubygems/specification' +require 'date' + +spec = Gem::Specification.new do |s| + s.name = "bundler" + s.version = "0.5.0.pre" + s.author = "Yehuda Katz" + s.email = "wycats@gmail.com" + s.homepage = "http://github.com/wycats/bundler" + s.description = s.summary = "An easy way to vendor gem dependencies" + + s.platform = Gem::Platform::RUBY + s.has_rdoc = true + s.extra_rdoc_files = ["README.markdown", "LICENSE"] + + s.required_rubygems_version = ">= 1.3.5" + + s.require_path = 'lib' + s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("lib/**/*") +end + +task :default => :spec + +begin + require 'spec/rake/spectask' +rescue LoadError + task(:spec) { $stderr.puts '`gem install rspec` to run specs' } +else + desc "Run specs" + Spec::Rake::SpecTask.new do |t| + t.spec_files = FileList['spec/**/*_spec.rb'] - FileList['spec/fixtures/**/*_spec.rb'] + t.spec_opts = %w(-fs --color) + end +end + +begin + require 'rake/gempackagetask' +rescue LoadError + task(:gem) { $stderr.puts '`gem install rake` to package gems' } +else + Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec + end +end + +desc "install the gem locally" +task :install => [:package] do + sh %{gem install pkg/#{spec.name}-#{spec.version}} +end + +desc "create a gemspec file" +task :make_spec do + File.open("#{spec.name}.gemspec", "w") do |file| + file.puts spec.to_ruby + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler.rb b/railties/lib/rails/vendor/bundler/lib/bundler.rb new file mode 100644 index 0000000000..1ede3517dd --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler.rb @@ -0,0 +1,34 @@ +require 'pathname' +require 'logger' +require 'set' +require 'erb' +# Required elements of rubygems +require "rubygems/remote_fetcher" +require "rubygems/installer" + +require "bundler/gem_bundle" +require "bundler/source" +require "bundler/finder" +require "bundler/gem_ext" +require "bundler/resolver" +require "bundler/environment" +require "bundler/dsl" +require "bundler/cli" +require "bundler/repository" +require "bundler/dependency" + +module Bundler + VERSION = "0.5.0" + + class << self + attr_writer :logger + + def logger + @logger ||= begin + logger = Logger.new(STDOUT, Logger::INFO) + logger.formatter = proc {|_,_,_,msg| "#{msg}\n" } + logger + end + end + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/cli.rb b/railties/lib/rails/vendor/bundler/lib/bundler/cli.rb new file mode 100644 index 0000000000..df9181fbc4 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/cli.rb @@ -0,0 +1,44 @@ +require "optparse" + +module Bundler + class CLI + def self.run(command, options = {}) + new(options).run(command) + rescue DefaultManifestNotFound => e + Bundler.logger.error "Could not find a Gemfile to use" + exit 2 + rescue InvalidEnvironmentName => e + Bundler.logger.error "Gemfile error: #{e.message}" + exit + rescue InvalidRepository => e + Bundler.logger.error e.message + exit + rescue VersionConflict => e + Bundler.logger.error e.message + exit + rescue GemNotFound => e + Bundler.logger.error e.message + exit + end + + def initialize(options) + @options = options + @manifest = Bundler::Environment.load(@options[:manifest]) + end + + def bundle + @manifest.install(@options[:update]) + end + + def exec + @manifest.setup_environment + # w0t? + super(*@options[:args]) + end + + def run(command) + send(command) + end + + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/commands/bundle_command.rb b/railties/lib/rails/vendor/bundler/lib/bundler/commands/bundle_command.rb new file mode 100644 index 0000000000..a1f9590f75 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/commands/bundle_command.rb @@ -0,0 +1,31 @@ +class Gem::Commands::BundleCommand < Gem::Command + + def initialize + super('bundle', 'Create a gem bundle based on your Gemfile', {:manifest => nil, :update => false}) + + add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options| + options[:manifest] = manifest + end + + add_option('-u', '--update', "Force a remote check for newer gems") do + options[:update] = true + end + end + + def usage + "#{program_name}" + end + + def description # :nodoc: + <<-EOF +Bundle stuff + EOF + end + + def execute + # Prevent the bundler from getting required unless it is actually being used + require 'bundler' + Bundler::CLI.run(:bundle, options) + end + +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/commands/exec_command.rb b/railties/lib/rails/vendor/bundler/lib/bundler/commands/exec_command.rb new file mode 100644 index 0000000000..228aa60619 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/commands/exec_command.rb @@ -0,0 +1,31 @@ +class Gem::Commands::ExecCommand < Gem::Command + + def initialize + super('exec', 'Run a command in context of a gem bundle', {:manifest => nil}) + + add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options| + options[:manifest] = manifest + end + end + + def usage + "#{program_name} COMMAND" + end + + def arguments # :nodoc: + "COMMAND command to run in context of the gem bundle" + end + + def description # :nodoc: + <<-EOF.gsub(' ', '') + Run in context of a bundle + EOF + end + + def execute + # Prevent the bundler from getting required unless it is actually being used + require 'bundler' + Bundler::CLI.run(:exec, options) + end + +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/dependency.rb b/railties/lib/rails/vendor/bundler/lib/bundler/dependency.rb new file mode 100644 index 0000000000..b627b58662 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/dependency.rb @@ -0,0 +1,56 @@ +module Bundler + class InvalidEnvironmentName < StandardError; end + + class Dependency + attr_reader :name, :version, :require_as, :only, :except + + def initialize(name, options = {}, &block) + options.each do |k, v| + options[k.to_s] = v + end + + @name = name + @version = options["version"] || ">= 0" + @require_as = Array(options["require_as"] || name) + @only = options["only"] + @except = options["except"] + @block = block + + if (@only && @only.include?("rubygems")) || (@except && @except.include?("rubygems")) + raise InvalidEnvironmentName, "'rubygems' is not a valid environment name" + end + end + + def in?(environment) + environment = environment.to_s + + return false unless !@only || @only.include?(environment) + return false if @except && @except.include?(environment) + true + end + + def to_s + to_gem_dependency.to_s + end + + def require(environment) + return unless in?(environment) + + @require_as.each do |file| + super(file) + end + + @block.call if @block + end + + def to_gem_dependency + @gem_dep ||= Gem::Dependency.new(name, version) + end + + def ==(o) + [name, version, require_as, only, except] == + [o.name, o.version, o.require_as, o.only, o.except] + end + + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/dsl.rb b/railties/lib/rails/vendor/bundler/lib/bundler/dsl.rb new file mode 100644 index 0000000000..d9a86ee1fd --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/dsl.rb @@ -0,0 +1,109 @@ +module Bundler + class ManifestFileNotFound < StandardError; end + + class Dsl + def initialize(environment) + @environment = environment + @sources = Hash.new { |h,k| h[k] = {} } + end + + def bundle_path(path) + path = Pathname.new(path) + @environment.gem_path = (path.relative? ? + @environment.root.join(path) : path).expand_path + end + + def bin_path(path) + path = Pathname.new(path) + @environment.bindir = (path.relative? ? + @environment.root.join(path) : path).expand_path + end + + def disable_rubygems + @environment.rubygems = false + end + + def disable_system_gems + @environment.system_gems = false + end + + def source(source) + source = GemSource.new(:uri => source) + unless @environment.sources.include?(source) + @environment.add_source(source) + end + end + + def only(env) + old, @only = @only, _combine_onlys(env) + yield + @only = old + end + + def except(env) + old, @except = @except, _combine_excepts(env) + yield + @except = old + end + + def clear_sources + @environment.clear_sources + end + + def gem(name, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + version = args.last + + options[:only] = _combine_onlys(options[:only] || options["only"]) + options[:except] = _combine_excepts(options[:except] || options["except"]) + + dep = Dependency.new(name, options.merge(:version => version)) + + # OMG REFACTORZ. KTHX + if vendored_at = options[:vendored_at] + vendored_at = Pathname.new(vendored_at) + vendored_at = @environment.filename.dirname.join(vendored_at) if vendored_at.relative? + + @sources[:directory][vendored_at.to_s] ||= begin + source = DirectorySource.new( + :name => name, + :version => version, + :location => vendored_at + ) + @environment.add_priority_source(source) + true + end + elsif git = options[:git] + @sources[:git][git] ||= begin + source = GitSource.new( + :name => name, + :version => version, + :uri => git, + :ref => options[:commit] || options[:tag], + :branch => options[:branch] + ) + @environment.add_priority_source(source) + true + end + end + + @environment.dependencies << dep + end + + private + + def _combine_onlys(only) + return @only unless only + only = [only].flatten.compact.uniq.map { |o| o.to_s } + only &= @only if @only + only + end + + def _combine_excepts(except) + return @except unless except + except = [except].flatten.compact.uniq.map { |o| o.to_s } + except |= @except if @except + except + end + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/environment.rb b/railties/lib/rails/vendor/bundler/lib/bundler/environment.rb new file mode 100644 index 0000000000..f07a9e2c6f --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/environment.rb @@ -0,0 +1,111 @@ +require "rubygems/source_index" + +module Bundler + class DefaultManifestNotFound < StandardError; end + + class Environment + attr_reader :filename, :dependencies + attr_accessor :rubygems, :system_gems, :gem_path, :bindir + + def self.load(gemfile = nil) + gemfile = gemfile ? Pathname.new(gemfile) : default_manifest_file + + unless gemfile.file? + raise ManifestFileNotFound, "#{filename.inspect} does not exist" + end + + new(gemfile) + end + + def self.default_manifest_file + current = Pathname.new(Dir.pwd) + + until current.root? + filename = current.join("Gemfile") + return filename if filename.exist? + current = current.parent + end + + raise DefaultManifestNotFound + end + + def initialize(filename) #, sources, dependencies, bindir, path, rubygems, system_gems) + @filename = filename + @default_sources = [GemSource.new(:uri => "http://gems.rubyforge.org")] + @sources = [] + @priority_sources = [] + @dependencies = [] + @rubygems = true + @system_gems = true + + # Evaluate the Gemfile + builder = Dsl.new(self) + builder.instance_eval(File.read(filename)) + end + + def install(update = false) + begin + tmp_path = filename.dirname.join(".tmp") + FileUtils.mkdir_p(tmp_path) + sources.each { |s| s.tmp_path = tmp_path } + repository.install(gem_dependencies, sources, + :rubygems => rubygems, + :system_gems => system_gems, + :manifest => filename, + :update => update + ) + ensure + FileUtils.rm_rf(tmp_path) + end + Bundler.logger.info "Done." + end + + def setup_environment + unless system_gems + ENV["GEM_HOME"] = gem_path + ENV["GEM_PATH"] = gem_path + end + ENV["PATH"] = "#{bindir}:#{ENV["PATH"]}" + ENV["RUBYOPT"] = "-r#{gem_path}/environment #{ENV["RUBYOPT"]}" + end + + def root + filename.parent + end + + def gem_path + @gem_path ||= root.join("vendor", "gems") + end + + def bindir + @bindir ||= root.join("bin") + end + + def sources + @priority_sources + @sources + @default_sources + end + + def add_source(source) + @sources << source + end + + def add_priority_source(source) + @priority_sources << source + end + + def clear_sources + @sources.clear + @default_sources.clear + end + + private + + def repository + @repository ||= Repository.new(gem_path, bindir) + end + + def gem_dependencies + @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency } + end + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/finder.rb b/railties/lib/rails/vendor/bundler/lib/bundler/finder.rb new file mode 100644 index 0000000000..b77ca65709 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/finder.rb @@ -0,0 +1,51 @@ +module Bundler + # Finder behaves like a rubygems source index in that it responds + # to #search. It also resolves a list of dependencies finding the + # best possible configuration of gems that satisifes all requirements + # without causing any gem activation errors. + class Finder + + # Takes an array of gem sources and fetches the full index of + # gems from each one. It then combines the indexes together keeping + # track of the original source so that any resolved gem can be + # fetched from the correct source. + # + # ==== Parameters + # *sources<String>:: URI pointing to the gem repository + def initialize(*sources) + @cache = {} + @index = {} + @sources = sources + end + + # Searches for a gem that matches the dependency + # + # ==== Parameters + # dependency<Gem::Dependency>:: The gem dependency to search for + # + # ==== Returns + # [Gem::Specification]:: A collection of gem specifications + # matching the search + def search(dependency) + @cache[dependency.hash] ||= begin + find_by_name(dependency.name).select do |spec| + dependency =~ spec + end.sort_by {|s| s.version } + end + end + + private + + def find_by_name(name) + matches = @index[name] ||= begin + versions = {} + @sources.reverse_each do |source| + versions.merge! source.specs[name] || {} + end + versions + end + matches.values + end + + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/gem_bundle.rb b/railties/lib/rails/vendor/bundler/lib/bundler/gem_bundle.rb new file mode 100644 index 0000000000..80d7710683 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/gem_bundle.rb @@ -0,0 +1,11 @@ +module Bundler + class GemBundle < Array + def download(repository) + sort_by {|s| s.full_name.downcase }.each do |spec| + spec.source.download(spec, repository) + end + + self + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/gem_ext.rb b/railties/lib/rails/vendor/bundler/lib/bundler/gem_ext.rb new file mode 100644 index 0000000000..155ad04c7e --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/gem_ext.rb @@ -0,0 +1,25 @@ +module Gem + class Installer + def app_script_text(bin_file_name) + path = @gem_home + template = File.read(File.join(File.dirname(__FILE__), "templates", "app_script.erb")) + erb = ERB.new(template, nil, '-') + erb.result(binding) + end + end + + class Specification + attr_accessor :source + attr_accessor :location + + # Hack to fix github's strange marshal file + def specification_version + @specification_version && @specification_version.to_i + end + + alias full_gem_path_without_location full_gem_path + def full_gem_path + @location ? @location : full_gem_path_without_location + end + end +end diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/repository.rb b/railties/lib/rails/vendor/bundler/lib/bundler/repository.rb new file mode 100644 index 0000000000..1a1dc7497d --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/repository.rb @@ -0,0 +1,151 @@ +require "bundler/repository/gem_repository" +require "bundler/repository/directory_repository" + +module Bundler + class InvalidRepository < StandardError ; end + + class Repository + attr_reader :path + + def initialize(path, bindir) + FileUtils.mkdir_p(path) + + @path = Pathname.new(path) + @bindir = Pathname.new(bindir) + + @repos = { + :gem => Gems.new(@path, @bindir), + :directory => Directory.new(@path.join("dirs"), @bindir) + } + end + + def install(dependencies, sources, options = {}) + if options[:update] || !satisfies?(dependencies) + fetch(dependencies, sources) + expand(options) + else + # Remove any gems that are still around if the Gemfile changed without + # requiring new gems to be download (e.g. a line in the Gemfile was + # removed) + cleanup(Resolver.resolve(dependencies, [source_index])) + end + configure(options) + sync + end + + def gems + gems = [] + each_repo do |repo| + gems.concat repo.gems + end + gems + end + + def satisfies?(dependencies) + index = source_index + dependencies.all? { |dep| index.search(dep).size > 0 } + end + + def source_index + index = Gem::SourceIndex.new + + each_repo do |repo| + index.gems.merge!(repo.source_index.gems) + end + + index + end + + def add_spec(type, spec) + @repos[type].add_spec(spec) + end + + def download_path_for(type) + @repos[type].download_path_for + end + + private + + def cleanup(bundle) + each_repo do |repo| + repo.cleanup(bundle) + end + end + + def each_repo + @repos.each do |k, repo| + yield repo + end + end + + def fetch(dependencies, sources) + bundle = Resolver.resolve(dependencies, sources) + # Cleanup here to remove any gems that could cause problem in the expansion + # phase + # + # TODO: Try to avoid double cleanup + cleanup(bundle) + bundle.download(self) + end + + def sync + glob = gems.map { |g| g.executables }.flatten.join(',') + + (Dir[@bindir.join("*")] - Dir[@bindir.join("{#{glob}}")]).each do |file| + Bundler.logger.info "Deleting bin file: #{File.basename(file)}" + FileUtils.rm_rf(file) + end + end + + def expand(options) + each_repo do |repo| + repo.expand(options) + end + end + + def configure(options) + generate_environment(options) + end + + def generate_environment(options) + FileUtils.mkdir_p(path) + + specs = gems + load_paths = load_paths_for_specs(specs) + bindir = @bindir.relative_path_from(path).to_s + filename = options[:manifest].relative_path_from(path).to_s + spec_files = specs.inject({}) do |hash, spec| + relative = spec.loaded_from.relative_path_from(@path).to_s + hash.merge!(spec.name => relative) + end + + File.open(path.join("environment.rb"), "w") do |file| + template = File.read(File.join(File.dirname(__FILE__), "templates", "environment.erb")) + erb = ERB.new(template, nil, '-') + file.puts erb.result(binding) + end + end + + def load_paths_for_specs(specs) + load_paths = [] + specs.each do |spec| + gem_path = Pathname.new(spec.full_gem_path) + if spec.bindir + load_paths << gem_path.join(spec.bindir).relative_path_from(@path).to_s + end + spec.require_paths.each do |path| + load_paths << gem_path.join(path).relative_path_from(@path).to_s + end + end + load_paths + end + + def require_code(file, dep) + constraint = case + when dep.only then %{ if #{dep.only.inspect}.include?(env)} + when dep.except then %{ unless #{dep.except.inspect}.include?(env)} + end + "require #{file.inspect}#{constraint}" + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/repository/directory_repository.rb b/railties/lib/rails/vendor/bundler/lib/bundler/repository/directory_repository.rb new file mode 100644 index 0000000000..e97dd38dd5 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/repository/directory_repository.rb @@ -0,0 +1,46 @@ +module Bundler + class Repository + class Directory + attr_reader :path, :bindir + + def initialize(path, bindir) + @path = path + @bindir = bindir + + FileUtils.mkdir_p(path.to_s) + end + + def source_index + index = Gem::SourceIndex.from_gems_in(@path.join("specifications")) + index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") } + index + end + + def gems + source_index.gems.values + end + + def add_spec(spec) + destination = path.join('specifications') + destination.mkdir unless destination.exist? + + File.open(destination.join("#{spec.full_name}.gemspec"), 'w') do |f| + f.puts spec.to_ruby + end + end + + def download_path_for + @path.join("dirs") + end + + # Checks whether a gem is installed + def expand(options) + # raise NotImplementedError + end + + def cleanup(gems) + # raise NotImplementedError + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/repository/gem_repository.rb b/railties/lib/rails/vendor/bundler/lib/bundler/repository/gem_repository.rb new file mode 100644 index 0000000000..90de49d83d --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/repository/gem_repository.rb @@ -0,0 +1,108 @@ +module Bundler + class Repository + class Gems + attr_reader :path, :bindir + + def initialize(path, bindir) + @path = path + @bindir = bindir + end + + # Returns the source index for all gems installed in the + # repository + def source_index + index = Gem::SourceIndex.from_gems_in(@path.join("specifications")) + index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") } + index + end + + def gems + source_index.gems.values + end + + # Checks whether a gem is installed + def expand(options) + cached_gems.each do |name, version| + unless installed?(name, version) + install_cached_gem(name, version, options) + end + end + end + + def cleanup(gems) + glob = gems.map { |g| g.full_name }.join(',') + base = path.join("{cache,specifications,gems}") + + (Dir[base.join("*")] - Dir[base.join("{#{glob}}{.gemspec,.gem,}")]).each do |file| + if File.basename(file) =~ /\.gem$/ + name = File.basename(file, '.gem') + Bundler.logger.info "Deleting gem: #{name}" + end + FileUtils.rm_rf(file) + end + end + + def add_spec(spec) + raise NotImplementedError + end + + def download_path_for + path + end + + private + + def cache_path + @path.join("cache") + end + + def cache_files + Dir[cache_path.join("*.gem")] + end + + def cached_gems + cache_files.map do |f| + full_name = File.basename(f).gsub(/\.gem$/, '') + full_name.split(/-(?=[^-]+$)/) + end + end + + def spec_path + @path.join("specifications") + end + + def spec_files + Dir[spec_path.join("*.gemspec")] + end + + def gem_path + @path.join("gems") + end + + def gem_paths + Dir[gem_path.join("*")] + end + + def installed?(name, version) + spec_files.any? { |g| File.basename(g) == "#{name}-#{version}.gemspec" } && + gem_paths.any? { |g| File.basename(g) == "#{name}-#{version}" } + end + + def install_cached_gem(name, version, options = {}) + cached_gem = cache_path.join("#{name}-#{version}.gem") + # TODO: Add a warning if cached_gem is not a file + if cached_gem.file? + Bundler.logger.info "Installing #{name}-#{version}.gem" + installer = Gem::Installer.new(cached_gem.to_s, options.merge( + :install_dir => @path, + :ignore_dependencies => true, + :env_shebang => true, + :wrappers => true, + :bin_dir => @bindir + )) + installer.install + end + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/resolver.rb b/railties/lib/rails/vendor/bundler/lib/bundler/resolver.rb new file mode 100644 index 0000000000..2a6a6371c2 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/resolver.rb @@ -0,0 +1,189 @@ +# This is the latest iteration of the gem dependency resolving algorithm. As of now, +# it can resolve (as a success of failure) any set of gem dependencies we throw at it +# in a reasonable amount of time. The most iterations I've seen it take is about 150. +# The actual implementation of the algorithm is not as good as it could be yet, but that +# can come later. + +# Extending Gem classes to add necessary tracking information +module Gem + class Dependency + def required_by + @required_by ||= [] + end + end + class Specification + def required_by + @required_by ||= [] + end + end +end + +module Bundler + class GemNotFound < StandardError; end + class VersionConflict < StandardError; end + + class Resolver + + attr_reader :errors + + # Figures out the best possible configuration of gems that satisfies + # the list of passed dependencies and any child dependencies without + # causing any gem activation errors. + # + # ==== Parameters + # *dependencies<Gem::Dependency>:: The list of dependencies to resolve + # + # ==== Returns + # <GemBundle>,nil:: If the list of dependencies can be resolved, a + # collection of gemspecs is returned. Otherwise, nil is returned. + def self.resolve(requirements, sources) + Bundler.logger.info "Calculating dependencies..." + + resolver = new(sources) + result = catch(:success) do + resolver.resolve(requirements, {}) + output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))| + o << " Conflict on: #{conflict.inspect}:\n" + o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n" + o << " * #{requirement} required by #{requirement.required_by.first}\n" + o << " All possible versions of origin requirements conflict." + end + raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{output}" + nil + end + result && GemBundle.new(result.values) + end + + def initialize(sources) + @errors = {} + @stack = [] + @specs = Hash.new { |h,k| h[k] = {} } + @cache = {} + @index = {} + + sources.reverse_each do |source| + source.gems.values.each do |spec| + # TMP HAX FOR OPTZ + spec.source = source + next unless Gem::Platform.match(spec.platform) + @specs[spec.name][spec.version] = spec + end + end + end + + def resolve(reqs, activated) + # If the requirements are empty, then we are in a success state. Aka, all + # gem dependencies have been resolved. + throw :success, activated if reqs.empty? + + # Sort requirements so that the ones that are easiest to resolve are first. + # Easiest to resolve is defined by: Is this gem already activated? Otherwise, + # check the number of child dependencies this requirement has. + reqs = reqs.sort_by do |req| + activated[req.name] ? 0 : search(req).size + end + + activated = activated.dup + # Pull off the first requirement so that we can resolve it + current = reqs.shift + + # Check if the gem has already been activated, if it has, we will make sure + # that the currently activated gem satisfies the requirement. + if existing = activated[current.name] + if current.version_requirements.satisfied_by?(existing.version) + @errors.delete(existing.name) + # Since the current requirement is satisfied, we can continue resolving + # the remaining requirements. + resolve(reqs, activated) + else + @errors[existing.name] = [existing, current] + # Since the current requirement conflicts with an activated gem, we need + # to backtrack to the current requirement's parent and try another version + # of it (maybe the current requirement won't be present anymore). If the + # current requirement is a root level requirement, we need to jump back to + # where the conflicting gem was activated. + parent = current.required_by.last || existing.required_by.last + # We track the spot where the current gem was activated because we need + # to keep a list of every spot a failure happened. + throw parent.name, existing.required_by.last.name + end + else + # There are no activated gems for the current requirement, so we are going + # to find all gems that match the current requirement and try them in decending + # order. We also need to keep a set of all conflicts that happen while trying + # this gem. This is so that if no versions work, we can figure out the best + # place to backtrack to. + conflicts = Set.new + + # Fetch all gem versions matching the requirement + # + # TODO: Warn / error when no matching versions are found. + matching_versions = search(current) + + if matching_versions.empty? + if current.required_by.empty? + raise GemNotFound, "Could not find gem '#{current}' in any of the sources" + end + Bundler.logger.warn "Could not find gem '#{current}' (required by '#{current.required_by.last}') in any of the sources" + end + + matching_versions.reverse_each do |spec| + conflict = resolve_requirement(spec, current, reqs.dup, activated.dup) + conflicts << conflict if conflict + end + # If the current requirement is a root level gem and we have conflicts, we + # can figure out the best spot to backtrack to. + if current.required_by.empty? && !conflicts.empty? + # Check the current "catch" stack for the first one that is included in the + # conflicts set. That is where the parent of the conflicting gem was required. + # By jumping back to this spot, we can try other version of the parent of + # the conflicting gem, hopefully finding a combination that activates correctly. + @stack.reverse_each do |savepoint| + if conflicts.include?(savepoint) + throw savepoint + end + end + end + end + end + + def resolve_requirement(spec, requirement, reqs, activated) + # We are going to try activating the spec. We need to keep track of stack of + # requirements that got us to the point of activating this gem. + spec.required_by.replace requirement.required_by + spec.required_by << requirement + + activated[spec.name] = spec + + # Now, we have to loop through all child dependencies and add them to our + # array of requirements. + spec.dependencies.each do |dep| + next if dep.type == :development + dep.required_by << requirement + reqs << dep + end + + # We create a savepoint and mark it by the name of the requirement that caused + # the gem to be activated. If the activated gem ever conflicts, we are able to + # jump back to this point and try another version of the gem. + length = @stack.length + @stack << requirement.name + retval = catch(requirement.name) do + resolve(reqs, activated) + end + # Since we're doing a lot of throw / catches. A push does not necessarily match + # up to a pop. So, we simply slice the stack back to what it was before the catch + # block. + @stack.slice!(length..-1) + retval + end + + def search(dependency) + @cache[dependency.hash] ||= begin + @specs[dependency.name].values.select do |spec| + dependency =~ spec + end.sort_by {|s| s.version } + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/runtime.rb b/railties/lib/rails/vendor/bundler/lib/bundler/runtime.rb new file mode 100644 index 0000000000..27e0254966 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/runtime.rb @@ -0,0 +1,2 @@ +require File.join(File.dirname(__FILE__), "runtime", "dsl") +require File.join(File.dirname(__FILE__), "runtime", "dependency")
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/source.rb b/railties/lib/rails/vendor/bundler/lib/bundler/source.rb new file mode 100644 index 0000000000..37828ca316 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/source.rb @@ -0,0 +1,150 @@ +module Bundler + # Represents a source of rubygems. Initially, this is only gem repositories, but + # eventually, this will be git, svn, HTTP + class Source + attr_accessor :tmp_path + end + + class GemSource < Source + attr_reader :uri + + def initialize(options) + @uri = options[:uri] + @uri = URI.parse(@uri) unless @uri.is_a?(URI) + raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute? + end + + def gems + @specs ||= fetch_specs + end + + def ==(other) + uri == other.uri + end + + def to_s + @uri.to_s + end + + class RubygemsRetardation < StandardError; end + + def download(spec, repository) + Bundler.logger.info "Downloading #{spec.full_name}.gem" + + destination = repository.download_path_for(:gem) + + unless destination.writable? + raise RubygemsRetardation + end + + Gem::RemoteFetcher.fetcher.download(spec, uri, repository.download_path_for(:gem)) + end + + private + + def fetch_specs + Bundler.logger.info "Updating source: #{to_s}" + + deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/Marshal.4.8.Z") + inflated = Gem.inflate deflated + + index = Marshal.load(inflated) + index.gems + rescue Gem::RemoteFetcher::FetchError => e + raise ArgumentError, "#{to_s} is not a valid source: #{e.message}" + end + end + + class DirectorySource < Source + def initialize(options) + @name = options[:name] + @version = options[:version] + @location = options[:location] + @require_paths = options[:require_paths] || %w(lib) + end + + def gems + @gems ||= begin + specs = {} + + # Find any gemspec files in the directory and load those specs + Dir[@location.join('**', '*.gemspec')].each do |file| + path = Pathname.new(file).relative_path_from(@location).dirname + spec = eval(File.read(file)) + spec.require_paths.map! { |p| path.join(p) } + specs[spec.full_name] = spec + end + + # If a gemspec for the dependency was not found, add it to the list + if specs.keys.grep(/^#{Regexp.escape(@name)}/).empty? + case + when @version.nil? + raise ArgumentError, "If you use :at, you must specify the gem" \ + "and version you wish to stand in for" + when !Gem::Version.correct?(@version) + raise ArgumentError, "If you use :at, you must specify a gem and" \ + "version. You specified #{@version} for the version" + end + + default = Gem::Specification.new do |s| + s.name = @name + s.version = Gem::Version.new(@version) if @version + end + specs[default.full_name] = default + end + + specs + end + end + + def ==(other) + # TMP HAX + other.is_a?(DirectorySource) + end + + def to_s + "#{@name} (#{@version}) Located at: '#{@location}'" + end + + def download(spec, repository) + spec.require_paths.map! { |p| File.join(@location, p) } + repository.add_spec(:directory, spec) + end + end + + class GitSource < DirectorySource + def initialize(options) + super + @uri = options[:uri] + @ref = options[:ref] + @branch = options[:branch] + end + + def gems + FileUtils.mkdir_p(tmp_path.join("gitz")) + + # TMP HAX to get the *.gemspec reading to work + @location = tmp_path.join("gitz", @name) + + Bundler.logger.info "Cloning git repository at: #{@uri}" + `git clone #{@uri} #{@location} --no-hardlinks` + + if @ref + Dir.chdir(@location) { `git checkout #{@ref}` } + elsif @branch && @branch != "master" + Dir.chdir(@location) { `git checkout origin/#{@branch}` } + end + super + end + + def download(spec, repository) + dest = repository.download_path_for(:directory).join(@name) + spec.require_paths.map! { |p| File.join(dest, p) } + repository.add_spec(:directory, spec) + if spec.name == @name + FileUtils.mkdir_p(dest.dirname) + FileUtils.mv(tmp_path.join("gitz", spec.name), dest) + end + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/templates/app_script.erb b/railties/lib/rails/vendor/bundler/lib/bundler/templates/app_script.erb new file mode 100644 index 0000000000..3e47a53ca8 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/templates/app_script.erb @@ -0,0 +1,3 @@ +<%= shebang bin_file_name %> +require File.join(File.dirname(__FILE__), "<%= path.join("environment").relative_path_from(Pathname.new(bin_dir)) %>") +load File.join(File.dirname(__FILE__), "<%= path.join("gems", @spec.full_name, @spec.bindir, bin_file_name).relative_path_from(Pathname.new(bin_dir)) %>")
\ No newline at end of file diff --git a/railties/lib/rails/vendor/bundler/lib/bundler/templates/environment.erb b/railties/lib/rails/vendor/bundler/lib/bundler/templates/environment.erb new file mode 100644 index 0000000000..21f3de8854 --- /dev/null +++ b/railties/lib/rails/vendor/bundler/lib/bundler/templates/environment.erb @@ -0,0 +1,127 @@ +# DO NOT MODIFY THIS FILE +module Bundler + dir = File.dirname(__FILE__) + +<% unless options[:system_gems] -%> + ENV["GEM_HOME"] = dir + ENV["GEM_PATH"] = dir +<% end -%> + ENV["PATH"] = "#{dir}/<%= bindir %>:#{ENV["PATH"]}" + ENV["RUBYOPT"] = "-r#{__FILE__} #{ENV["RUBYOPT"]}" + +<% load_paths.each do |load_path| -%> + $LOAD_PATH.unshift File.expand_path("#{dir}/<%= load_path %>") +<% end -%> + + @gemfile = "#{dir}/<%= filename %>" + +<% if options[:rubygems] -%> + require "rubygems" + + @bundled_specs = {} +<% spec_files.each do |name, path| -%> + @bundled_specs["<%= name %>"] = eval(File.read("#{dir}/<%= path %>")) + @bundled_specs["<%= name %>"].loaded_from = "#{dir}/<%= path %>" +<% end -%> + + def self.add_specs_to_loaded_specs + Gem.loaded_specs.merge! @bundled_specs + end + + def self.add_specs_to_index + @bundled_specs.each do |name, spec| + Gem.source_index.add_spec spec + end + end + + add_specs_to_loaded_specs + add_specs_to_index +<% end -%> + + def self.require_env(env = nil) + context = Class.new do + def initialize(env) @env = env && env.to_s ; end + def method_missing(*) ; end + def only(env) + old, @only = @only, _combine_onlys(env) + yield + @only = old + end + def except(env) + old, @except = @except, _combine_excepts(env) + yield + @except = old + end + def gem(name, *args) + opt = args.last || {} + only = _combine_onlys(opt[:only] || opt["only"]) + except = _combine_excepts(opt[:except] || opt["except"]) + files = opt[:require_as] || opt["require_as"] || name + + return unless !only || only.any? {|e| e == @env } + return if except && except.any? {|e| e == @env } + + files.each { |f| require f } + yield if block_given? + true + end + private + def _combine_onlys(only) + return @only unless only + only = [only].flatten.compact.uniq.map { |o| o.to_s } + only &= @only if @only + only + end + def _combine_excepts(except) + return @except unless except + except = [except].flatten.compact.uniq.map { |o| o.to_s } + except |= @except if @except + except + end + end + context.new(env && env.to_s).instance_eval(File.read(@gemfile)) + end +end + +<% if options[:rubygems] -%> +module Gem + def source_index.refresh! + super + Bundler.add_specs_to_index + end +end +<% else -%> +$" << "rubygems.rb" + +module Kernel + def gem(*) + # Silently ignore calls to gem, since, in theory, everything + # is activated correctly already. + end +end + +# Define all the Gem errors for gems that reference them. +module Gem + def self.ruby ; <%= Gem.ruby.inspect %> ; end + class LoadError < ::LoadError; end + class Exception < RuntimeError; end + class CommandLineError < Exception; end + class DependencyError < Exception; end + class DependencyRemovalException < Exception; end + class GemNotInHomeException < Exception ; end + class DocumentError < Exception; end + class EndOfYAMLException < Exception; end + class FilePermissionError < Exception; end + class FormatException < Exception; end + class GemNotFoundException < Exception; end + class InstallError < Exception; end + class InvalidSpecificationException < Exception; end + class OperationNotSupportedError < Exception; end + class RemoteError < Exception; end + class RemoteInstallationCancelled < Exception; end + class RemoteInstallationSkipped < Exception; end + class RemoteSourceException < Exception; end + class VerificationError < Exception; end + class SystemExitException < SystemExit; end +end +<% end -%>
\ No newline at end of file diff --git a/railties/lib/vendor/thor-0.11.5/CHANGELOG.rdoc b/railties/lib/rails/vendor/thor-0.11.6/CHANGELOG.rdoc index dba25b7205..dba25b7205 100644 --- a/railties/lib/vendor/thor-0.11.5/CHANGELOG.rdoc +++ b/railties/lib/rails/vendor/thor-0.11.6/CHANGELOG.rdoc diff --git a/railties/lib/vendor/thor-0.11.5/LICENSE b/railties/lib/rails/vendor/thor-0.11.6/LICENSE index 98722da459..98722da459 100644 --- a/railties/lib/vendor/thor-0.11.5/LICENSE +++ b/railties/lib/rails/vendor/thor-0.11.6/LICENSE diff --git a/railties/lib/vendor/thor-0.11.5/README.rdoc b/railties/lib/rails/vendor/thor-0.11.6/README.rdoc index f1106f02b6..f1106f02b6 100644 --- a/railties/lib/vendor/thor-0.11.5/README.rdoc +++ b/railties/lib/rails/vendor/thor-0.11.6/README.rdoc diff --git a/railties/lib/vendor/thor-0.11.5/bin/rake2thor b/railties/lib/rails/vendor/thor-0.11.6/bin/rake2thor index 50c7410d80..50c7410d80 100755 --- a/railties/lib/vendor/thor-0.11.5/bin/rake2thor +++ b/railties/lib/rails/vendor/thor-0.11.6/bin/rake2thor diff --git a/railties/lib/vendor/thor-0.11.5/bin/thor b/railties/lib/rails/vendor/thor-0.11.6/bin/thor index eaf849fb4a..eaf849fb4a 100755 --- a/railties/lib/vendor/thor-0.11.5/bin/thor +++ b/railties/lib/rails/vendor/thor-0.11.6/bin/thor diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor.rb index 8dfcfd4c5b..3b45c4e9b7 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor.rb @@ -135,7 +135,7 @@ class Thor args, opts = given_args, {} end - task ||= Task.dynamic(meth) + task ||= Thor::Task::Dynamic.new(meth) trailing = args[Range.new(arguments.size, -1)] new(args, opts, config).invoke(task, trailing || []) end diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions.rb index 1d09dc38ae..d561ccb2aa 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions.rb @@ -8,23 +8,8 @@ class Thor module Actions attr_accessor :behavior - # On inclusion, add some options to base. - # def self.included(base) #:nodoc: base.extend ClassMethods - return unless base.respond_to?(:class_option) - - base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, - :desc => "Run but do not make any changes" - - base.class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, - :desc => "Overwrite files that already exist" - - base.class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, - :desc => "Skip files that already exist" - - base.class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, - :desc => "Supress status output" end module ClassMethods @@ -49,6 +34,22 @@ class Thor paths += from_superclass(:source_paths, []) paths end + + # Add runtime options that help actions execution. + # + def add_runtime_options! + class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + + class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Supress status output" + end end # Extends initializer to add more configuration options. diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/create_file.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/create_file.rb index 8f6badee27..8f6badee27 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/create_file.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/create_file.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/directory.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/directory.rb index be5eb822ac..be5eb822ac 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/directory.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/directory.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/empty_directory.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/empty_directory.rb index 03c1fe4af1..03c1fe4af1 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/empty_directory.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/empty_directory.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/file_manipulation.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/file_manipulation.rb index 74c157ba8c..d77d90d448 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/file_manipulation.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/file_manipulation.rb @@ -100,7 +100,7 @@ class Thor FileUtils.chmod_R(mode, path) unless options[:pretend] end - # Prepend text to a file. + # Prepend text to a file. Since it depends on inject_into_file, it's reversible. # # ==== Parameters # path<String>:: path of the file to be changed @@ -111,19 +111,17 @@ class Thor # # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' # - def prepend_file(path, data=nil, config={}, &block) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true) - - unless options[:pretend] - content = data || block.call - content << File.read(path) - File.open(path, 'wb') { |file| file.write(content) } - end + # prepend_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def prepend_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /\A/) + inject_into_file(path, *(args << config), &block) end - # Append text to a file. + # Append text to a file. Since it depends on inject_into_file, it's reversible. # # ==== Parameters # path<String>:: path of the file to be changed @@ -134,11 +132,37 @@ class Thor # # append_file 'config/environments/test.rb', 'config.gem "rspec"' # - def append_file(path, data=nil, config={}, &block) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true) - File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend] + # append_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def append_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:before => /\z/) + inject_into_file(path, *(args << config), &block) + end + + # Injects text right after the class definition. Since it depends on + # inject_into_file, it's reversible. + # + # ==== Parameters + # path<String>:: path of the file to be changed + # klass<String|Class>:: the class to be manipulated + # data<String>:: the data to append to the class, can be also given as a block. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n" + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController do + # " filter_parameter :password\n" + # end + # + def inject_into_class(path, klass, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) + inject_into_file(path, *(args << config), &block) end # Run a regular expression replacement on a file. diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/inject_into_file.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/inject_into_file.rb index 66dd1f5fc1..0636ec6591 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/actions/inject_into_file.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/actions/inject_into_file.rb @@ -3,10 +3,8 @@ require 'thor/actions/empty_directory' class Thor module Actions - # Injects the given content into a file. Different from append_file, - # prepend_file and gsub_file, this method is reversible. By this reason, - # the flag can only be strings. gsub_file is your friend if you need to - # deal with more complex cases. + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. # # ==== Parameters # destination<String>:: Relative path to the destination root @@ -16,11 +14,11 @@ class Thor # # ==== Examples # - # inject_into_file "config/environment.rb", "config.gem thor", :after => "Rails::Initializer.run do |config|\n" + # inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" # # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do # gems = ask "Which gems would you like to add?" - # gems.split(" ").map{ |gem| " config.gem #{gem}" }.join("\n") + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") # end # def inject_into_file(destination, *args, &block) @@ -29,40 +27,65 @@ class Thor else data, config = args.shift, args.shift end - - log_status = args.empty? || args.pop action InjectIntoFile.new(self, destination, data, config) end class InjectIntoFile < EmptyDirectory #:nodoc: - attr_reader :flag, :replacement + attr_reader :replacement, :flag, :behavior def initialize(base, destination, data, config) super(base, destination, { :verbose => true }.merge(config)) - data = data.call if data.is_a?(Proc) - - @replacement = if @config.key?(:after) - @flag = @config.delete(:after) - @flag + data + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] else - @flag = @config.delete(:before) - data + @flag + [:before, @config.delete(:before)] end + + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) end def invoke! - say_status :inject, config[:verbose] - replace!(flag, replacement) + say_status :invoke + + content = if @behavior == :after + '\0' + replacement + else + replacement + '\0' + end + + replace!(/#{flag}/, content) end def revoke! - say_status :deinject, config[:verbose] - replace!(replacement, flag) + say_status :revoke + + regexp = if @behavior == :after + content = '\1\2' + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m + else + content = '\2\3' + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m + end + + replace!(regexp, content) end protected + def say_status(behavior) + status = if flag == /\A/ + behavior == :invoke ? :prepend : :unprepend + elsif flag == /\z/ + behavior == :invoke ? :append : :unappend + else + behavior == :invoke ? :inject : :deinject + end + + super(status, config[:verbose]) + end + # Adds the content to the file. # def replace!(regexp, string) diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/base.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/base.rb index 0fa87f8162..700d794123 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/base.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/base.rb @@ -355,6 +355,7 @@ class Thor else config[:shell].error e.message end + exit(1) if exit_on_failure? end protected @@ -491,6 +492,12 @@ class Thor end end + # A flag that makes the process exit with status 1 if any error happens. + # + def exit_on_failure? + false + end + # SIGNATURE: Sets the baseclass. This is where the superclass lookup # finishes. def baseclass #:nodoc: diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/core_ext/hash_with_indifferent_access.rb index 78bc5cf4bf..78bc5cf4bf 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/core_ext/hash_with_indifferent_access.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/core_ext/hash_with_indifferent_access.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/core_ext/ordered_hash.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/core_ext/ordered_hash.rb index 27fea5bb35..27fea5bb35 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/core_ext/ordered_hash.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/core_ext/ordered_hash.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/error.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/error.rb index f9b31a35d1..f9b31a35d1 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/error.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/error.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/group.rb index 1e59df2313..1e59df2313 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/group.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/group.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/invocation.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/invocation.rb index c0388dd863..32e6a72454 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/invocation.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/invocation.rb @@ -153,7 +153,7 @@ class Thor raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base task ||= klass.default_task if klass <= Thor - task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task) + task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task) task end diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/parser.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser.rb index 57a3f6e1a5..57a3f6e1a5 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/parser.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/argument.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/argument.rb index aa8ace4719..aa8ace4719 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/argument.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/argument.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/arguments.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/arguments.rb index fb5d965e06..fb5d965e06 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/arguments.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/arguments.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/option.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/option.rb index 9e40ec73fa..9e40ec73fa 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/option.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/option.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/options.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/options.rb index 75092308b5..75092308b5 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/parser/options.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/options.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/rake_compat.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/rake_compat.rb index 3ab6bb21f5..3ab6bb21f5 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/rake_compat.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/rake_compat.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/runner.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/runner.rb index 3639ac0aa9..43da09b336 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/runner.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/runner.rb @@ -175,6 +175,10 @@ class Thor::Runner < Thor #:nodoc: File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } end + def self.exit_on_failure? + true + end + # Load the thorfiles. If relevant_to is supplied, looks for specific files # in the thor_root instead of loading them all. # diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/shell.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell.rb index 0d3f4d5951..0d3f4d5951 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/shell.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/shell/basic.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell/basic.rb index 3c02e47c33..ea9665380b 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/shell/basic.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell/basic.rb @@ -34,12 +34,11 @@ class Thor # ==== Example # say("I know you knew that.") # - def say(message="", color=nil, force_new_line=false) + def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/)) message = message.to_s - new_line = force_new_line || !(message[-1, 1] == " " || message[-1, 1] == "\t") message = set_color(message, color) if color - if new_line + if force_new_line $stdout.puts(message) else $stdout.print(message) diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/shell/color.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell/color.rb index 24704f7885..24704f7885 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/shell/color.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/shell/color.rb diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/task.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/task.rb index 23d35b883c..91c7564d3f 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/task.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/task.rb @@ -1,11 +1,19 @@ class Thor class Task < Struct.new(:name, :description, :usage, :options) - # Creates a dynamic task. Dynamic tasks are created on demand to allow method - # missing calls (since a method missing does not have a task object for it). + # A dynamic task that handles method missing scenarios. # - def self.dynamic(name) - new(name, "A dynamically-generated task", name.to_s) + class Dynamic < Task + def initialize(name) + super(name.to_s, "A dynamically-generated task", name.to_s) + end + + def run(instance, args=[]) + unless (instance.methods & [name.to_s, name.to_sym]).empty? + raise Error, "could not find Thor class or task '#{name}'" + end + super + end end def initialize(name, description, usage, options=nil) @@ -37,8 +45,6 @@ class Thor # injected in the usage. # def formatted_usage(klass=nil, namespace=false, show_options=true) - formatted = '' - formatted = if namespace.is_a?(String) "#{namespace}:" elsif klass && namespace @@ -77,7 +83,7 @@ class Thor # def public_method?(instance) #:nodoc: collection = instance.private_methods + instance.protected_methods - !(collection).include?(name.to_s) && !(collection).include?(name.to_sym) # For Ruby 1.9 + (collection & [name.to_s, name.to_sym]).empty? end # Clean everything that comes from the Thor gempath and remove the caller. diff --git a/railties/lib/vendor/thor-0.11.5/lib/thor/util.rb b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/util.rb index 4938dc4aca..fd820d7462 100644 --- a/railties/lib/vendor/thor-0.11.5/lib/thor/util.rb +++ b/railties/lib/rails/vendor/thor-0.11.6/lib/thor/util.rb @@ -22,7 +22,7 @@ class Thor # namespace<String>:: The namespace to search for. # def self.find_by_namespace(namespace) - namespace = 'default' if namespace.empty? + namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ Thor::Base.subclasses.find do |klass| klass.namespace == namespace @@ -137,17 +137,18 @@ class Thor # inherit from Thor or Thor::Group. # def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true) - klass, task_name = Thor::Util.find_by_namespace(namespace), nil + if namespace.include?(?:) + pieces = namespace.split(":") + task = pieces.pop + klass = Thor::Util.find_by_namespace(pieces.join(":")) + end - if klass.nil? && namespace.include?(?:) - namespace = namespace.split(":") - task_name = namespace.pop - klass = Thor::Util.find_by_namespace(namespace.join(":")) + unless klass + klass, task = Thor::Util.find_by_namespace(namespace), nil end raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil? - - return klass, task_name + return klass, task end # Receives a path and load the thor file in the path. The file is evaluated diff --git a/railties/lib/webrick_server.rb b/railties/lib/rails/webrick_server.rb index 2f60151b22..2f60151b22 100644 --- a/railties/lib/webrick_server.rb +++ b/railties/lib/rails/webrick_server.rb diff --git a/railties/lib/vendor/bundler/Rakefile b/railties/lib/vendor/bundler/Rakefile deleted file mode 100644 index fd39fbff77..0000000000 --- a/railties/lib/vendor/bundler/Rakefile +++ /dev/null @@ -1,52 +0,0 @@ -require 'rubygems' unless ENV['NO_RUBYGEMS'] -require 'rake/gempackagetask' -require 'rubygems/specification' -require 'date' -require 'spec/rake/spectask' - -spec = Gem::Specification.new do |s| - s.name = "bundler" - s.version = "0.0.1" - s.author = "Your Name" - s.email = "Your Email" - s.homepage = "http://example.com" - s.description = s.summary = "A gem that provides..." - - s.platform = Gem::Platform::RUBY - s.has_rdoc = true - s.extra_rdoc_files = ["README", "LICENSE"] - s.summary = "" - - # Uncomment this to add a dependency - # s.add_dependency "foo" - - s.bindir = "bin" - s.executables = %w( gem_bundler ) - s.require_path = 'lib' - s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*") -end - -task :default => :spec - -desc "Run specs" -Spec::Rake::SpecTask.new do |t| - t.spec_files = FileList['spec/**/*_spec.rb'] - t.spec_opts = %w(-fs --color) -end - - -Rake::GemPackageTask.new(spec) do |pkg| - pkg.gem_spec = spec -end - -desc "install the gem locally" -task :install => [:package] do - sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}} -end - -desc "create a gemspec file" -task :make_spec do - File.open("#{GEM}.gemspec", "w") do |file| - file.puts spec.to_ruby - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/bin/gem_bundler b/railties/lib/vendor/bundler/bin/gem_bundler deleted file mode 100755 index 7a8771efee..0000000000 --- a/railties/lib/vendor/bundler/bin/gem_bundler +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env ruby -require "optparse" -require "bundler" - -options = {} - -parser = OptionParser.new do |op| - op.banner = "Usage: gem_bundler [OPTIONS] [PATH]" - - op.on("-m", "--manifest MANIFEST") do |manifest| - options[:manifest] = manifest - end - - op.on_tail("-h", "--help", "Show this message") do - puts op - exit - end -end -parser.parse! - -options[:path] = ARGV.shift - -unless options[:path] - puts parser - puts %( - [PATH] must be specified - ) - exit -end - -unless options[:manifest] && File.exist?(options[:manifest]) - puts parser - puts %( - MANIFEST must be a valid manifest file - ) - exit -end - - -Bundler.run(options)
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler.rb b/railties/lib/vendor/bundler/lib/bundler.rb deleted file mode 100644 index 4dcab20da9..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'logger' -require 'set' -# Required elements of rubygems -require "rubygems/remote_fetcher" -require "rubygems/installer" - -require "bundler/gem_bundle" -require "bundler/installer" -require "bundler/finder" -require "bundler/gem_specification" -require "bundler/resolver" -require "bundler/manifest" -require "bundler/dependency" -require "bundler/runtime" -require "bundler/cli" - -module Bundler - VERSION = "0.5.0" - - def self.run(options = {}) - manifest = ManifestBuilder.load(options[:path], options[:manifest]) - manifest.install - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/cli.rb b/railties/lib/vendor/bundler/lib/bundler/cli.rb deleted file mode 100644 index ed49ef88f0..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/cli.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Bundler - module CLI - - def default_manifest - current = Pathname.new(Dir.pwd) - - begin - manifest = current.join("Gemfile") - return manifest.to_s if File.exist?(manifest) - current = current.parent - end until current.root? - nil - end - - module_function :default_manifest - - def default_path - Pathname.new(File.dirname(default_manifest)).join("vendor").join("gems").to_s - end - - module_function :default_path - - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/dependency.rb b/railties/lib/vendor/bundler/lib/bundler/dependency.rb deleted file mode 100644 index 739a7a117d..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/dependency.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Bundler - class Dependency - - attr_reader :name, :version, :require_as, :only, :except - - def initialize(name, options = {}) - options.each do |k, v| - options[k.to_s] = v - end - - @name = name - @version = options["version"] || ">= 0" - @require_as = Array(options["require_as"] || name) - @only = Array(options["only"]).map {|e| e.to_s } if options["only"] - @except = Array(options["except"]).map {|e| e.to_s } if options["except"] - end - - def in?(environment) - environment = environment.to_s - - return false unless !@only || @only.include?(environment) - return false if @except && @except.include?(environment) - true - end - - def to_s - to_gem_dependency.to_s - end - - def to_gem_dependency - @gem_dep ||= Gem::Dependency.new(name, version) - end - - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/finder.rb b/railties/lib/vendor/bundler/lib/bundler/finder.rb deleted file mode 100644 index 43ff370ae4..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/finder.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Bundler - class Finder - def initialize(*sources) - @results = {} - @index = Hash.new { |h,k| h[k] = {} } - - sources.each { |source| fetch(source) } - end - - def resolve(*dependencies) - resolved = Resolver.resolve(dependencies, self) - resolved && GemBundle.new(resolved.all_specs) - end - - def fetch(source) - deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z") - inflated = Gem.inflate deflated - - append(Marshal.load(inflated), source) - rescue Gem::RemoteFetcher::FetchError => e - raise ArgumentError, "#{source} is not a valid source: #{e.message}" - end - - def append(index, source) - index.gems.values.each do |spec| - next unless Gem::Platform.match(spec.platform) - spec.source = source - @index[spec.name][spec.version] ||= spec - end - self - end - - def search(dependency) - @results[dependency.hash] ||= begin - possibilities = @index[dependency.name].values - possibilities.select do |spec| - dependency =~ spec - end.sort_by {|s| s.version } - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb b/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb deleted file mode 100644 index b749720fd9..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Bundler - class GemBundle < Array - def download(directory) - FileUtils.mkdir_p(directory) - - current = Dir[File.join(directory, "cache", "*.gem*")] - - each do |spec| - cached = File.join(directory, "cache", "#{spec.full_name}.gem") - - unless File.file?(cached) - Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory) - end - - current.delete(cached) - end - - current.each { |file| File.delete(file) } - - self - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb b/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb deleted file mode 100644 index 680b61fd69..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Gem - class Specification - attribute :source - - def source=(source) - @source = source.is_a?(URI) ? source : URI.parse(source) - raise ArgumentError, "The source must be an absolute URI" unless @source.absolute? - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/installer.rb b/railties/lib/vendor/bundler/lib/bundler/installer.rb deleted file mode 100644 index 581d9d3c8e..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/installer.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Bundler - class Installer - def initialize(path) - if !File.directory?(path) - raise ArgumentError, "#{path} is not a directory" - elsif !File.directory?(File.join(path, "cache")) - raise ArgumentError, "#{path} is not a valid environment (it does not contain a cache directory)" - end - - @path = path - @gems = Dir[(File.join(path, "cache", "*.gem"))] - end - - def install(options = {}) - bin_dir = options[:bin_dir] ||= File.join(@path, "bin") - - specs = Dir[File.join(@path, "specifications", "*.gemspec")] - gems = Dir[File.join(@path, "gems", "*")] - - @gems.each do |gem| - name = File.basename(gem).gsub(/\.gem$/, '') - installed = specs.any? { |g| File.basename(g) == "#{name}.gemspec" } && - gems.any? { |g| File.basename(g) == name } - - unless installed - installer = Gem::Installer.new(gem, :install_dir => @path, - :ignore_dependencies => true, - :env_shebang => true, - :wrappers => true, - :bin_dir => bin_dir) - installer.install - end - - # remove this spec - specs.delete_if { |g| File.basename(g) == "#{name}.gemspec"} - gems.delete_if { |g| File.basename(g) == name } - end - - (specs + gems).each do |path| - FileUtils.rm_rf(path) - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/manifest.rb b/railties/lib/vendor/bundler/lib/bundler/manifest.rb deleted file mode 100644 index 847a41940d..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/manifest.rb +++ /dev/null @@ -1,130 +0,0 @@ -require "rubygems/source_index" -require "pathname" - -module Bundler - class VersionConflict < StandardError; end - - class Manifest - attr_reader :sources, :dependencies, :path - - def initialize(sources, dependencies, path) - sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) } - @sources, @dependencies, @path = sources, dependencies, Pathname.new(path) - end - - def fetch - return if all_gems_installed? - - finder = Finder.new(*sources) - unless bundle = finder.resolve(*gem_dependencies) - gems = @dependencies.map {|d| " #{d.to_s}" }.join("\n") - raise VersionConflict, "No compatible versions could be found for:\n#{gems}" - end - - bundle.download(@path) - end - - def install(options = {}) - fetch - installer = Installer.new(@path) - installer.install # options come here - create_load_paths_files(File.join(@path, "environments")) - create_fake_rubygems(File.join(@path, "environments")) - end - - def activate(environment = "default") - require File.join(@path, "environments", "#{environment}.rb") - end - - def require_all - dependencies.each do |dep| - dep.require_as.each {|file| require file } - end - end - - def gems_for(environment) - deps = dependencies.select { |d| d.in?(environment) } - deps.map! { |d| d.to_gem_dependency } - index = Gem::SourceIndex.from_gems_in(File.join(@path, "specifications")) - Resolver.resolve(deps, index).all_specs - end - - def environments - envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten - envs << "default" - end - - private - - def gem_dependencies - @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency } - end - - def all_gems_installed? - gem_versions = {} - - Dir[File.join(@path, "cache", "*.gem")].each do |file| - file =~ /\/([^\/]+)-([\d\.]+)\.gem$/ - name, version = $1, $2 - gem_versions[name] = Gem::Version.new(version) - end - - gem_dependencies.all? do |dep| - gem_versions[dep.name] && - dep.version_requirements.satisfied_by?(gem_versions[dep.name]) - end - end - - def create_load_paths_files(path) - FileUtils.mkdir_p(path) - environments.each do |environment| - gem_specs = gems_for(environment) - File.open(File.join(path, "#{environment}.rb"), "w") do |file| - file.puts <<-RUBY_EVAL - module Bundler - def self.rubygems_required - #{create_gem_stubs(path, gem_specs)} - end - end - RUBY_EVAL - file.puts "$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))" - load_paths_for_specs(gem_specs).each do |load_path| - file.puts "$LOAD_PATH.unshift #{load_path.inspect}" - end - end - end - end - - def create_gem_stubs(path, gem_specs) - gem_specs.map do |spec| - path = File.expand_path(File.join(path, '..', 'specifications', "#{spec.full_name}.gemspec")) - %{ - Gem.loaded_specs["#{spec.name}"] = eval(File.read("#{path}")) - } - end.join("\n") - end - - def create_fake_rubygems(path) - File.open(File.join(path, "rubygems.rb"), "w") do |file| - file.puts <<-RUBY_EVAL - $:.delete File.expand_path(File.dirname(__FILE__)) - load "rubygems.rb" - if defined?(Bundler) && Bundler.respond_to?(:rubygems_required) - Bundler.rubygems_required - end - RUBY_EVAL - end - end - - def load_paths_for_specs(specs) - load_paths = [] - specs.each do |spec| - load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir - spec.require_paths.each do |path| - load_paths << File.join(spec.full_gem_path, path) - end - end - load_paths - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver.rb b/railties/lib/vendor/bundler/lib/bundler/resolver.rb deleted file mode 100644 index 1ec89e53c4..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'bundler/resolver/inspect' -require 'bundler/resolver/search' -require 'bundler/resolver/engine' -require 'bundler/resolver/stack' -require 'bundler/resolver/state' - -module Bundler - module Resolver - def self.resolve(deps, source_index = Gem.source_index, logger = nil) - unless logger - logger = Logger.new($stderr) - logger.datetime_format = "" - logger.level = ENV["GEM_RESOLVER_DEBUG"] ? Logger::DEBUG : Logger::ERROR - end - - Engine.resolve(deps, source_index, logger) - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb deleted file mode 100644 index 2b7b48211c..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Bundler - module Resolver - module Builders - def build_index(&block) - index = Gem::SourceIndex.new - IndexBuilder.run(index, &block) if block_given? - index - end - - def build_spec(name, version, &block) - spec = Gem::Specification.new - spec.instance_variable_set(:@name, name) - spec.instance_variable_set(:@version, Gem::Version.new(version)) - DepBuilder.run(spec, &block) if block_given? - spec - end - - def build_dep(name, requirements, type = :runtime) - Gem::Dependency.new(name, requirements, type) - end - - class IndexBuilder - include Builders - - def self.run(index, &block) - new(index).run(&block) - end - - def initialize(index) - @index = index - end - - def run(&block) - instance_eval(&block) - end - - def add_spec(*args, &block) - @index.add_spec(build_spec(*args, &block)) - end - end - - class DepBuilder - def self.run(spec, &block) - new(spec).run(&block) - end - - def initialize(spec) - @spec = spec - end - - def run(&block) - instance_eval(&block) - end - - def runtime(name, requirements) - @spec.add_runtime_dependency(name, requirements) - end - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb deleted file mode 100644 index 475ba516ff..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Bundler - module Resolver - class ClosedSet < Set - end - - class Engine - include Search, Inspect - - def self.resolve(deps, source_index, logger) - new(deps, source_index, logger).resolve - end - - def initialize(deps, source_index, logger) - @deps, @source_index, @logger = deps, source_index, logger - logger.debug "searching for #{gem_resolver_inspect(@deps)}" - end - attr_reader :deps, :source_index, :logger, :solution - - def resolve - state = State.initial(self, [], Stack.new, Stack.new([[[], @deps.dup]])) - if solution = search(state) - logger.info "got the solution with #{solution.all_specs.size} specs" - solution.dump(Logger::INFO) - solution - end - end - - def open - @open ||= [] - end - - def closed - @closed ||= ClosedSet.new - end - end - end - -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb deleted file mode 100644 index 59640aa5f3..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Bundler - module Resolver - module Inspect - def gem_resolver_inspect(o) - case o - when Gem::Specification - "#<Spec: #{o.full_name}>" - when Array - '[' + o.map {|x| gem_resolver_inspect(x)}.join(", ") + ']' - when Set - gem_resolver_inspect(o.to_a) - when Hash - '{' + o.map {|k,v| "#{gem_resolver_inspect(k)} => #{gem_resolver_inspect(v)}"}.join(", ") + '}' - when Stack - o.gem_resolver_inspect - else - o.inspect - end - end - - module_function :gem_resolver_inspect - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb deleted file mode 100644 index 34102ff04c..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb +++ /dev/null @@ -1,71 +0,0 @@ -module Bundler - module Resolver - module Search - def search(initial, max_depth = (1.0 / 0.0)) - if initial.goal_met? - return initial - end - - open << initial - - while open.any? - current = open.pop - closed << current - - new = [] - current.each_possibility do |attempt| - unless closed.include?(attempt) - if attempt.goal_met? - return attempt - elsif attempt.depth < max_depth - new << attempt - end - end - end - new.reverse.each do |state| - open << state - end - end - - nil - end - - def open - raise "implement #open in #{self.class}" - end - - def closed - raise "implement #closed in #{self.class}" - end - - module Node - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def initial(*data) - new(0, *data) - end - end - - def initialize(depth) - @depth = depth - end - attr_reader :depth - - def child(*data) - self.class.new(@depth + 1, *data) - end - - def each_possibility - raise "implement #each_possibility on #{self.class}" - end - - def goal_met? - raise "implement #goal_met? on #{self.class}" - end - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb deleted file mode 100644 index 6e1ac67e1f..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Bundler - module Resolver - class Stack - def initialize(initial = []) - @data = [] - initial.each do |(path,value)| - self[path] = value - end - end - - def last - @data.last - end - - def []=(path, value) - raise ArgumentError, "#{path.inspect} already has a value" if key?(path) - @data << [path.dup, value] - end - - def [](path) - if key?(path) - _, value = @data.find do |(k,v)| - k == path - end - value - else - raise "No value for #{path.inspect}" - end - end - - def key?(path) - @data.any? do |(k,v)| - k == path - end - end - - def each - @data.each do |(k,v)| - yield k, v - end - end - - def map - @data.map do |(k,v)| - yield k, v - end - end - - def each_value - @data.each do |(k,v)| - yield v - end - end - - def dup - self.class.new(@data.dup) - end - - def to_s - @data.to_s - end - - def inspect - @data.inspect - end - - def gem_resolver_inspect - Inspect.gem_resolver_inspect(@data) - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb deleted file mode 100644 index f13ecbbee7..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb +++ /dev/null @@ -1,172 +0,0 @@ -module Bundler - module Resolver - class State - include Search::Node, Inspect - - def initialize(depth, engine, path, spec_stack, dep_stack) - super(depth) - @engine, @path, @spec_stack, @dep_stack = engine, path, spec_stack, dep_stack - end - attr_reader :path - - def logger - @engine.logger - end - - def goal_met? - logger.info "checking if goal is met" - dump - no_duplicates? - all_deps.all? do |dep| - dependency_satisfied?(dep) - end - end - - def no_duplicates? - names = [] - all_specs.each do |s| - if names.include?(s.name) - raise "somehow got duplicates for #{s.name}" - end - names << s.name - end - end - - def dependency_satisfied?(dep) - all_specs.any? do |spec| - spec.satisfies_requirement?(dep) - end - end - - def each_possibility(&block) - index, dep = remaining_deps.first - if dep - logger.warn "working on #{dep} for #{spec_name}" - handle_dep(index, dep, &block) - else - logger.warn "no dependencies left for #{spec_name}" - jump_to_parent(&block) - end - end - - def handle_dep(index, dep) - specs = @engine.source_index.search(dep) - - specs.reverse.each do |s| - logger.info "attempting with spec: #{s.full_name}" - new_path = @path + [index] - new_spec_stack = @spec_stack.dup - new_dep_stack = @dep_stack.dup - - new_spec_stack[new_path] = s - new_dep_stack[new_path] = s.runtime_dependencies.sort_by do |dep| - @engine.source_index.search(dep).size - end - yield child(@engine, new_path, new_spec_stack, new_dep_stack) - end - end - - def jump_to_parent - if @path.empty? - dump - logger.warn "at the end" - return - end - - logger.info "jumping to parent for #{spec_name}" - new_path = @path[0..-2] - new_spec_stack = @spec_stack.dup - new_dep_stack = @dep_stack.dup - - yield child(@engine, new_path, new_spec_stack, new_dep_stack) - end - - def remaining_deps - remaining_deps_for(@path) - end - - def remaining_deps_for(path) - no_duplicates? - remaining = [] - @dep_stack[path].each_with_index do |dep,i| - remaining << [i, dep] unless all_specs.find {|s| s.name == dep.name} - end - remaining - end - - def deps - @dep_stack[@path] - end - - def spec - @spec_stack[@path] - end - - def spec_name - @path.empty? ? "<top>" : spec.full_name - end - - def all_deps - all_deps = Set.new - @dep_stack.each_value do |deps| - all_deps.merge(deps) - end - all_deps.to_a - end - - def all_specs - @spec_stack.map do |path,spec| - spec - end - end - - def dump(level = Logger::DEBUG) - logger.add level, "v" * 80 - logger.add level, "path: #{@path.inspect}" - logger.add level, "deps: (#{deps.size})" - deps.map do |dep| - logger.add level, gem_resolver_inspect(dep) - end - logger.add level, "remaining_deps: (#{remaining_deps.size})" - remaining_deps.each do |dep| - logger.add level, gem_resolver_inspect(dep) - end - logger.add level, "dep_stack: " - @dep_stack.each do |path,deps| - logger.add level, "#{path.inspect} (#{deps.size})" - deps.each do |dep| - logger.add level, "-> #{gem_resolver_inspect(dep)}" - end - end - logger.add level, "spec_stack: " - @spec_stack.each do |path,spec| - logger.add level, "#{path.inspect}: #{gem_resolver_inspect(spec)}" - end - logger.add level, "^" * 80 - end - - def to_dot - io = StringIO.new - io.puts 'digraph deps {' - io.puts ' fontname = "Courier";' - io.puts ' mincross = 4.0;' - io.puts ' ratio = "auto";' - dump_to_dot(io, "<top>", []) - io.puts '}' - io.string - end - - def dump_to_dot(io, name, path) - @dep_stack[path].each_with_index do |dep,i| - new_path = path + [i] - spec_name = all_specs.find {|x| x.name == dep.name}.full_name - io.puts ' "%s" -> "%s";' % [name, dep.to_s] - io.puts ' "%s" -> "%s";' % [dep.to_s, spec_name] - if @spec_stack.key?(new_path) - dump_to_dot(io, spec_name, new_path) - end - end - end - end - end -end
\ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/runtime.rb b/railties/lib/vendor/bundler/lib/bundler/runtime.rb deleted file mode 100644 index 2376deef28..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/runtime.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Bundler - class ManifestBuilder - - attr_reader :sources - - def self.build(path, string) - builder = new(path) - builder.instance_eval(string) - builder.to_manifest - end - - def self.load(path, file) - string = File.read(file) - build(path, string) - end - - def initialize(path) - @path = path - @sources = %w(http://gems.rubyforge.org) - @dependencies = [] - end - - def to_manifest - Manifest.new(@sources, @dependencies, @path) - end - - def source(source) - @sources << source - end - - def gem(name, *args) - options = args.last.is_a?(Hash) ? args.pop : {} - version = args.last - - @dependencies << Dependency.new(name, options.merge(:version => version)) - end - - end -end
\ No newline at end of file diff --git a/railties/rails.gemspec b/railties/rails.gemspec new file mode 100644 index 0000000000..659d6727ea --- /dev/null +++ b/railties/rails.gemspec @@ -0,0 +1,31 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'rails' + s.version = '3.0.pre' + s.summary = "Web-application framework with template engine, control-flow layer, and ORM." + 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('activerecord', '= 3.0.pre') + s.add_dependency('actionpack', '= 3.0.pre') + s.add_dependency('actionmailer', '= 3.0.pre') + s.add_dependency('activeresource', '= 3.0.pre') + + s.rdoc_options << '--exclude' << '.' + s.has_rdoc = false + + s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'builtin/**/*', 'guides/**/*', 'lib/**/*'] + 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/abstract_unit.rb b/railties/test/abstract_unit.rb index 9a640bdbc5..4510e6241c 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -1,5 +1,9 @@ ORIG_ARGV = ARGV.dup +require 'rubygems' +gem 'rack', '~> 1.0.0' +gem 'rack-test', '~> 0.5.0' + $:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" $:.unshift File.dirname(__FILE__) + "/../../activerecord/lib" $:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" @@ -9,12 +13,13 @@ $:.unshift File.dirname(__FILE__) + "/../lib" $:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" require 'stringio' -require 'rubygems' require 'test/unit' require 'active_support' require 'active_support/test_case' +require 'action_controller' + if defined?(RAILS_ROOT) RAILS_ROOT.replace File.dirname(__FILE__) else diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb new file mode 100644 index 0000000000..5158abdbb4 --- /dev/null +++ b/railties/test/application/load_test.rb @@ -0,0 +1,51 @@ +require "isolation/abstract_unit" +# require "rails" +# require 'action_dispatch' + +module ApplicationTests + class LoadTest < Test::Unit::TestCase + 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 + end + + def setup + build_app + boot_rails + end + + test "rails app is present" do + assert File.exist?(app_path("config")) + end + + test "config.ru can be racked up" do + @app = rackup + assert_welcome get("/") + end + + test "Rails.application is available after config.ru has been racked up" do + rackup + assert Rails.application.new.is_a?(Rails::Application) + end + + test "the config object is available on the application object" do + rackup + assert_equal 'UTC', Rails.application.config.time_zone + end + end +end diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb index 7a1b361440..c3e4f970fe 100644 --- a/railties/test/backtrace_cleaner_test.rb +++ b/railties/test/backtrace_cleaner_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -require 'initializer' +require 'rails/initializer' require 'rails/backtrace_cleaner' if defined? Test::Unit::Util::BacktraceFilter @@ -14,13 +14,13 @@ if defined? Test::Unit::Util::BacktraceFilter @test = TestWithBacktrace.new @backtrace = [ './test/rails/benchmark_test.rb', './test/rails/dependencies.rb', '/opt/local/lib/ruby/kernel.rb' ] end - + test "test with backtrace should use the rails backtrace cleaner to clean" do Rails.stubs(:backtrace_cleaner).returns(stub(:clean)) Rails.backtrace_cleaner.expects(:clean).with(@backtrace, nil) @test.filter_backtrace(@backtrace) end - + test "filter backtrace should have the same arity as Test::Unit::Util::BacktraceFilter" do assert_nothing_raised do @test.filter_backtrace(@backtrace, '/opt/local/lib') diff --git a/railties/test/boot_test.rb b/railties/test/boot_test.rb index 7bf420d4f2..1280d27ffe 100644 --- a/railties/test/boot_test.rb +++ b/railties/test/boot_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -require 'initializer' -require "#{File.dirname(__FILE__)}/../lib/generators/rails/app/templates/config/boot" +require 'rails/initializer' +require "#{File.dirname(__FILE__)}/../lib/rails/generators/rails/app/templates/config/boot" require 'rails/gem_dependency' class BootTest < Test::Unit::TestCase @@ -48,13 +48,6 @@ class BootTest < Test::Unit::TestCase Rails::GemBoot.any_instance.expects(:run).returns('result') assert_equal 'result', Rails.boot! end - - def test_run_loads_initializer_and_sets_load_path - boot = Rails::Boot.new - boot.expects(:load_initializer) - Rails::Initializer.expects(:run).with(:set_load_path) - boot.run - end end class VendorBootTest < Test::Unit::TestCase @@ -62,8 +55,8 @@ class VendorBootTest < Test::Unit::TestCase def test_load_initializer_requires_from_vendor_rails boot = VendorBoot.new - boot.expects(:require).with("#{RAILS_ROOT}/vendor/rails/railties/lib/initializer") - Rails::Initializer.expects(:run).with(:install_gem_spec_stubs) + boot.expects(:require).with("rails") + boot.expects(:install_gem_spec_stubs) Rails::GemDependency.expects(:add_frozen_gem_path) boot.load_initializer end @@ -76,7 +69,7 @@ class GemBootTest < Test::Unit::TestCase boot = GemBoot.new GemBoot.expects(:load_rubygems) boot.expects(:load_rails_gem) - boot.expects(:require).with('initializer') + boot.expects(:require).with('rails') boot.load_initializer end diff --git a/railties/test/console_app_test.rb b/railties/test/console_app_test.rb index f11de087e3..1437e6d885 100644 --- a/railties/test/console_app_test.rb +++ b/railties/test/console_app_test.rb @@ -2,13 +2,14 @@ require 'abstract_unit' require 'action_controller' # console_app uses 'action_controller/integration' -unless defined? ApplicationController - class ApplicationController < ActionController::Base; end - ActionController::Base.session_store = nil -end +require 'rails/dispatcher' +require 'rails/console_app' -require 'dispatcher' -require 'console_app' +module Rails + def self.application + ActionController::Routing::Routes + end +end # console_app sets Test::Unit.run to work around the at_exit hook in test/unit, which kills IRB if Test::Unit.respond_to?(:run=) @@ -27,9 +28,9 @@ if Test::Unit.respond_to?(:run=) def test_reload_should_fire_preparation_callbacks a = b = c = nil - Dispatcher.to_prepare { a = b = c = 1 } - Dispatcher.to_prepare { b = c = 2 } - Dispatcher.to_prepare { c = 3 } + ActionDispatch::Callbacks.to_prepare { a = b = c = 1 } + ActionDispatch::Callbacks.to_prepare { b = c = 2 } + ActionDispatch::Callbacks.to_prepare { c = 3 } ActionController::Routing::Routes.expects(:reload) reload! diff --git a/railties/test/fcgi_dispatcher_test.rb b/railties/test/fcgi_dispatcher_test.rb index c7a7f77118..4d77a321a0 100644 --- a/railties/test/fcgi_dispatcher_test.rb +++ b/railties/test/fcgi_dispatcher_test.rb @@ -3,9 +3,13 @@ require 'abstract_unit' uses_gem "fcgi", "0.8.7" do require 'action_controller' -require 'fcgi_handler' +require 'rails/fcgi_handler' -Dispatcher.middleware.clear +module Rails + def self.application + ActionController::Routing::Routes + end +end class RailsFCGIHandlerTest < Test::Unit::TestCase def setup diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb index 7a4edb8bcb..a7d079a1bc 100644 --- a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb +++ b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb @@ -1,4 +1,4 @@ -require 'generators/active_record' +require 'rails/generators/active_record' module ActiveRecord module Generators diff --git a/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml b/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml new file mode 100644 index 0000000000..641a7e035c --- /dev/null +++ b/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml @@ -0,0 +1,2 @@ +en: + hello: "Hello from Engine" diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index fdaef6d9cb..f226e184d1 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/app/app_generator' +require 'rails/generators/rails/app/app_generator' class ActionsTest < GeneratorsTestCase def setup diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 19e41c15c8..afc0585fba 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/app/app_generator' +require 'rails/generators/rails/app/app_generator' class AppGeneratorTest < GeneratorsTestCase @@ -55,14 +55,12 @@ class AppGeneratorTest < GeneratorsTestCase def test_dispatchers_are_not_added_by_default run_generator - assert_no_file "config.ru" assert_no_file "public/dispatch.cgi" assert_no_file "public/dispatch.fcgi" end def test_dispatchers_are_added_if_required run_generator ["--with-dispatchers"] - assert_file "config.ru" assert_file "public/dispatch.cgi" assert_file "public/dispatch.fcgi" end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index 850b9e632d..56bc688ad0 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/controller/controller_generator' +require 'rails/generators/rails/controller/controller_generator' class ControllerGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb index b1ebe5f1b5..aea3f4da51 100644 --- a/railties/test/generators/generator_generator_test.rb +++ b/railties/test/generators/generator_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/generator/generator_generator' +require 'rails/generators/rails/generator/generator_generator' class GeneratorGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index a258574dce..d917812383 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -9,10 +9,12 @@ else end $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" +require 'rails/generators' + +require 'rubygems' $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib" $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../actionpack/lib" -require 'generators' -require 'activerecord' +require 'active_record' require 'action_dispatch' CURRENT_PATH = File.expand_path(Dir.pwd) diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb index 3d655016f1..f8bfc517a2 100644 --- a/railties/test/generators/helper_generator_test.rb +++ b/railties/test/generators/helper_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/helper/helper_generator' +require 'rails/generators/rails/helper/helper_generator' ObjectHelper = Class.new AnotherObjectHelperTest = Class.new diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb index 190b28012c..6a504ceea2 100644 --- a/railties/test/generators/integration_test_generator_test.rb +++ b/railties/test/generators/integration_test_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/integration_test/integration_test_generator' +require 'rails/generators/rails/integration_test/integration_test_generator' class IntegrationTestGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index 9e74859c3a..251474ad16 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/mailer/mailer_generator' +require 'rails/generators/rails/mailer/mailer_generator' class MailerGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb index 97e892cefb..80bf342892 100644 --- a/railties/test/generators/metal_generator_test.rb +++ b/railties/test/generators/metal_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/metal/metal_generator' +require 'rails/generators/rails/metal/metal_generator' class MetalGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index a4335068e6..35172a8be4 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/migration/migration_generator' +require 'rails/generators/rails/migration/migration_generator' class MigrationGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 501c7d10c6..e073b11e1e 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/model/model_generator' +require 'rails/generators/rails/model/model_generator' class ModelGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 3a5c58ab32..98cbf9b8f6 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/scaffold_controller/scaffold_controller_generator' +require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator' # Mock out what we need from AR::Base. module ActiveRecord diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb index a556731e16..6fed2998dd 100644 --- a/railties/test/generators/observer_generator_test.rb +++ b/railties/test/generators/observer_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/observer/observer_generator' +require 'rails/generators/rails/observer/observer_generator' class ObserverGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb index 74c9c20c4c..d19128f79a 100644 --- a/railties/test/generators/performance_test_generator_test.rb +++ b/railties/test/generators/performance_test_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/performance_test/performance_test_generator' +require 'rails/generators/rails/performance_test/performance_test_generator' class PerformanceTestGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 3982586bc3..f5b8b6ffb6 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/plugin/plugin_generator' +require 'rails/generators/rails/plugin/plugin_generator' class PluginGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 876ddbf84e..dcae81c204 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/resource/resource_generator' +require 'rails/generators/rails/resource/resource_generator' class ResourceGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 834e43e776..f555725eb8 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/scaffold_controller/scaffold_controller_generator' +require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator' class ScaffoldControllerGeneratorTest < GeneratorsTestCase @@ -100,7 +100,7 @@ class ScaffoldControllerGeneratorTest < GeneratorsTestCase def test_error_is_shown_if_orm_does_not_provide_interface error = capture(:stderr){ run_generator ["User", "--orm=unknown"] } assert_equal "Could not load Unknown::Generators::ActiveModel, skipping controller. " << - "Error: no such file to load -- generators/unknown.\n", error + "Error: no such file to load -- rails/generators/unknown.\n", error end protected diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 05eadd3460..c0652c034f 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/scaffold/scaffold_generator' +require 'rails/generators/rails/scaffold/scaffold_generator' class ScaffoldGeneratorTest < GeneratorsTestCase @@ -75,6 +75,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase edit new show + _form ).each { |view| assert_file "app/views/product_lines/#{view}.html.erb" } assert_file "app/views/layouts/product_lines.html.erb" diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb index 293b903b87..34fb996b7f 100644 --- a/railties/test/generators/session_migration_generator_test.rb +++ b/railties/test/generators/session_migration_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/session_migration/session_migration_generator' +require 'rails/generators/rails/session_migration/session_migration_generator' class SessionMigrationGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb index 02082d5cc8..15263d4bb8 100644 --- a/railties/test/generators/stylesheets_generator_test.rb +++ b/railties/test/generators/stylesheets_generator_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'generators/generators_test_helper' -require 'generators/rails/stylesheets/stylesheets_generator' +require 'rails/generators/rails/stylesheets/stylesheets_generator' class StylesheetsGeneratorTest < GeneratorsTestCase diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 4cc0b33521..7e6b7b183c 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -1,6 +1,6 @@ require File.join(File.dirname(__FILE__), 'generators', 'generators_test_helper') -require 'generators/rails/model/model_generator' -require 'generators/test_unit/model/model_generator' +require 'rails/generators/rails/model/model_generator' +require 'rails/generators/test_unit/model/model_generator' require 'mocha' class GeneratorsTest < GeneratorsTestCase diff --git a/railties/test/initializer/boot_test.rb b/railties/test/initializer/boot_test.rb new file mode 100644 index 0000000000..5ee3c45b21 --- /dev/null +++ b/railties/test/initializer/boot_test.rb @@ -0,0 +1,16 @@ +require "isolation/abstract_unit" + +module BootTests + class GemBooting < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def setup + # build_app + # boot_rails + end + + test "booting rails sets the load paths correctly" do + # This test is pending reworking the boot process + end + end +end
\ No newline at end of file diff --git a/railties/test/initializer/check_ruby_version_test.rb b/railties/test/initializer/check_ruby_version_test.rb index 68feba058e..1852fea4df 100644 --- a/railties/test/initializer/check_ruby_version_test.rb +++ b/railties/test/initializer/check_ruby_version_test.rb @@ -1,9 +1,14 @@ -require "initializer/test_helper" +require "isolation/abstract_unit" module InitializerTests class PathsTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation + def setup + build_app + boot_rails + end + test "rails does not initialize with ruby version 1.8.1" do assert_rails_does_not_boot "1.8.1" end diff --git a/railties/test/initializer/install_gem_spec_stubs_test.rb b/railties/test/initializer/install_gem_spec_stubs_test.rb deleted file mode 100644 index cfb12d7405..0000000000 --- a/railties/test/initializer/install_gem_spec_stubs_test.rb +++ /dev/null @@ -1,86 +0,0 @@ -require "initializer/test_helper" - -module InitializerTests - class GemSpecStubsTest < Test::Unit::TestCase - include ActiveSupport::Testing::Isolation - - def setup - $stderr = StringIO.new - end - - test "user has an old boot.rb (defined by having no Rails.vendor_rails?)" do - class << Rails - undef vendor_rails? - end - - assert_stderr(/outdated/) do - assert_raises(SystemExit) do - Rails::Initializer.run { |c| c.frameworks = [] } - end - end - end - - test "requires rubygems" do - Kernel.module_eval do - alias old_require require - def require(name) - $rubygems_required = true if name == "rubygems" - old_require(name) - end - end - - Rails.vendor_rails = true - Rails::Initializer.run { |c| c.frameworks = [] } - assert $rubygems_required - end - - # Pending until we're further along - # test "does not fail if rubygems does not exist" do - # Kernel.module_eval do - # alias old_require require - # def require(name) - # raise LoadError if name == "rubygems" - # old_require(name) - # end - # end - # - # assert_nothing_raised do - # Rails::Initializer.run { |c| c.frameworks = [] } - # end - # end - - test "adds fake Rubygems stubs if a framework is not loaded in Rubygems and we've vendored" do - Rails.vendor_rails = true - - Rails::Initializer.run { |c| c.frameworks = [] } - - %w(rails activesupport activerecord actionpack actionmailer activeresource).each do |stub| - gem_spec = Gem.loaded_specs[stub] - assert_equal Gem::Version.new(Rails::VERSION::STRING), gem_spec.version - assert_equal stub, gem_spec.name - assert_equal "", gem_spec.loaded_from - end - end - - test "doesn't replace gem specs that are already loaded" do - Rails.vendor_rails = true - - Gem.loaded_specs["rails"] = Gem::Specification.new do |s| - s.name = "rails" - s.version = Rails::VERSION::STRING - s.loaded_from = "/foo/bar/baz" - end - - Rails::Initializer.run { |c| c.frameworks = [] } - - assert_equal "/foo/bar/baz", Gem.loaded_specs["rails"].loaded_from - - %w(activesupport activerecord actionpack actionmailer activeresource).each do |stub| - gem_spec = Gem.loaded_specs[stub] - assert_equal Gem::Version.new(Rails::VERSION::STRING), gem_spec.version - assert_equal stub, gem_spec.name - assert_equal "", gem_spec.loaded_from - end - end - end -end
\ No newline at end of file diff --git a/railties/test/initializer/path_test.rb b/railties/test/initializer/path_test.rb index 8de3161546..a4264bc31c 100644 --- a/railties/test/initializer/path_test.rb +++ b/railties/test/initializer/path_test.rb @@ -1,26 +1,36 @@ -require "initializer/test_helper" +require "isolation/abstract_unit" class PathsTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation - def self.setup + def setup + build_app + boot_rails Rails::Initializer.run do |config| config.frameworks = [:action_controller, :action_view, :action_mailer, :active_record] + config.after_initialize do + ActionController::Base.session_store = nil + end end - end - - def setup @paths = Rails::Initializer.default.config.paths end def root(*path) - File.expand_path(File.join(File.dirname(__FILE__), "root", *path)) + app_path(*path).to_s end def assert_path(paths, *dir) assert_equal [root(*dir)], paths.paths end + def assert_in_load_path(*path) + assert $:.any? { |p| File.expand_path(p) == root(*path) }, "Load path does not include '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" + end + + def assert_not_in_load_path(*path) + assert !$:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" + end + test "booting up Rails yields a valid paths object" do assert_path @paths.app, "app" assert_path @paths.app.metals, "app", "metal" @@ -36,8 +46,7 @@ class PathsTest < Test::Unit::TestCase assert_path @paths.config.locales, "config", "locales" assert_path @paths.config.environments, "config", "environments" - assert_equal Pathname.new(File.dirname(__FILE__)).join("root", "app", "controllers").expand_path, - Pathname.new(@paths.app.controllers.to_a.first).expand_path + assert_equal root("app", "controllers"), @paths.app.controllers.to_a.first assert_equal Pathname.new(File.dirname(__FILE__)).join("..", "..", "builtin", "rails_info").expand_path, Pathname.new(@paths.app.controllers.to_a[1]).expand_path end @@ -53,24 +62,16 @@ class PathsTest < Test::Unit::TestCase assert_equal "#{RAILS_ENV}.rb", @paths.config.environments.glob end - def assert_in_load_path(*path) - assert $:.any? { |p| File.expand_path(p) == root(*path) }, "Load path does not include '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" - end - - def assert_not_in_load_path(*path) - assert !$:.any? { |p| File.expand_path(p) == root(*path) }, "Load path includes '#{root(*path)}'. They are:\n-----\n #{$:.join("\n")}\n-----" - end - test "load path includes each of the paths in config.paths as long as the directories exist" do assert_in_load_path "app" assert_in_load_path "app", "controllers" - assert_in_load_path "app", "metal" assert_in_load_path "app", "models" assert_in_load_path "app", "helpers" assert_in_load_path "lib" assert_in_load_path "vendor" assert_not_in_load_path "app", "views" + assert_not_in_load_path "app", "metal" assert_not_in_load_path "app", "services" assert_not_in_load_path "config" assert_not_in_load_path "config", "locales" diff --git a/railties/test/initializer/root/app/controllers/.keep b/railties/test/initializer/root/app/controllers/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/app/controllers/.keep +++ /dev/null diff --git a/railties/test/initializer/root/app/helpers/.keep b/railties/test/initializer/root/app/helpers/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/app/helpers/.keep +++ /dev/null diff --git a/railties/test/initializer/root/app/metal/.keep b/railties/test/initializer/root/app/metal/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/app/metal/.keep +++ /dev/null diff --git a/railties/test/initializer/root/app/models/.keep b/railties/test/initializer/root/app/models/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/app/models/.keep +++ /dev/null diff --git a/railties/test/initializer/root/app/views/.keep b/railties/test/initializer/root/app/views/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/app/views/.keep +++ /dev/null diff --git a/railties/test/initializer/root/config/database.yml b/railties/test/initializer/root/config/database.yml deleted file mode 100644 index ce3356be0c..0000000000 --- a/railties/test/initializer/root/config/database.yml +++ /dev/null @@ -1,4 +0,0 @@ -development: - adapter: sqlite3 - database: db/railties.db - timeout: 5000
\ No newline at end of file diff --git a/railties/test/initializer/root/config/environments/.keep b/railties/test/initializer/root/config/environments/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/config/environments/.keep +++ /dev/null diff --git a/railties/test/initializer/root/config/locales/.keep b/railties/test/initializer/root/config/locales/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/config/locales/.keep +++ /dev/null diff --git a/railties/test/initializer/root/config/routes.rb b/railties/test/initializer/root/config/routes.rb deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/config/routes.rb +++ /dev/null diff --git a/railties/test/initializer/root/lib/.keep b/railties/test/initializer/root/lib/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/lib/.keep +++ /dev/null diff --git a/railties/test/initializer/root/tmp/.keep b/railties/test/initializer/root/tmp/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/tmp/.keep +++ /dev/null diff --git a/railties/test/initializer/root/tmp/cache/.keep b/railties/test/initializer/root/tmp/cache/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/tmp/cache/.keep +++ /dev/null diff --git a/railties/test/initializer/root/vendor/.keep b/railties/test/initializer/root/vendor/.keep deleted file mode 100644 index e69de29bb2..0000000000 --- a/railties/test/initializer/root/vendor/.keep +++ /dev/null diff --git a/railties/test/initializer/test_helper.rb b/railties/test/initializer/test_helper.rb deleted file mode 100644 index 9d7dfff1c0..0000000000 --- a/railties/test/initializer/test_helper.rb +++ /dev/null @@ -1,48 +0,0 @@ -# This is a test helper file that simulates a rails application being -# boot from scratch in vendored mode. This file should really only be -# required in test cases that use the isolation helper so that requires -# can be reset correctly. -RAILS_ROOT = File.join(File.dirname(__FILE__), "root") -RAILS_FRAMEWORK_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..')) - -require "test/unit" -# We are purposely avoiding adding things to the load path to catch bugs that only happen in the genuine article -require File.join(RAILS_FRAMEWORK_ROOT, 'activesupport', 'lib', 'active_support', 'testing', 'isolation') -require File.join(RAILS_FRAMEWORK_ROOT, 'activesupport', 'lib', 'active_support', 'testing', 'declarative') - -class Test::Unit::TestCase - extend ActiveSupport::Testing::Declarative - - def assert_stderr(match) - $stderr = StringIO.new - yield - $stderr.rewind - err = $stderr.read - assert_match match, err - ensure - $stderr = STDERR - end -end - -# Fake boot.rb -module Rails - class << self - attr_accessor :vendor_rails - - def vendor_rails? - @vendor_rails - end - - def boot! - # Require the initializer - require File.join(RAILS_FRAMEWORK_ROOT, 'railties', 'lib', 'initializer') - # Run the initializer the same way boot.rb does it - Rails::Initializer.run(:install_gem_spec_stubs) - Rails::GemDependency.add_frozen_gem_path - Rails::Initializer.run(:set_load_path) - end - end -end - -# All that for this: -Rails.boot!
\ No newline at end of file diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 1fecd62995..b67db9c835 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -require 'initializer' -require 'generators' +require 'rails/initializer' +require 'rails/generators' require 'action_view' require 'action_mailer' @@ -406,6 +406,7 @@ class InitializerSetupI18nTests < Test::Unit::TestCase File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../activemodel/lib/active_model/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"), + File.expand_path(File.dirname(__FILE__) + "/../../railties/test/fixtures/plugins/engines/engine/config/locales/en.yml"), "my/test/locale.yml", "my/other/locale.yml" ], I18n.load_path.collect { |path| path =~ /\.\./ ? File.expand_path(path) : path } end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb new file mode 100644 index 0000000000..869e8429cf --- /dev/null +++ b/railties/test/isolation/abstract_unit.rb @@ -0,0 +1,131 @@ +# Note: +# It is important to keep this file as light as possible +# the goal for tests that require this is to test booting up +# rails from an empty state, so anything added here could +# hide potential failures +# +# It is also good to know what is the bare minimum to get +# Rails booted up. + +# TODO: Remove rubygems when possible +require 'rubygems' +require 'test/unit' + +# TODO: Remove setting this magic constant +RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..") + +# These files do not require any others and are needed +# to run the tests +require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/testing/isolation" +require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/testing/declarative" + +module TestHelpers + module Paths + module_function + + def tmp_path(*args) + File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. tmp] + args)) + end + + def app_path(*args) + tmp_path(*%w[app] + args) + end + end + + module Rack + def extract_body(response) + "".tap do |body| + response[2].each {|chunk| body << chunk } + end + end + + def get(path) + @app.call(::Rack::MockRequest.env_for(path)) + end + + def assert_welcome(resp) + assert_equal 200, resp[0] + assert resp[1]["Content-Type"] = "text/html" + assert extract_body(resp).match(/Welcome aboard/) + end + + def assert_success(resp) + assert_equal 202, resp[0] + end + + def assert_missing(resp) + assert_equal 404, resp[0] + end + + def assert_header(key, value, resp) + assert_equal value, resp[1][key.to_s] + end + + def assert_body(expected, resp) + assert_equal expected, extract_body(resp) + end + end + + module Generation + def build_app(options = {}) + FileUtils.rm_rf(app_path) + FileUtils.cp_r(tmp_path('app_template'), app_path) + + # Delete the initializers unless requested + unless options[:initializers] + Dir["#{app_path}/config/initializers/*.rb"].each do |initializer| + File.delete(initializer) + end + end + + environment = File.read("#{app_path}/config/environment.rb") + if environment =~ /(\n\s*end\s*)\Z/ + File.open("#{app_path}/config/environment.rb", 'w') do |f| + f.puts $` + %'\nconfig.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }\n' + $1 + end + end + end + + def app_file(path, contents) + File.open(app_path(path), 'w') do |f| + f.puts contents + end + end + + def controller(name, contents) + app_file("app/controllers/#{name}_controller.rb", contents) + end + + def boot_rails + # TMP mega hax to prevent boot.rb from actually booting + Object.class_eval <<-RUBY, __FILE__, __LINE__+1 + module Rails + Initializer = 'lol' + require "#{app_path}/config/boot" + remove_const(:Initializer) + booter = VendorBoot.new + booter.run + end + RUBY + end + end +end + +class Test::Unit::TestCase + include TestHelpers::Paths + include TestHelpers::Rack + include TestHelpers::Generation + extend ActiveSupport::Testing::Declarative +end + +# Create a scope and build a fixture rails app +Module.new do + extend TestHelpers::Paths + # Build a rails app + if File.exist?(tmp_path) + FileUtils.rm_rf(tmp_path) + end + + FileUtils.mkdir(tmp_path) + `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails #{tmp_path('app_template')}` +end diff --git a/railties/test/metal_test.rb b/railties/test/metal_test.rb index c79a819a76..6864254e4c 100644 --- a/railties/test/metal_test.rb +++ b/railties/test/metal_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'initializer' +require 'rails/initializer' class MetalTest < Test::Unit::TestCase def test_metals_should_return_list_of_found_metal_apps diff --git a/railties/test/new_initializer_test.rb b/railties/test/new_initializer_test.rb index 8d9ef7bee3..67b66fb088 100644 --- a/railties/test/new_initializer_test.rb +++ b/railties/test/new_initializer_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'active_support/ruby/shim' -require 'initializer' +require 'rails/initializer' class InitializerRunnerTest < ActiveSupport::TestCase diff --git a/railties/test/plugin_loader_test.rb b/railties/test/plugin_loader_test.rb index 873e000222..99301347b6 100644 --- a/railties/test/plugin_loader_test.rb +++ b/railties/test/plugin_loader_test.rb @@ -156,6 +156,14 @@ class TestPluginLoader < Test::Unit::TestCase plugin_load_paths.each { |path| assert $LOAD_PATH.include?(path) } end + def test_should_add_locale_files_to_I18n_load_path + only_load_the_following_plugins! [:engine] + + @loader.send :add_engine_locales + + assert I18n.load_path.include?(File.join(plugin_fixture_path('engines/engine'), 'config', 'locales', 'en.yml')) + end + private def reset_load_path! diff --git a/railties/test/plugin_test_helper.rb b/railties/test/plugin_test_helper.rb index 893095fa66..93004e0ddf 100644 --- a/railties/test/plugin_test_helper.rb +++ b/railties/test/plugin_test_helper.rb @@ -3,7 +3,7 @@ $:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" require 'test/unit' require 'active_support' -require 'initializer' +require 'rails/initializer' require 'abstract_unit' # We need to set RAILS_ROOT if it isn't already set diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index 6a60908859..99cf9168e1 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -1,6 +1,5 @@ require 'abstract_unit' require 'action_controller' -require 'action_controller/testing/process' require 'rails/info' require 'rails/info_controller' diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index fdcc7a1ef6..dcf9966c0d 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -3,6 +3,9 @@ $:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" $:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" $:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" +require 'rubygems' +gem 'rack', '~> 1.0.0' + require 'test/unit' require 'active_support' require 'active_support/test_case' @@ -77,11 +80,11 @@ EOS end end - def test_middleware_property - assert property_defined?('Middleware') - end - def test_html_includes_middleware + Rails::Info.module_eval do + property 'Middleware', ['Rack::Lock', 'Rack::Static'] + end + html = Rails::Info.to_html assert html.include?('<tr><td class="name">Middleware</td>') properties.value_for('Middleware').each do |value| |