diff options
216 files changed, 3460 insertions, 1744 deletions
@@ -15,9 +15,11 @@ task :default => :test %w(test isolated_test rdoc pgem package release).each do |task_name| desc "Run #{task_name} task for all projects" task task_name do + errors = [] PROJECTS.each do |project| - system %(cd #{project} && #{env} #{$0} #{task_name}) + system(%(cd #{project} && #{env} #{$0} #{task_name})) || errors << project end + fail("Errors in #{errors.join(', ')}") unless errors.empty? end end diff --git a/actionpack/test/adv_attr_test.rb b/actionmailer/test/adv_attr_test.rb index fdda4ad92d..fd909a5627 100644 --- a/actionpack/test/adv_attr_test.rb +++ b/actionmailer/test/adv_attr_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/abstract_unit' +require 'abstract_unit' require 'action_mailer/adv_attr_accessor' class AdvAttrTest < Test::Unit::TestCase @@ -15,6 +15,4 @@ class AdvAttrTest < Test::Unit::TestCase assert_raise(ArgumentError) {bob.name 'x', 'y'} end - - -end
\ No newline at end of file +end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 204f5ae272..e3ad19558e 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,12 @@ *Edge* +* Change integration test helpers to accept Rack environment instead of just HTTP Headers [Pratik Naik] + + Before : get '/path', {}, 'Accept' => 'text/javascript' + After : get '/path', {}, 'HTTP_ACCEPT' => 'text/javascript' + +* Instead of checking Rails.env.test? in Failsafe middleware, check env["rails.raise_exceptions"] [Bryan Helmkamp] + * Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 6ce8179646..9ce897aae8 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -22,14 +22,15 @@ task :default => [ :test ] # 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_new_base, :test_new_base_on_old_tests] +test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"] Rake::TestTask.new(:test_action_pack) do |t| - t.libs << "test" + t.libs.concat test_lib_dirs # 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}/**/*_test.rb" ).sort + t.test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort t.verbose = true #t.warning = true @@ -37,24 +38,46 @@ 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', file) + system(ruby, "-Ilib:#{test_lib_dirs * ':'}", file) end or raise "Failures" end desc 'ActiveRecord Integration Tests' Rake::TestTask.new(:test_active_record_integration) do |t| - t.libs << "test" + t.libs.concat test_lib_dirs 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" + 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" + # layout + # Dir.glob( "test/{dispatch,template}/**/*_test.rb" ).sort + + + # ==== Not ported + # * filters + # * integration + # * test + # * view_paths + t.test_files = %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 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 url_rewriter verification webservice + ).map { |name| "test/controller/#{name}_test.rb" } + t.verbose = true +end # Genereate the RDoc documentation diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb index 84a8499daf..0fc527445e 100644 --- a/actionpack/examples/minimal.rb +++ b/actionpack/examples/minimal.rb @@ -1,3 +1,7 @@ +# Pass NEW=1 to run with the new Base +ENV['RAILS_ENV'] ||= 'production' +ENV['NO_RELOAD'] ||= '1' + $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" require 'action_controller' require 'action_controller/new_base' if ENV['NEW'] @@ -9,26 +13,30 @@ class BaseController < ActionController::Base end end -n = (ENV['N'] || 10000).to_i -input = StringIO.new('') - -def call_index(controller, input, n) - n.times do - controller.action(:index).call({ 'rack.input' => input }) +class Runner + def initialize(app) + @app = app end - puts controller.name - status, headers, body = controller.action(:index).call({ 'rack.input' => input }) + def call(env) + env['n'].to_i.times { @app.call(env) } + @app.call(env).tap { |response| report(env, response) } + end - puts status - puts headers.to_yaml - puts '---' - body.each do |part| - puts part + def report(env, response) + out = env['rack.errors'] + out.puts response[0], response[1].to_yaml, '---' + response[2].each { |part| out.puts part } + out.puts '---' end - puts '---' end -elapsed = Benchmark.realtime { call_index BaseController, input, n } +n = (ENV['N'] || 1000).to_i +input = StringIO.new('') -puts "%dms elapsed, %d requests/sec" % [1000 * elapsed, n / elapsed] +elapsed = Benchmark.realtime do + Runner.new(BaseController.action(:index)). + call('n' => n, 'rack.input' => input, 'rack.errors' => $stdout) +end +puts "%dms elapsed, %d req/sec, %.2f msec/req" % + [1000 * elapsed, n / elapsed, 1000 * elapsed / n] diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 098dfcf5a2..4033bbcbad 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -66,6 +66,7 @@ module ActionController autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' autoload :Verification, 'action_controller/base/verification' + autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' module Assertions autoload :DomAssertions, 'action_controller/testing/assertions/dom' diff --git a/actionpack/lib/action_controller/abstract.rb b/actionpack/lib/action_controller/abstract.rb index 48e33282ec..d6b7a44f5f 100644 --- a/actionpack/lib/action_controller/abstract.rb +++ b/actionpack/lib/action_controller/abstract.rb @@ -3,6 +3,7 @@ require "active_support/core_ext/module/delegation" module AbstractController autoload :Base, "action_controller/abstract/base" + autoload :Benchmarker, "action_controller/abstract/benchmarker" autoload :Callbacks, "action_controller/abstract/callbacks" autoload :Helpers, "action_controller/abstract/helpers" autoload :Layouts, "action_controller/abstract/layouts" diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index 1f2f096dae..0e4803388a 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -68,11 +68,11 @@ module AbstractController self.response_obj = {} end - def process(action_name) - @_action_name = action_name = action_name.to_s + def process(action) + @_action_name = action_name = action.to_s unless action_name = method_for_action(action_name) - raise ActionNotFound, "The action '#{action_name}' could not be found" + raise ActionNotFound, "The action '#{action}' could not be found" end process_action(action_name) diff --git a/actionpack/lib/action_controller/abstract/benchmarker.rb b/actionpack/lib/action_controller/abstract/benchmarker.rb new file mode 100644 index 0000000000..9f5889c704 --- /dev/null +++ b/actionpack/lib/action_controller/abstract/benchmarker.rb @@ -0,0 +1,28 @@ +module AbstractController + module Benchmarker + extend ActiveSupport::DependencyModule + + depends_on Logger + + module ClassMethods + 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
\ No newline at end of file diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index 51b968c694..e4f9dd3112 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -36,6 +36,17 @@ module AbstractController process_action_callback(:#{filter}, name, options) end end + + def skip_#{filter}_filter(*names, &blk) + options = names.last.is_a?(Hash) ? names.pop : {} + _normalize_callback_options(options) + names.push(blk) if block_given? + names.each do |name| + skip_process_action_callback(:#{filter}, name, options) + end + end + + alias_method :append_#{filter}_filter, :#{filter}_filter RUBY_EVAL end end diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 968d3080c1..41decfd0c7 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -24,11 +24,30 @@ module AbstractController super end - + + # Makes all the (instance) methods in the helper module available to templates rendered through this controller. + # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules + # available to the templates. def add_template_helper(mod) master_helper_module.module_eval { include mod } end - + + # Declare a controller method as a helper. For example, the following + # makes the +current_user+ controller method available to the view: + # class ApplicationController < ActionController::Base + # helper_method :current_user, :logged_in? + # + # def current_user + # @current_user ||= User.find_by_id(session[:user]) + # end + # + # def logged_in? + # current_user != nil + # end + # end + # + # In a view: + # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> def helper_method(*meths) meths.flatten.each do |meth| master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 @@ -39,14 +58,14 @@ module AbstractController end end - def helper(*args, &blk) + def helper(*args, &block) args.flatten.each do |arg| case arg when Module add_template_helper(arg) end end - master_helper_module.module_eval(&blk) if block_given? + master_helper_module.module_eval(&block) if block_given? end end diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 35d5e85ed9..3d6810bda9 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -4,11 +4,19 @@ module AbstractController depends_on Renderer + included do + extlib_inheritable_accessor :_layout_conditions + self._layout_conditions = {} + end + module ClassMethods - def layout(layout) + def layout(layout, conditions = {}) unless [String, Symbol, FalseClass, NilClass].include?(layout.class) raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" end + + conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} } + self._layout_conditions = conditions @_layout = layout || false # Converts nil to false _write_layout_method @@ -31,15 +39,15 @@ module AbstractController def _write_layout_method case @_layout when String - self.class_eval %{def _layout() #{@_layout.inspect} end} + self.class_eval %{def _layout(details) #{@_layout.inspect} end} when Symbol - self.class_eval %{def _layout() #{@_layout} end} + self.class_eval %{def _layout(details) #{@_layout} end} when false - self.class_eval %{def _layout() end} + self.class_eval %{def _layout(details) end} else self.class_eval %{ - def _layout - if view_paths.find_by_parts?("#{_implied_layout_name}", {:formats => formats}, "layouts") + def _layout(details) + if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts") "#{_implied_layout_name}" else super @@ -49,38 +57,50 @@ module AbstractController end end end - - def _render_template(template, options) - _action_view._render_template_from_controller(template, options[:_layout], options, options[:_partial]) - end private - def _layout() end # This will be overwritten + def _layout(details) end # This will be overwritten # :api: plugin # ==== # Override this to mutate the inbound layout name - def _layout_for_name(name) + def _layout_for_name(name, details = {:formats => formats}) unless [String, FalseClass, NilClass].include?(name.class) raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}" end - name && view_paths.find_by_parts(name, {:formats => formats}, "layouts") + name && view_paths.find_by_parts(name, details, _layout_prefix(name)) + end + + # TODO: Decide if this is the best hook point for the feature + def _layout_prefix(name) + "layouts" end - def _default_layout(require_layout = false) - if require_layout && !_layout + def _default_layout(require_layout = false, details = {:formats => formats}) + if require_layout && _action_has_layout? && !_layout(details) raise ArgumentError, "There was no default layout for #{self.class} in #{view_paths.inspect}" end begin - layout = _layout_for_name(_layout) + _layout_for_name(_layout(details), details) if _action_has_layout? rescue NameError => e raise NoMethodError, "You specified #{@_layout.inspect} as the layout, but no such method was found" end end + + def _action_has_layout? + conditions = _layout_conditions + if only = conditions[:only] + only.include?(action_name) + elsif except = conditions[:except] + !except.include?(action_name) + else + true + end + end end end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb index 750a5c9fb6..980ede0bed 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/action_controller/abstract/logger.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/logger' module AbstractController module Logger diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index f2044a35ec..d7c68549e1 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -33,16 +33,12 @@ module AbstractController # # :api: plugin def render_to_body(options = {}) - name = options[:_template_name] || action_name - # TODO: Refactor so we can just use the normal template logic for this if options[:_partial_object] _action_view._render_partial_from_controller(options) - else - options[:_template] ||= view_paths.find_by_parts(name.to_s, {:formats => formats}, - options[:_prefix], options[:_partial]) - - _render_template(options[:_template], options) + else + _determine_template(options) + _render_template(options) end end @@ -56,8 +52,8 @@ module AbstractController AbstractController::Renderer.body_to_s(render_to_body(options)) end - def _render_template(template, options) - _action_view._render_template_from_controller(template, nil, options, options[:_partial]) + def _render_template(options) + _action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial]) end def view_paths() _view_paths end @@ -74,6 +70,16 @@ module AbstractController end end + private + + def _determine_template(options) + name = (options[:_template_name] || action_name).to_s + + options[:_template] ||= view_paths.find_by_parts( + name, { :formats => formats }, options[:_prefix], options[:_partial] + ) + end + module ClassMethods def append_view_path(path) diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb index 0993b311cd..1b400d8ed3 100644 --- a/actionpack/lib/action_controller/base/base.rb +++ b/actionpack/lib/action_controller/base/base.rb @@ -242,7 +242,6 @@ module ActionController #:nodoc: # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, # and images to a dedicated asset server away from the main web server. Example: # ActionController::Base.asset_host = "http://assets.example.com" - @@asset_host = "" cattr_accessor :asset_host # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors. @@ -368,9 +367,8 @@ module ActionController #:nodoc: attr_reader :template def action(name, env) - # HACK: For global rescue to have access to the original request and response - request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env) - response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new + request = ActionDispatch::Request.new(env) + response = ActionDispatch::Response.new self.action_name = name && name.to_s process(request, response).to_a end @@ -449,55 +447,6 @@ module ActionController #:nodoc: @view_paths = superclass.view_paths.dup if @view_paths.nil? @view_paths.push(*path) end - - # Replace sensitive parameter data from the request log. - # Filters parameters that have any of the arguments as a substring. - # Looks in all subhashes of the param hash for keys to filter. - # If a block is given, each key and value of the parameter hash and all - # subhashes is passed to it, the value or key - # can be replaced using String#replace or similar method. - # - # Examples: - # filter_parameter_logging - # => Does nothing, just slows the logging process down - # - # filter_parameter_logging :password - # => replaces the value to all keys matching /password/i with "[FILTERED]" - # - # filter_parameter_logging :foo, "bar" - # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - # - # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i - # - # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } - # => reverses the value to all keys matching /secret/i, and - # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - def filter_parameter_logging(*filter_words, &block) - parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 - - define_method(:filter_parameters) do |unfiltered_parameters| - filtered_parameters = {} - - unfiltered_parameters.each do |key, value| - if key =~ parameter_filter - filtered_parameters[key] = '[FILTERED]' - elsif value.is_a?(Hash) - filtered_parameters[key] = filter_parameters(value) - elsif block_given? - key = key.dup - value = value.dup if value - yield key, value - filtered_parameters[key] = value - else - filtered_parameters[key] = value - end - end - - filtered_parameters - end - protected :filter_parameters - end @@exempt_from_layout = [ActionView::TemplateHandlers::RJS] @@ -854,13 +803,6 @@ module ActionController #:nodoc: logger.info(request_id) end - def log_processing_for_parameters - parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup - parameters = parameters.except!(:controller, :action, :format, :_method) - - logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? - end - def default_render #:nodoc: render end @@ -934,7 +876,7 @@ module ActionController #:nodoc: [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers, Cookies, Caching, Verification, Streaming, SessionManagement, HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier, - RequestForgeryProtection, Translation + RequestForgeryProtection, Translation, FilterParameterLogging ].each do |mod| include mod end diff --git a/actionpack/lib/action_controller/base/chained/benchmarking.rb b/actionpack/lib/action_controller/base/chained/benchmarking.rb index 66e9e9c31d..57a1ac8314 100644 --- a/actionpack/lib/action_controller/base/chained/benchmarking.rb +++ b/actionpack/lib/action_controller/base/chained/benchmarking.rb @@ -21,7 +21,7 @@ module ActionController #:nodoc: # 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 + 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)") diff --git a/actionpack/lib/action_controller/base/chained/filters.rb b/actionpack/lib/action_controller/base/chained/filters.rb index 98fe306fd5..e121c0129d 100644 --- a/actionpack/lib/action_controller/base/chained/filters.rb +++ b/actionpack/lib/action_controller/base/chained/filters.rb @@ -571,12 +571,7 @@ module ActionController #:nodoc: # Returns an array of Filter objects for this controller. def filter_chain - if chain = read_inheritable_attribute('filter_chain') - return chain - else - write_inheritable_attribute('filter_chain', FilterChain.new) - return filter_chain - end + read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new) end # Returns all the before filters for this class and all its ancestors. diff --git a/actionpack/lib/action_controller/base/chained/flash.rb b/actionpack/lib/action_controller/base/chained/flash.rb index 56ee9c67e2..6bd482d85a 100644 --- a/actionpack/lib/action_controller/base/chained/flash.rb +++ b/actionpack/lib/action_controller/base/chained/flash.rb @@ -26,9 +26,18 @@ module ActionController #:nodoc: # # See docs on the FlashHash class for more details about the flash. module Flash - def self.included(base) - base.class_eval do - include InstanceMethods + extend ActiveSupport::DependencyModule + + # TODO : Remove the defined? check when new base is the main base + depends_on Session if defined?(ActionController::Http) + + included do + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + include InstanceMethodsForNewBase + else + include InstanceMethodsForBase + alias_method_chain :perform_action, :flash alias_method_chain :reset_session, :flash end @@ -135,29 +144,50 @@ module ActionController #:nodoc: end end - module InstanceMethods #:nodoc: + module InstanceMethodsForBase #:nodoc: protected - def perform_action_with_flash - perform_action_without_flash - remove_instance_variable(:@_flash) if defined? @_flash - end - def reset_session_with_flash - reset_session_without_flash - remove_instance_variable(:@_flash) if defined? @_flash - end + def perform_action_with_flash + perform_action_without_flash + remove_instance_variable(:@_flash) if defined?(@_flash) + end - # Access the contents of the flash. Use <tt>flash["notice"]</tt> to - # read a notice you put there or <tt>flash["notice"] = "hello"</tt> - # to put a new one. - def flash #:doc: - unless defined? @_flash - @_flash = session["flash"] ||= FlashHash.new - @_flash.sweep - end + def reset_session_with_flash + reset_session_without_flash + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end - @_flash - end + module InstanceMethodsForNewBase #:nodoc: + protected + + def reset_session + super + remove_flash_instance_variable + end + + def process_action(method_name) + super + remove_flash_instance_variable + end + + def remove_flash_instance_variable + remove_instance_variable(:@_flash) if defined?(@_flash) + end + end + + protected + + # Access the contents of the flash. Use <tt>flash["notice"]</tt> to + # read a notice you put there or <tt>flash["notice"] = "hello"</tt> + # to put a new one. + def flash #:doc: + unless defined?(@_flash) + @_flash = session["flash"] ||= FlashHash.new + @_flash.sweep + end + + @_flash end end end diff --git a/actionpack/lib/action_controller/base/filter_parameter_logging.rb b/actionpack/lib/action_controller/base/filter_parameter_logging.rb new file mode 100644 index 0000000000..f5a678ca03 --- /dev/null +++ b/actionpack/lib/action_controller/base/filter_parameter_logging.rb @@ -0,0 +1,97 @@ +module ActionController + module FilterParameterLogging + extend ActiveSupport::DependencyModule + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + depends_on AbstractController::Logger + end + + included do + if defined?(ActionController::Http) + include InstanceMethodsForNewBase + end + end + + module ClassMethods + # Replace sensitive parameter data from the request log. + # Filters parameters that have any of the arguments as a substring. + # Looks in all subhashes of the param hash for keys to filter. + # If a block is given, each key and value of the parameter hash and all + # subhashes is passed to it, the value or key + # can be replaced using String#replace or similar method. + # + # Examples: + # filter_parameter_logging + # => Does nothing, just slows the logging process down + # + # filter_parameter_logging :password + # => replaces the value to all keys matching /password/i with "[FILTERED]" + # + # filter_parameter_logging :foo, "bar" + # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + # + # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i + # + # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i } + # => reverses the value to all keys matching /secret/i, and + # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + def filter_parameter_logging(*filter_words, &block) + parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0 + + define_method(:filter_parameters) do |unfiltered_parameters| + filtered_parameters = {} + + unfiltered_parameters.each do |key, value| + if key =~ parameter_filter + filtered_parameters[key] = '[FILTERED]' + elsif value.is_a?(Hash) + filtered_parameters[key] = filter_parameters(value) + elsif block_given? + key = key.dup + value = value.dup if value + yield key, value + filtered_parameters[key] = value + else + filtered_parameters[key] = value + end + end + + filtered_parameters + end + protected :filter_parameters + end + end + + module InstanceMethodsForNewBase + # TODO : Fix the order of information inside such that it's exactly same as the old base + def process(*) + ret = super + + if logger + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method, :only_path) + + unless parameters.empty? + # TODO : Move DelayedLog to AS + log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" } + logger.info(log) + end + end + + ret + end + end + + private + + # TODO : This method is not needed for the new base + def log_processing_for_parameters + parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup + parameters = parameters.except!(:controller, :action, :format, :_method) + + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? + end + end +end diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index ba65032f6a..96fa7896a9 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb @@ -3,23 +3,19 @@ require 'active_support/dependencies' # FIXME: helper { ... } is broken on Ruby 1.9 module ActionController #:nodoc: module Helpers #:nodoc: - def self.included(base) + extend ActiveSupport::DependencyModule + + included do # Initialize the base module to aggregate its helpers. - base.class_inheritable_accessor :master_helper_module - base.master_helper_module = Module.new + class_inheritable_accessor :master_helper_module + self.master_helper_module = Module.new # Set the default directory for helpers - base.class_inheritable_accessor :helpers_dir - base.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - - # Extend base with class methods to declare helpers. - base.extend(ClassMethods) + class_inheritable_accessor :helpers_dir + self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - base.class_eval do - # Wrap inherited to create a new master helper module for subclasses. - class << self - alias_method_chain :inherited, :helper - end + class << self + alias_method_chain :inherited, :helper end end diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb index 0be53cb02d..680900446c 100644 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ b/actionpack/lib/action_controller/base/http_authentication.rb @@ -194,9 +194,10 @@ module ActionController if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] password = password_procedure.call(credentials[:username]) + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] [true, false].any? do |password_is_ha1| - expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) expected == credentials[:response] end end diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb index 9ec8883f8e..3c17dda1a1 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -1,123 +1,103 @@ module ActionController #:nodoc: module MimeResponds #:nodoc: - def self.included(base) - base.module_eval do - include ActionController::MimeResponds::InstanceMethods - end - end - - module InstanceMethods - # Without web-service support, an action which collects the data for displaying a list of people - # might look something like this: - # - # def index - # @people = Person.find(:all) - # end - # - # Here's the same action, with web-service support baked in: - # - # def index - # @people = Person.find(:all) - # - # respond_to do |format| - # format.html - # format.xml { render :xml => @people.to_xml } - # end - # end - # - # What that says is, "if the client wants HTML in response to this action, just respond as we - # would have before, but if the client wants XML, return them the list of people in XML format." - # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) - # - # Supposing you have an action that adds a new person, optionally creating their company - # (by name) if it does not already exist, without web-services, it might look like this: - # - # def create - # @company = Company.find_or_create_by_name(params[:company][:name]) - # @person = @company.people.create(params[:person]) - # - # redirect_to(person_list_url) - # end - # - # Here's the same action, with web-service support baked in: - # - # def create - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # @person = @company.people.create(params[:person]) - # - # respond_to do |format| - # format.html { redirect_to(person_list_url) } - # format.js - # format.xml { render :xml => @person.to_xml(:include => @company) } - # end - # end - # - # If the client wants HTML, we just redirect them back to the person list. If they want Javascript - # (format.js), then it is an RJS request and we render the RJS template associated with this action. - # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also - # include the person's company in the rendered XML, so you get something like this: - # - # <person> - # <id>...</id> - # ... - # <company> - # <id>...</id> - # <name>...</name> - # ... - # </company> - # </person> - # - # Note, however, the extra bit at the top of that action: - # - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # - # This is because the incoming XML document (if a web-service request is in process) can only contain a - # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): - # - # person[name]=...&person[company][name]=...&... - # - # And, like this (xml-encoded): - # - # <person> - # <name>...</name> - # <company> - # <name>...</name> - # </company> - # </person> - # - # In other words, we make the request so that it operates on a single entity's person. Then, in the action, - # we extract the company data from the request, find or create the company, and then create the new person - # with the remaining data. - # - # Note that you can define your own XML parameter parser which would allow you to describe multiple entities - # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow - # and accept Rails' defaults, life will be much easier. - # - # Further more, you may call the #any method on the block's object in order to run the same code for different responses. - # def index - # - # respond_to do |format| - # format.html { @people = People.all(:limit => 10) } - # format.any(:xml, :atom) { @people = People.all } - # end - # end - # - # This will limit the @people variable to 10 people records if we're requesting HTML, but will list all the - # people for any xml or atom request. - # - # If you need to use a MIME type which isn't supported by default, you can register your own handlers in - # environment.rb as follows. - # - # Mime::Type.register "image/jpg", :jpg - def respond_to(*types, &block) - raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block - block ||= lambda { |responder| types.each { |type| responder.send(type) } } - responder = Responder.new(self) - block.call(responder) - responder.respond - end + # Without web-service support, an action which collects the data for displaying a list of people + # might look something like this: + # + # def index + # @people = Person.find(:all) + # end + # + # Here's the same action, with web-service support baked in: + # + # def index + # @people = Person.find(:all) + # + # respond_to do |format| + # format.html + # format.xml { render :xml => @people.to_xml } + # end + # end + # + # What that says is, "if the client wants HTML in response to this action, just respond as we + # would have before, but if the client wants XML, return them the list of people in XML format." + # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) + # + # Supposing you have an action that adds a new person, optionally creating their company + # (by name) if it does not already exist, without web-services, it might look like this: + # + # def create + # @company = Company.find_or_create_by_name(params[:company][:name]) + # @person = @company.people.create(params[:person]) + # + # redirect_to(person_list_url) + # end + # + # Here's the same action, with web-service support baked in: + # + # def create + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # @person = @company.people.create(params[:person]) + # + # respond_to do |format| + # format.html { redirect_to(person_list_url) } + # format.js + # format.xml { render :xml => @person.to_xml(:include => @company) } + # end + # end + # + # If the client wants HTML, we just redirect them back to the person list. If they want Javascript + # (format.js), then it is an RJS request and we render the RJS template associated with this action. + # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also + # include the person's company in the rendered XML, so you get something like this: + # + # <person> + # <id>...</id> + # ... + # <company> + # <id>...</id> + # <name>...</name> + # ... + # </company> + # </person> + # + # Note, however, the extra bit at the top of that action: + # + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # + # This is because the incoming XML document (if a web-service request is in process) can only contain a + # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): + # + # person[name]=...&person[company][name]=...&... + # + # And, like this (xml-encoded): + # + # <person> + # <name>...</name> + # <company> + # <name>...</name> + # </company> + # </person> + # + # In other words, we make the request so that it operates on a single entity's person. Then, in the action, + # we extract the company data from the request, find or create the company, and then create the new person + # with the remaining data. + # + # Note that you can define your own XML parameter parser which would allow you to describe multiple entities + # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow + # and accept Rails' defaults, life will be much easier. + # + # If you need to use a MIME type which isn't supported by default, you can register your own handlers in + # environment.rb as follows. + # + # Mime::Type.register "image/jpg", :jpg + def respond_to(*types, &block) + raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block + block ||= lambda { |responder| types.each { |type| responder.send(type) } } + responder = Responder.new(self) + block.call(responder) + responder.respond end class Responder #:nodoc: @@ -139,8 +119,14 @@ module ActionController #:nodoc: @order << mime_type @responses[mime_type] ||= Proc.new do + # TODO: Remove this when new base is merged in + if defined?(Http) + @controller.formats = [mime_type.to_sym] + end + @controller.template.formats = [mime_type.to_sym] @response.content_type = mime_type.to_s + block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) end end diff --git a/actionpack/lib/action_controller/base/request_forgery_protection.rb b/actionpack/lib/action_controller/base/request_forgery_protection.rb index df91dc1006..f30a4d41a3 100644 --- a/actionpack/lib/action_controller/base/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/base/request_forgery_protection.rb @@ -3,12 +3,26 @@ module ActionController #:nodoc: end module RequestForgeryProtection - def self.included(base) - base.class_eval do - helper_method :form_authenticity_token - helper_method :protect_against_forgery? + extend ActiveSupport::DependencyModule + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + depends_on AbstractController::Helpers, Session + end + + included do + if defined?(ActionController::Http) + # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ + # sets it to <tt>:authenticity_token</tt> by default. + cattr_accessor :request_forgery_protection_token + + # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + class_inheritable_accessor :allow_forgery_protection + self.allow_forgery_protection = true end - base.extend(ClassMethods) + + helper_method :form_authenticity_token + helper_method :protect_against_forgery? end # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a diff --git a/actionpack/lib/action_controller/base/streaming.rb b/actionpack/lib/action_controller/base/streaming.rb index 9f80f48c3d..5872ba99a2 100644 --- a/actionpack/lib/action_controller/base/streaming.rb +++ b/actionpack/lib/action_controller/base/streaming.rb @@ -2,6 +2,13 @@ module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, # instead of rendering. module Streaming + extend ActiveSupport::DependencyModule + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + depends_on ActionController::Renderer + end + DEFAULT_SEND_FILE_OPTIONS = { :type => 'application/octet-stream'.freeze, :disposition => 'attachment'.freeze, @@ -88,6 +95,7 @@ module ActionController #:nodoc: head options[:status], X_SENDFILE_HEADER => path else if options[:stream] + # TODO : Make render :text => proc {} work with the new base render :status => options[:status], :text => Proc.new { |response, output| logger.info "Streaming file #{path}" unless logger.nil? len = options[:buffer_size] || 4096 diff --git a/actionpack/lib/action_controller/base/verification.rb b/actionpack/lib/action_controller/base/verification.rb index c62b81b666..3fa5a105b1 100644 --- a/actionpack/lib/action_controller/base/verification.rb +++ b/actionpack/lib/action_controller/base/verification.rb @@ -1,7 +1,10 @@ module ActionController #:nodoc: module Verification #:nodoc: - def self.included(base) #:nodoc: - base.extend(ClassMethods) + extend ActiveSupport::DependencyModule + + # TODO : Remove the defined? check when new base is the main base + if defined?(ActionController::Http) + depends_on AbstractController::Callbacks, Session, Flash, Renderer end # This module provides a class-level method for specifying that certain @@ -102,7 +105,7 @@ module ActionController #:nodoc: end def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: - [*options[:params] ].find { |v| params[v].nil? } || + [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || [*options[:session]].find { |v| session[v].nil? } || [*options[:flash] ].find { |v| flash[v].nil? } end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ffd8081edc..2f2eec10f6 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -24,31 +24,31 @@ module ActionController #:nodoc: # ActionController::Base.cache_store = :mem_cache_store, "localhost" # ActionController::Base.cache_store = MyOwnStore.new("parameter") module Caching + extend ActiveSupport::DependencyModule + autoload :Actions, 'action_controller/caching/actions' autoload :Fragments, 'action_controller/caching/fragments' autoload :Pages, 'action_controller/caching/pages' autoload :Sweeper, 'action_controller/caching/sweeping' autoload :Sweeping, 'action_controller/caching/sweeping' - def self.included(base) #:nodoc: - base.class_eval do - @@cache_store = nil - cattr_reader :cache_store + included do + @@cache_store = nil + cattr_reader :cache_store - # Defines the storage option for cached fragments - def self.cache_store=(store_option) - @@cache_store = ActiveSupport::Cache.lookup_store(store_option) - end + # Defines the storage option for cached fragments + def self.cache_store=(store_option) + @@cache_store = ActiveSupport::Cache.lookup_store(store_option) + end - include Pages, Actions, Fragments - include Sweeping if defined?(ActiveRecord) + include Pages, Actions, Fragments + include Sweeping if defined?(ActiveRecord) - @@perform_caching = true - cattr_accessor :perform_caching + @@perform_caching = true + cattr_accessor :perform_caching - def self.cache_configured? - perform_caching && cache_store - end + def self.cache_configured? + perform_caching && cache_store end end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 2b822b52c8..430db3932f 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -61,8 +61,14 @@ module ActionController #:nodoc: filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) } cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options) - around_filter(filter_options) do |controller, action| - cache_filter.filter(controller, action) + + # TODO: Remove this once new base is swapped in. + if defined?(Http) + around_filter cache_filter, filter_options + else + around_filter(filter_options) do |controller, action| + cache_filter.filter(controller, action) + end end end end @@ -85,14 +91,22 @@ module ActionController #:nodoc: @options = options end + # TODO: Remove once New Base is merged def filter(controller, action) should_continue = before(controller) action.call if should_continue after(controller) end + def around_process_action(controller) + should_continue = before(controller) + yield if should_continue + after(controller) + end + def before(controller) cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path))) + if cache = controller.read_fragment(cache_path.path, @options[:store_options]) controller.rendered_action_cache = true set_content_type!(controller, cache_path.extension) @@ -129,7 +143,9 @@ module ActionController #:nodoc: end def content_for_layout(controller) - controller.template.layout && controller.template.instance_variable_get('@cached_content_for_layout') + # TODO: Remove this when new base is merged in + template = controller.respond_to?(:template) ? controller.template : controller._action_view + template.layout && template.instance_variable_get('@cached_content_for_layout') end end diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb index 63866caed9..9ad1cadfd3 100644 --- a/actionpack/lib/action_controller/dispatch/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb @@ -1,87 +1,65 @@ +require 'active_support/core_ext/module/delegation' + module ActionController # Dispatches requests to the appropriate controller and takes care of # reloading the app after each request when Dependencies.load? is true. class Dispatcher + 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 + # Run prepare callbacks before every request in development mode + self.prepare_each_request = true + # Development mode callbacks - before_dispatch :reload_application - after_dispatch :cleanup_application + ActionDispatch::Callbacks.before_dispatch do |app| + ActionController::Dispatcher.router.reload + end + + ActionDispatch::Callbacks.after_dispatch do + # Cleanup the application before processing the current request. + ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) + ActiveSupport::Dependencies.clear + ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) + end ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false end if defined?(ActiveRecord) - to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers } + to_prepare(:activerecord_instantiate_observers) do + ActiveRecord::Base.instantiate_observers + end end - after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush) + if Base.logger && Base.logger.respond_to?(:flush) + after_dispatch do + Base.logger.flush + end + end to_prepare do I18n.reload! end 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 to_prepare(identifier = nil, &block) - @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new - callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier) - @prepare_dispatch_callbacks.replace_or_append!(callback) - end + delegate :to_prepare, :prepare_dispatch, :before_dispatch, :after_dispatch, + :to => ActionDispatch::Callbacks - def run_prepare_callbacks - new.send :run_callbacks, :prepare_dispatch + def new + @@middleware.build(@@router) end end - - 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 - - include ActiveSupport::Callbacks - define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch - - def initialize - @app = @@middleware.build(@@router) - freeze - end - - def call(env) - run_callbacks :before_dispatch - @app.call(env) - ensure - run_callbacks :after_dispatch, :enumerator => :reverse_each - end - - def reload_application - # Run prepare callbacks before every request in development mode - run_callbacks :prepare_dispatch - - @@router.reload - end - - def cleanup_application - # Cleanup the application before processing the current request. - ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) - ActiveSupport::Dependencies.clear - ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) - end - - def flush_logger - Base.logger.flush - end end end diff --git a/actionpack/lib/action_controller/dispatch/middlewares.rb b/actionpack/lib/action_controller/dispatch/middlewares.rb index 0e4ab6fa39..b25ed3fd3f 100644 --- a/actionpack/lib/action_controller/dispatch/middlewares.rb +++ b/actionpack/lib/action_controller/dispatch/middlewares.rb @@ -2,8 +2,8 @@ use "Rack::Lock", :if => lambda { !ActionController::Base.allow_concurrency } -use "ActionDispatch::Failsafe" 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) diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb index bc47713529..276be50614 100644 --- a/actionpack/lib/action_controller/new_base.rb +++ b/actionpack/lib/action_controller/new_base.rb @@ -7,13 +7,19 @@ module ActionController autoload :Rails2Compatibility, "action_controller/new_base/compatibility" autoload :Redirector, "action_controller/new_base/redirector" autoload :Renderer, "action_controller/new_base/renderer" + autoload :RenderOptions, "action_controller/new_base/render_options" + autoload :Renderers, "action_controller/new_base/render_options" autoload :Rescue, "action_controller/new_base/rescuable" autoload :Testing, "action_controller/new_base/testing" autoload :UrlFor, "action_controller/new_base/url_for" - + autoload :Session, "action_controller/new_base/session" + autoload :Helpers, "action_controller/new_base/helpers" + # Ported modules # require 'action_controller/routing' + autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :MimeResponds, 'action_controller/base/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' @@ -21,9 +27,18 @@ module ActionController autoload :TestCase, 'action_controller/testing/test_case' autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter' autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter' - + + autoload :Verification, 'action_controller/base/verification' + autoload :Flash, 'action_controller/base/chained/flash' + autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection' + autoload :Streaming, 'action_controller/base/streaming' + autoload :HttpAuthentication, 'action_controller/base/http_authentication' + autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging' + autoload :Translation, 'action_controller/translation' + autoload :Cookies, 'action_controller/base/cookies' + require 'action_controller/routing' end require 'action_dispatch' -require 'action_view'
\ No newline at end of file +require 'action_view' diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb index c25ffd5c84..ffe608ade4 100644 --- a/actionpack/lib/action_controller/new_base/base.rb +++ b/actionpack/lib/action_controller/new_base/base.rb @@ -2,41 +2,56 @@ module ActionController class Base < Http abstract! + include AbstractController::Benchmarker include AbstractController::Callbacks - include AbstractController::Helpers include AbstractController::Logger - + + include ActionController::Helpers include ActionController::HideActions include ActionController::UrlFor include ActionController::Redirector include ActionController::Renderer + include ActionController::Renderers::All include ActionController::Layouts include ActionController::ConditionalGet # Legacy modules include SessionManagement include ActionDispatch::StatusCodes + include ActionController::Caching + include ActionController::MimeResponds # Rails 2.x compatibility include ActionController::Rails2Compatibility + include ActionController::Cookies + include ActionController::Session + include ActionController::Flash + include ActionController::Verification + include ActionController::RequestForgeryProtection + include ActionController::Streaming + include ActionController::HttpAuthentication::Basic::ControllerMethods + include ActionController::HttpAuthentication::Digest::ControllerMethods + include ActionController::FilterParameterLogging + include ActionController::Translation + # TODO: Extract into its own module # This should be moved together with other normalizing behavior module ImplicitRender def process_action(method_name) ret = super - render if response_body.nil? + default_render if response_body.nil? ret end - def _implicit_render + def default_render render end def method_for_action(action_name) super || begin if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path) - "_implicit_render" + "default_render" end end end @@ -61,7 +76,7 @@ module ActionController end end - def render_to_body(action = nil, options = {}) + def _normalize_options(action = nil, options = {}, &blk) if action.is_a?(Hash) options, action = action, nil elsif action.is_a?(String) || action.is_a?(Symbol) @@ -78,11 +93,25 @@ module ActionController if options.key?(:action) && options[:action].to_s.index("/") options[:template] = options.delete(:action) end - - # options = {:template => options.to_s} if options.is_a?(String) || options.is_a?(Symbol) - super(options) || " " + + if options[:status] + options[:status] = interpret_status(options[:status]).to_i + end + + options[:update] = blk if block_given? + options end - + + def render(action = nil, options = {}, &blk) + options = _normalize_options(action, options, &blk) + super(options) + end + + def render_to_string(action = nil, options = {}, &blk) + options = _normalize_options(action, options, &blk) + super(options) + end + # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: # # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+. @@ -140,4 +169,4 @@ module ActionController super(url, status) end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb index 0a283887b6..b3190486e8 100644 --- a/actionpack/lib/action_controller/new_base/compatibility.rb +++ b/actionpack/lib/action_controller/new_base/compatibility.rb @@ -1,7 +1,10 @@ module ActionController module Rails2Compatibility extend ActiveSupport::DependencyModule - + + class ::ActionController::ActionControllerError < StandardError #:nodoc: + end + # Temporary hax included do ::ActionController::UnknownAction = ::AbstractController::ActionNotFound @@ -53,10 +56,23 @@ module ActionController cattr_accessor :consider_all_requests_local self.consider_all_requests_local = true + + # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, + # and images to a dedicated asset server away from the main web server. Example: + # ActionController::Base.asset_host = "http://assets.example.com" + cattr_accessor :asset_host end + # For old tests + def initialize_template_class(*) end + def assign_shortcuts(*) end + + # TODO: Remove this after we flip + def template + _action_view + end + module ClassMethods - def protect_from_forgery() end def consider_all_requests_local() end def rescue_action(env) raise env["action_dispatch.rescue.exception"] @@ -80,7 +96,9 @@ module ActionController options[:text] = nil if options[:nothing] == true - super + body = super + body = [' '] if body.blank? + body end def _handle_method_missing @@ -91,10 +109,12 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _layout_for_name(name) - name &&= name.sub(%r{^/?layouts/}, '') - super + def _layout_prefix(name) + super unless name =~ /\blayouts/ + end + + def performed? + response_body end - end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/new_base/conditional_get.rb b/actionpack/lib/action_controller/new_base/conditional_get.rb index e1407e671a..116ce34494 100644 --- a/actionpack/lib/action_controller/new_base/conditional_get.rb +++ b/actionpack/lib/action_controller/new_base/conditional_get.rb @@ -57,7 +57,7 @@ module ActionController raise ArgumentError, "too few arguments to head" end options = args.extract_options! - status = interpret_status(args.shift || options.delete(:status) || :ok) + status = args.shift || options.delete(:status) || :ok options.each do |key, value| headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb new file mode 100644 index 0000000000..e00c3c338b --- /dev/null +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -0,0 +1,130 @@ +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/name_error' +require 'active_support/dependencies' + +module ActionController + module Helpers + extend ActiveSupport::DependencyModule + + depends_on AbstractController::Helpers + + included do + # Set the default directory for helpers + class_inheritable_accessor :helpers_dir + self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") + end + + module ClassMethods + def inherited(klass) + klass.__send__ :default_helper_module! + super + end + + # The +helper+ class method can take a series of helper module names, a block, or both. + # + # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>. + # * <tt>&block</tt>: A block defining helper methods. + # + # ==== Examples + # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file + # and include the module in the template class. The second form illustrates how to include custom helpers + # when working with namespaced controllers, or other cases where the file containing the helper definition is not + # in one of Rails' standard load paths: + # helper :foo # => requires 'foo_helper' and includes FooHelper + # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper + # + # When the argument is a module it will be included directly in the template class. + # helper FooHelper # => includes FooHelper + # + # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers beneath + # <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT). + # helper :all + # + # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available + # to the template. + # # One line + # helper { def hello() "Hello, world!" end } + # # Multi-line + # helper do + # def foo(bar) + # "#{bar} is the very best" + # end + # end + # + # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of + # +symbols+, +strings+, +modules+ and blocks. + # helper(:three, BlindHelper) { def mice() 'mice' end } + # + def helper(*args, &block) + args.flatten.each do |arg| + case arg + when :all + helper all_application_helpers + when String, Symbol + file_name = arg.to_s.underscore + '_helper' + class_name = file_name.camelize + + begin + require_dependency(file_name) + rescue LoadError => load_error + requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] + if requiree == file_name + msg = "Missing helper file helpers/#{file_name}.rb" + raise LoadError.new(msg).copy_blame!(load_error) + else + raise + end + end + + super class_name.constantize + else + super args + end + end + + # Evaluate block in template class if given. + master_helper_module.module_eval(&block) if block_given? + end + + # Declares helper accessors for controller attributes. For example, the + # following adds new +name+ and <tt>name=</tt> instance methods to a + # controller and makes them available to the view: + # helper_attr :name + # attr_accessor :name + def helper_attr(*attrs) + attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } + end + + # Provides a proxy to access helpers methods from outside the view. + def helpers + unless @helper_proxy + @helper_proxy = ActionView::Base.new + @helper_proxy.extend master_helper_module + else + @helper_proxy + end + end + + private + + def default_helper_module! + unless name.blank? + module_name = name.sub(/Controller$|$/, 'Helper') + module_path = module_name.split('::').map { |m| m.underscore }.join('/') + require_dependency module_path + helper module_name.constantize + end + rescue MissingSourceFile => e + raise unless e.is_missing? module_path + rescue NameError => e + raise unless e.missing_name? module_name + end + + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end + end # ClassMethods + end +end diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb index 8891a2a8c3..2525e221a6 100644 --- a/actionpack/lib/action_controller/new_base/http.rb +++ b/actionpack/lib/action_controller/new_base/http.rb @@ -37,7 +37,7 @@ module ActionController end delegate :headers, :to => "@_response" - + def params @_params ||= @_request.parameters end @@ -60,7 +60,6 @@ module ActionController # :api: private def to_rack - @_response.body = response_body @_response.prepare! @_response.to_a end diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb index bf5b14c4e1..35068db770 100644 --- a/actionpack/lib/action_controller/new_base/layouts.rb +++ b/actionpack/lib/action_controller/new_base/layouts.rb @@ -11,23 +11,20 @@ module ActionController end end - def render_to_body(options) - # render :text => ..., :layout => ... - # or - # render :anything_else + private + + def _determine_template(options) + super if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout) - options[:_layout] = options.key?(:layout) ? _layout_for_option(options[:layout]) : _default_layout + options[:_layout] = _layout_for_option(options.key?(:layout) ? options[:layout] : :none, options[:_template].details) end - - super end - - private - def _layout_for_option(name) + def _layout_for_option(name, details) case name - when String then _layout_for_name(name) - when true then _default_layout(true) + when String then _layout_for_name(name, details) + when true then _default_layout(true, details) + when :none then _default_layout(false, details) when false, nil then nil else raise ArgumentError, diff --git a/actionpack/lib/action_controller/new_base/render_options.rb b/actionpack/lib/action_controller/new_base/render_options.rb new file mode 100644 index 0000000000..581a92cb7b --- /dev/null +++ b/actionpack/lib/action_controller/new_base/render_options.rb @@ -0,0 +1,107 @@ +module ActionController + module RenderOptions + extend ActiveSupport::DependencyModule + + included do + extlib_inheritable_accessor :_renderers + self._renderers = [] + end + + module ClassMethods + def _write_render_options + renderers = _renderers.map do |r| + <<-RUBY_EVAL + if options.key?(:#{r}) + _process_options(options) + return _render_#{r}(options[:#{r}], options) + end + RUBY_EVAL + end + + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def _handle_render_options(options) + #{renderers.join} + end + RUBY_EVAL + end + + def _add_render_option(name) + _renderers << name + _write_render_options + end + end + + def render_to_body(options) + _handle_render_options(options) || super + end + end + + module RenderOption + extend ActiveSupport::DependencyModule + + included do + extend ActiveSupport::DependencyModule + depends_on ::ActionController::RenderOptions + + def self.register_renderer(name) + included { _add_render_option(name) } + end + end + end + + module Renderers + module Json + include RenderOption + register_renderer :json + + def _render_json(json, options) + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + response.content_type ||= Mime::JSON + self.response_body = json + end + end + + module Js + include RenderOption + register_renderer :js + + def _render_js(js, options) + response.content_type ||= Mime::JS + self.response_body = js + end + end + + module Xml + include RenderOption + register_renderer :xml + + def _render_xml(xml, options) + response.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end + end + + module Rjs + include RenderOption + register_renderer :update + + def _render_update(proc, options) + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc) + response.content_type = Mime::JS + self.response_body = generator.to_s + end + end + + module All + extend ActiveSupport::DependencyModule + + included do + include ::ActionController::Renderers::Json + include ::ActionController::Renderers::Js + include ::ActionController::Renderers::Xml + include ::ActionController::Renderers::Rjs + end + end + end +end diff --git a/actionpack/lib/action_controller/new_base/renderer.rb b/actionpack/lib/action_controller/new_base/renderer.rb index 8a9f230603..987751a601 100644 --- a/actionpack/lib/action_controller/new_base/renderer.rb +++ b/actionpack/lib/action_controller/new_base/renderer.rb @@ -4,17 +4,45 @@ module ActionController depends_on AbstractController::Renderer - def initialize(*) - self.formats = [:html] + def process_action(*) + self.formats = request.formats.map {|x| x.to_sym} super end + def response_body=(body) + response.body = body if response + super + end + + def render(options) + super + options[:_template] ||= _action_view._partial + response.content_type ||= begin + mime = options[:_template].mime_type + formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first) + end + response_body + end + def render_to_body(options) _process_options(options) + if options.key?(:partial) + _render_partial(options[:partial], options) + end + + super + end + + private + + def _prefix + controller_path + end + + def _determine_template(options) if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(_text(options)) - template = nil + options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first) elsif options.key?(:inline) handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) @@ -23,35 +51,14 @@ module ActionController options[:_template_name] = options[:template] elsif options.key?(:file) options[:_template_name] = options[:file] - elsif options.key?(:partial) - _render_partial(options[:partial], options) - else + elsif !options.key?(:partial) options[:_template_name] = (options[:action] || action_name).to_s options[:_prefix] = _prefix end - ret = super(options) - - options[:_template] ||= _action_view._partial - response.content_type ||= options[:_template].mime_type - ret + super end - - private - - def _prefix - controller_path - end - - def _text(options) - text = options[:text] - case text - when nil then " " - else text.to_s - end - end - def _render_partial(partial, options) case partial when true @@ -68,9 +75,10 @@ module ActionController end def _process_options(options) - status, content_type = options.values_at(:status, :content_type) - response.status = status.to_i if status + status, content_type, location = options.values_at(:status, :content_type, :location) + response.status = status if status response.content_type = content_type if content_type + response.headers["Location"] = url_for(location) if location end end end diff --git a/actionpack/lib/action_controller/new_base/session.rb b/actionpack/lib/action_controller/new_base/session.rb new file mode 100644 index 0000000000..a8715555fb --- /dev/null +++ b/actionpack/lib/action_controller/new_base/session.rb @@ -0,0 +1,11 @@ +module ActionController + module Session + def session + @_request.session + end + + def reset_session + @_request.reset_session + end + end +end diff --git a/actionpack/lib/action_controller/new_base/testing.rb b/actionpack/lib/action_controller/new_base/testing.rb index b39d8d539d..d8c3421587 100644 --- a/actionpack/lib/action_controller/new_base/testing.rb +++ b/actionpack/lib/action_controller/new_base/testing.rb @@ -1,13 +1,13 @@ module ActionController module Testing - + # OMG MEGA HAX - def process_with_test(request, response) + def process_with_new_base_test(request, response) @_request = request @_response = response @_response.request = request ret = process(request.parameters[:action]) - @_response.body = self.response_body || " " + @_response.body ||= self.response_body @_response.prepare! set_test_assigns ret @@ -20,6 +20,11 @@ module ActionController @assigns[name] = value end end - + + # TODO : Rewrite tests using controller.headers= to use Rack env + def headers=(new_headers) + @_response ||= ActionDispatch::Response.new + @_response.headers.replace(new_headers) + end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/new_base/url_for.rb b/actionpack/lib/action_controller/new_base/url_for.rb index af5b21012b..94de9fab50 100644 --- a/actionpack/lib/action_controller/new_base/url_for.rb +++ b/actionpack/lib/action_controller/new_base/url_for.rb @@ -1,5 +1,10 @@ module ActionController module UrlFor + def process_action(*) + initialize_current_url + super + end + def initialize_current_url @url = UrlRewriter.new(request, params.clone) end diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb index d6991ab4f5..cc157816e2 100644 --- a/actionpack/lib/action_controller/testing/integration.rb +++ b/actionpack/lib/action_controller/testing/integration.rb @@ -2,6 +2,9 @@ require 'stringio' require 'uri' require 'active_support/test_case' +require 'rack/mock_session' +require 'rack/test/cookie_jar' + module ActionController module Integration #:nodoc: module RequestHelpers @@ -62,8 +65,8 @@ module ActionController # with 'HTTP_' if not already. def xml_http_request(request_method, path, parameters = nil, headers = nil) headers ||= {} - headers['X-Requested-With'] = 'XMLHttpRequest' - headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') process(request_method, path, parameters, headers) end alias xhr :xml_http_request @@ -121,6 +124,8 @@ module ActionController # IntegrationTest#open_session, rather than instantiating # Integration::Session directly. class Session + DEFAULT_HOST = "www.example.com" + include Test::Unit::Assertions include ActionDispatch::Assertions include ActionController::TestProcess @@ -145,7 +150,9 @@ module ActionController # A map of the cookies returned by the last response, and which will be # sent with the next request. - attr_reader :cookies + def cookies + @mock_session.cookie_jar + end # A reference to the controller instance used by the last request. attr_reader :controller @@ -172,11 +179,11 @@ module ActionController # session.reset! def reset! @https = false - @cookies = {} + @mock_session = Rack::MockSession.new(@app, DEFAULT_HOST) @controller = @request = @response = nil @request_count = 0 - self.host = "www.example.com" + self.host = DEFAULT_HOST self.remote_addr = "127.0.0.1" self.accept = "text/xml,application/xml,application/xhtml+xml," + "text/html;q=0.9,text/plain;q=0.8,image/png," + @@ -227,8 +234,9 @@ module ActionController end private + # Performs the actual request. - def process(method, path, parameters = nil, headers = nil) + def process(method, path, parameters = nil, rack_environment = nil) if path =~ %r{://} location = URI.parse(path) https! URI::HTTPS === location if location.scheme @@ -242,8 +250,6 @@ module ActionController end end - ActionController::Base.clear_last_instantiation! - opts = { :method => method, :params => parameters, @@ -258,34 +264,23 @@ module ActionController "HTTP_HOST" => host, "REMOTE_ADDR" => remote_addr, "CONTENT_TYPE" => "application/x-www-form-urlencoded", - "HTTP_ACCEPT" => accept, - "HTTP_COOKIE" => cookies.inject("") { |string, (name, value)| - string << "#{name}=#{value}; " - } + "HTTP_ACCEPT" => accept } env = Rack::MockRequest.env_for(path, opts) - (headers || {}).each do |key, value| - key = key.to_s.upcase.gsub(/-/, "_") - key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/ + (rack_environment || {}).each do |key, value| env[key] = value end - app = Rack::Lint.new(@app) - status, headers, body = app.call(env) - mock_response = ::Rack::MockResponse.new(status, headers, body) + @controller = ActionController::Base.capture_instantiation do + @mock_session.request(URI.parse(path), env) + end @request_count += 1 @request = ActionDispatch::Request.new(env) - @response = ActionDispatch::TestResponse.from_response(mock_response) - - @cookies.merge!(@response.cookies) + @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response) @html_document = nil - if @controller = ActionController::Base.last_instantiation - @controller.send(:set_test_assigns) - end - return response.status end @@ -306,11 +301,10 @@ module ActionController # A module used to extend ActionController::Base, so that integration tests # can capture the controller used to satisfy a request. module ControllerCapture #:nodoc: - def self.included(base) - base.extend(ClassMethods) - base.class_eval do - alias_method_chain :initialize, :capture - end + extend ActiveSupport::DependencyModule + + included do + alias_method_chain :initialize, :capture end def initialize_with_capture(*args) @@ -321,8 +315,10 @@ module ActionController module ClassMethods #:nodoc: mattr_accessor :last_instantiation - def clear_last_instantiation! + def capture_instantiation self.last_instantiation = nil + yield + return last_instantiation end end end diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 8f4358c33e..9647f8ce45 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -41,6 +41,7 @@ module ActionController #:nodoc: 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'] = {} @@ -132,9 +133,6 @@ module ActionController #:nodoc: @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash build_request_uri(action, parameters) - @request.env["action_controller.rescue.request"] = @request - @request.env["action_controller.rescue.response"] = @response - Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest env = @request.env diff --git a/actionpack/lib/action_controller/testing/process2.rb b/actionpack/lib/action_controller/testing/process2.rb index e9a79369b9..677dd41781 100644 --- a/actionpack/lib/action_controller/testing/process2.rb +++ b/actionpack/lib/action_controller/testing/process2.rb @@ -40,6 +40,8 @@ module ActionController @request.recycle! @response.recycle! @controller.response_body = nil + @controller.formats = nil + @controller.params = nil @html_document = nil @request.env['REQUEST_METHOD'] = http_method @@ -53,7 +55,8 @@ module ActionController @controller.request = @request @controller.params.merge!(parameters) # Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest - @controller.process_with_test(@request, @response) + @controller.process_with_new_base_test(@request, @response) + @response end def build_request_uri(action, parameters) diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index ae20f9947c..a992f7d912 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -73,7 +73,7 @@ module HTML # Specifies the default Set of tags that the #sanitize helper will allow unscathed. self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub - sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr + sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr acronym a img blockquote del ins)) # Specifies the default Set of html attributes that the #sanitize helper will leave diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 27d229835a..884828a01a 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -27,23 +27,26 @@ require 'active_support' begin gem 'rack', '~> 1.1.pre' -rescue Gem::LoadError +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 ActionDispatch autoload :Request, 'action_dispatch/http/request' autoload :Response, 'action_dispatch/http/response' autoload :StatusCodes, 'action_dispatch/http/status_codes' - autoload :Failsafe, 'action_dispatch/middleware/failsafe' + autoload :Callbacks, 'action_dispatch/middleware/callbacks' 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 :HTML, 'action_controller/vendor/html-scanner' autoload :Assertions, 'action_dispatch/testing/assertions' autoload :TestRequest, 'action_dispatch/testing/test_request' autoload :TestResponse, 'action_dispatch/testing/test_response' diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index dfcf3a558f..25156a4c75 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/class/attribute_accessors' module Mime SET = [] - EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? } + EXTENSION_LOOKUP = {} LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? } def self.[](type) diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 2d7fba1173..7c28cac419 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -1,9 +1,9 @@ # Build list of Mime types for HTTP responses # http://www.iana.org/assignments/media-types/ +Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml ) Mime::Type.register "*/*", :all Mime::Type.register "text/plain", :text, [], %w(txt) -Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml ) Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript ) Mime::Type.register "text/css", :css Mime::Type.register "text/calendar", :ics diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 13ff049a97..140feb9a68 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -3,7 +3,9 @@ require 'stringio' require 'strscan' require 'active_support/memoizable' +require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/object/tap' module ActionDispatch class Request < Rack::Request @@ -173,9 +175,21 @@ module ActionDispatch def formats if ActionController::Base.use_accept_header - Array(Mime[parameters[:format]] || accepts) + if param = parameters[:format] + Array.wrap(Mime[param]) + else + accepts.dup + end.tap do |ret| + if defined?(ActionController::Http) + if ret == ONLY_ALL + ret.replace Mime::SET + elsif all = ret.index(Mime::ALL) + ret.delete_at(all) && ret.insert(all, *Mime::SET) + end + end + end else - [format] + [format] + Mime::SET end end diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb new file mode 100644 index 0000000000..0a2b4cf5f7 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -0,0 +1,40 @@ +module ActionDispatch + class Callbacks + include ActiveSupport::Callbacks + define_callbacks :prepare, :before, :after + + 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 + end + + def call(env) + run_callbacks :before + run_callbacks :prepare if @prepare_each_request + @app.call(env) + ensure + run_callbacks :after, :enumerator => :reverse_each + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/failsafe.rb b/actionpack/lib/action_dispatch/middleware/failsafe.rb deleted file mode 100644 index 836098482c..0000000000 --- a/actionpack/lib/action_dispatch/middleware/failsafe.rb +++ /dev/null @@ -1,52 +0,0 @@ -module ActionDispatch - class Failsafe - cattr_accessor :error_file_path - self.error_file_path = Rails.public_path if defined?(Rails.public_path) - - def initialize(app) - @app = app - end - - def call(env) - @app.call(env) - rescue Exception => exception - # Reraise exception in test environment - if defined?(Rails) && Rails.env.test? - raise exception - else - failsafe_response(exception) - end - end - - private - def failsafe_response(exception) - log_failsafe_exception(exception) - [500, {'Content-Type' => 'text/html'}, failsafe_response_body] - rescue Exception => failsafe_error # Logger or IO errors - $stderr.puts "Error during failsafe response: #{failsafe_error}" - end - - def failsafe_response_body - error_path = "#{self.class.error_file_path}/500.html" - if File.exist?(error_path) - [File.read(error_path)] - else - ["<html><body><h1>500 Internal Server Error</h1></body></html>"] - end - end - - def log_failsafe_exception(exception) - message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n" - message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception - failsafe_logger.fatal(message) - end - - def failsafe_logger - if defined?(Rails) && Rails.logger - Rails.logger - else - Logger.new($stderr) - end - end - end -end diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 58d527a6e7..e83cf9236b 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -32,16 +32,14 @@ module ActionDispatch when Proc strategy.call(request.raw_post) when :xml_simple, :xml_node - body = request.raw_post - body.blank? ? {} : Hash.from_xml(body).with_indifferent_access + request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access when :yaml - YAML.load(request.raw_post) + YAML.load(request.body) when :json - body = request.raw_post - if body.blank? + if request.body.size == 0 {} else - data = ActiveSupport::JSON.decode(body) + data = ActiveSupport::JSON.decode(request.body) data = {:_json => data} unless data.is_a?(Hash) data.with_indifferent_access end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 71c1e1b9a9..4d598669c7 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -4,8 +4,11 @@ module ActionDispatch LOCALHOST = '127.0.0.1'.freeze - DEFAULT_RESCUE_RESPONSE = :internal_server_error - DEFAULT_RESCUE_RESPONSES = { + RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') + + cattr_accessor :rescue_responses + @@rescue_responses = Hash.new(:internal_server_error) + @@rescue_responses.update({ 'ActionController::RoutingError' => :not_found, 'ActionController::UnknownAction' => :not_found, 'ActiveRecord::RecordNotFound' => :not_found, @@ -15,25 +18,19 @@ module ActionDispatch 'ActionController::MethodNotAllowed' => :method_not_allowed, 'ActionController::NotImplemented' => :not_implemented, 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity - } + }) - DEFAULT_RESCUE_TEMPLATE = 'diagnostics' - DEFAULT_RESCUE_TEMPLATES = { + cattr_accessor :rescue_templates + @@rescue_templates = Hash.new('diagnostics') + @@rescue_templates.update({ 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', 'ActionController::UnknownAction' => 'unknown_action', 'ActionView::TemplateError' => 'template_error' - } + }) - RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') - - cattr_accessor :rescue_responses - @@rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE) - @@rescue_responses.update DEFAULT_RESCUE_RESPONSES - - cattr_accessor :rescue_templates - @@rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE) - @@rescue_templates.update DEFAULT_RESCUE_TEMPLATES + FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, + ['<html><body><h1>500 Internal Server Error</h1></body></html>']] def initialize(app, consider_all_requests_local = false) @app = app @@ -43,34 +40,35 @@ module ActionDispatch def call(env) @app.call(env) rescue Exception => exception - raise exception if env['rack.test'] + raise exception if env['action_dispatch.show_exceptions'] == false + render_exception(env, exception) + end - log_error(exception) if logger + private + def render_exception(env, exception) + log_error(exception) - request = Request.new(env) - if @consider_all_requests_local || local_request?(request) - rescue_action_locally(request, exception) - else - rescue_action_in_public(exception) + request = Request.new(env) + if @consider_all_requests_local || local_request?(request) + rescue_action_locally(request, exception) + else + rescue_action_in_public(exception) + end + rescue Exception => failsafe_error + $stderr.puts "Error during failsafe response: #{failsafe_error}" + FAILSAFE_RESPONSE end - end - private # Render detailed diagnostics for unhandled exceptions rescued from # a controller action. def rescue_action_locally(request, exception) template = ActionView::Base.new([RESCUES_TEMPLATE_PATH], - :template => template, :request => request, :exception => exception ) file = "rescues/#{@@rescue_templates[exception.class.name]}.erb" body = template.render(:file => file, :layout => 'rescues/layout.erb') - - headers = {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s} - status = status_code(exception) - - [status, headers, body] + render(status_code(exception), body) end # Attempts to render a static error page based on the @@ -86,11 +84,11 @@ module ActionDispatch path = "#{public_path}/#{status}.html" if locale_path && File.exist?(locale_path) - render_public_file(status, locale_path) + render(status, File.read(locale_path)) elsif File.exist?(path) - render_public_file(status, path) + render(status, File.read(path)) else - [status, {'Content-Type' => 'text/html', 'Content-Length' => '0'}, []] + render(status, '') end end @@ -99,24 +97,21 @@ module ActionDispatch request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST end - def render_public_file(status, path) - body = File.read(path) - [status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, body] - end - def status_code(exception) interpret_status(@@rescue_responses[exception.class.name]).to_i end + def render(status, body) + [status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, [body]] + end + def public_path - if defined?(Rails) - Rails.public_path - else - "public" - end + defined?(Rails.public_path) ? Rails.public_path : 'public_path' end - def log_error(exception) #:doc: + def log_error(exception) + return unless logger + ActiveSupport::Deprecation.silence do if ActionView::TemplateError === exception logger.fatal(exception.to_s) @@ -136,9 +131,7 @@ module ActionDispatch end def logger - if defined?(Rails.logger) - Rails.logger - end + defined?(Rails.logger) ? Rails.logger : Logger.new($stderr) 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 new file mode 100644 index 0000000000..eba6226538 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb @@ -0,0 +1,50 @@ +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 new file mode 100644 index 0000000000..70384b1d76 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb @@ -0,0 +1,239 @@ +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 new file mode 100644 index 0000000000..d58c914c9b --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb @@ -0,0 +1,169 @@ +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 new file mode 100644 index 0000000000..a191fa23d8 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb @@ -0,0 +1,45 @@ +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 new file mode 100644 index 0000000000..81c398ba51 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb @@ -0,0 +1,27 @@ +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 new file mode 100644 index 0000000000..239302fbe4 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000000..d25b849709 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb @@ -0,0 +1,75 @@ +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/base.rb b/actionpack/lib/action_view/base.rb index 56f0b5ef4f..4ab568b44c 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -269,15 +269,16 @@ module ActionView #:nodoc: nil end - private - # Evaluates the local assigns and controller ivars, pushes them to the view. - def _evaluate_assigns_and_ivars #:nodoc: - unless @assigns_added - @assigns.each { |key, value| instance_variable_set("@#{key}", value) } - _copy_ivars_from_controller - @assigns_added = true - end + # Evaluates the local assigns and controller ivars, pushes them to the view. + def _evaluate_assigns_and_ivars #:nodoc: + unless @assigns_added + @assigns.each { |key, value| instance_variable_set("@#{key}", value) } + _copy_ivars_from_controller + @assigns_added = true end + end + + private def _copy_ivars_from_controller #:nodoc: if @controller @@ -288,8 +289,11 @@ module ActionView #:nodoc: end def _set_controller_content_type(content_type) #:nodoc: - if controller.respond_to?(:response) - controller.response.content_type ||= content_type + # TODO: Remove this method when new base is switched + unless defined?(ActionController::Http) + if controller.respond_to?(:response) + controller.response.content_type ||= content_type + end end end end diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 7c0dfdab10..b4b9f6e34b 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -1,5 +1,6 @@ require 'cgi' require 'action_view/helpers/form_helper' +require 'active_support/core_ext/class/attribute_accessors' module ActionView class Base diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index c02692b09a..999d5b34fc 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -248,6 +248,11 @@ module ActionView # number_to_human_size(483989, :precision => 0) # => 473 KB # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB # + # Zeros after the decimal point are always stripped out, regardless of the + # specified precision: + # helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB" + # helper.number_to_human_size(524288000, :precision=>5) # => "500 MB" + # # You can still use <tt>number_to_human_size</tt> with the old API that accepts the # +precision+ as its optional second parameter: # number_to_human_size(1234567, 2) # => 1.18 MB @@ -293,7 +298,7 @@ module ActionView :precision => precision, :separator => separator, :delimiter => delimiter - ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit) rescue number diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 1fbe012a95..c0f5df3468 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1,5 +1,6 @@ require 'set' require 'active_support/json' +require 'active_support/core_ext/object/extending' module ActionView module Helpers @@ -572,6 +573,7 @@ module ActionView # #include_helpers_from_context has nothing to overwrite. class JavaScriptGenerator #:nodoc: def initialize(context, &block) #:nodoc: + context._evaluate_assigns_and_ivars @context, @lines = context, [] include_helpers_from_context @context.with_output_buffer(@lines) do diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index f16e311d6c..c3ce4c671e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -34,12 +34,16 @@ module ActionView # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt> # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "..."). + # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. # # ==== Examples # # truncate("Once upon a time in a world far far away") # # => Once upon a time in a world f... # + # truncate("Once upon a time in a world far far away", :separator => ' ') + # # => Once upon a time in a world... + # # truncate("Once upon a time in a world far far away", :length => 14) # # => Once upon a... # @@ -71,7 +75,8 @@ module ActionView if text l = options[:length] - options[:omission].mb_chars.length chars = text.mb_chars - (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s + stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l + (chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s end end @@ -535,7 +540,7 @@ module ActionView link_attributes = html_options.stringify_keys text.gsub(AUTO_LINK_RE) do href = $& - punctuation = '' + punctuation = [] left, right = $`, $' # detect already linked URLs and URLs in the middle of a tag if left =~ /<[^>]+$/ && right =~ /^[^>]*>/ @@ -543,17 +548,18 @@ module ActionView href else # don't include trailing punctuation character as part of the URL - if href.sub!(/[^\w\/-]$/, '') and punctuation = $& and opening = BRACKETS[punctuation] - if href.scan(opening).size > href.scan(punctuation).size - href << punctuation - punctuation = '' + while href.sub!(/[^\w\/-]$/, '') + punctuation.push $& + if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size + href << punctuation.pop + break end end link_text = block_given?? yield(href) : href href = 'http://' + href unless href.index('http') == 0 - content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation + content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('') end end end diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 672da0ed2b..3071c78174 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -1,3 +1,6 @@ +require "active_support/core_ext/class/inheritable_attributes" +require "action_dispatch/http/mime_type" + # Legacy TemplateHandler stub module ActionView module TemplateHandlers #:nodoc: @@ -19,6 +22,9 @@ module ActionView end class TemplateHandler + extlib_inheritable_accessor :default_format + self.default_format = Mime::HTML + def self.call(template) "#{name}.new(self).render(template, local_assigns)" end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index 0590372d09..faf54b9fe5 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -33,7 +33,7 @@ module ActionView #:nodoc: end def template_handler_extensions - @@template_handlers.keys.map(&:to_s).sort + @@template_handlers.keys.map {|key| key.to_s }.sort end def registered_template_handler(extension) diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 788dc93326..f412228752 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -5,6 +5,8 @@ module ActionView class Builder < TemplateHandler include Compilable + self.default_format = Mime::XML + def compile(template) "_set_controller_content_type(Mime::XML);" + "xml = ::Builder::XmlMarkup.new(:indent => 2);" + diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index fdcb108ffc..95f11d6490 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -13,6 +13,8 @@ module ActionView cattr_accessor :erb_trim_mode self.erb_trim_mode = '-' + self.default_format = Mime::HTML + def compile(template) src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index 802a79b3fc..a36744c2b7 100644 --- a/actionpack/lib/action_view/template/handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -3,11 +3,17 @@ module ActionView class RJS < TemplateHandler include Compilable + self.default_format = Mime::JS + def compile(template) "@formats = [:html];" + "controller.response.content_type ||= Mime::JS;" + "update_page do |page|;#{template.source}\nend" end + + def default_format + Mime::JS + end end end end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index dcc5006103..f61dd591a5 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -7,13 +7,20 @@ require "action_view/template/path" module ActionView class Template extend TemplateHandlers - attr_reader :source, :identifier, :handler + attr_reader :source, :identifier, :handler, :mime_type, :details def initialize(source, identifier, handler, details) @source = source @identifier = identifier @handler = handler @details = details + + format = details.delete(:format) || begin + # TODO: Clean this up + handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" + end + @mime_type = Mime::Type.lookup_by_extension(format.to_s) + @details[:formats] = Array.wrap(format && format.to_sym) end def render(view, locals, &blk) @@ -35,12 +42,7 @@ module ActionView def partial? @details[:partial] end - - # TODO: Move out of Template - def mime_type - Mime::Type.lookup_by_extension(@details[:format].to_s) if @details[:format] - end - + private def compile(locals, view) diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index a777021a12..fd57b1677e 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,11 +1,20 @@ module ActionView #:nodoc: class TextTemplate < String #:nodoc: + def initialize(string, content_type = Mime[:html]) + super(string.to_s) + @content_type = Mime[content_type] + end + + def details + {:formats => [@content_type.to_sym]} + end + def identifier() self end def render(*) self end - def mime_type() Mime::HTML end + def mime_type() @content_type end def partial?() false end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 22adf97304..7355af4192 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -11,7 +11,7 @@ module ActionView attr_internal :rendered alias_method :_render_template_without_template_tracking, :_render_template def _render_template(template, local_assigns = {}) - if template.respond_to?(:identifier) + if template.respond_to?(:identifier) && template.present? @_rendered[:partials][template] += 1 if template.partial? @_rendered[:template] ||= [] @_rendered[:template] << template diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 6e1c2bf9e8..d3440c3de0 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -1,4 +1,5 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'active_support/core_ext/class/removal' module AbstractControllerTests module Layouts diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 7982f06545..c71da7fa6c 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,6 +1,7 @@ -if ENV["new_base"] - require "abstract_unit2" -else +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') @@ -26,6 +27,8 @@ 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 @@ -41,4 +44,3 @@ 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 -end
\ No newline at end of file diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index 663cd259c8..47f8496181 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -28,9 +28,9 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end def call_reset_session - session[:bar] + session[:foo] reset_session - session[:bar] = "baz" + session[:foo] = "baz" head :ok end @@ -91,7 +91,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest get '/get_session_value' assert_response :success - assert_equal 'foo: nil', response.body + assert_equal 'foo: "baz"', response.body get '/get_session_id' assert_response :success diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 484d3c5ce7..24686ab4b6 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'action_controller/vendor/html-scanner' # a controller class to facilitate the tests class ActionPackAssertionsController < ActionController::Base @@ -12,6 +13,9 @@ class ActionPackAssertionsController < ActionController::Base # a standard template def hello_xml_world() render :template => "test/hello_xml_world"; end + # a standard partial + def partial() render :partial => 'test/partial'; end + # a redirect to an internal location def redirect_internal() redirect_to "/nothing"; end @@ -292,8 +296,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase # make sure that the template objects exist def test_template_objects_alive process :assign_this - assert !@controller.template.assigns['hi'] - assert @controller.template.assigns['howdy'] + assert !@controller.template.instance_variable_get(:"@hi") + assert @controller.template.instance_variable_get(:"@howdy") end # make sure we don't have template objects when we shouldn't @@ -331,6 +335,26 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end end + def test_assert_template_with_partial + get :partial + assert_template :partial => '_partial' + end + + def test_assert_template_with_nil + get :nothing + assert_template nil + end + + def test_assert_template_with_string + get :hello_world + assert_template 'hello_world' + end + + def test_assert_template_with_symbol + get :hello_world + assert_template :hello_world + end + # check if we were rendered by a file-based template? def test_rendered_action process :nothing @@ -421,7 +445,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase assert_equal "Mr. David", @response.body end - def test_assert_redirection_fails_with_incorrect_controller process :redirect_to_controller assert_raise(ActiveSupport::TestCase::Assertion) do diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 560c09509b..c286976315 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -48,6 +48,8 @@ class PageCachingTest < ActionController::TestCase super ActionController::Base.perform_caching = true + ActionController::Routing::Routes.clear! + ActionController::Routing::Routes.draw do |map| map.main '', :controller => 'posts' map.formatted_posts 'posts.:format', :controller => 'posts' diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 64b8b10d5b..d622ac1e85 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -148,12 +148,13 @@ class AcceptBasedContentTypeTest < ActionController::TestCase def setup super + @_old_accept_header = ActionController::Base.use_accept_header ActionController::Base.use_accept_header = true end def teardown super - ActionController::Base.use_accept_header = false + ActionController::Base.use_accept_header = @_old_accept_header end diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index 0f22714071..39d0017dd8 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -4,44 +4,48 @@ class CookieTest < ActionController::TestCase class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" + head :ok end def set_with_with_escapable_characters cookies["that & guy"] = "foo & bar => baz" + head :ok end def authenticate_for_fourteen_days cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) } + head :ok end def authenticate_for_fourteen_days_with_symbols cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) } + head :ok end def set_multiple_cookies cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) } cookies["login"] = "XJ-122" + head :ok end def access_frozen_cookies cookies["will"] = "work" + head :ok end def logout cookies.delete("user_name") + head :ok end def delete_cookie_with_path cookies.delete("user_name", :path => '/beaten') - render :text => "hello world" + head :ok end def authenticate_with_http_only cookies["user_name"] = { :value => "david", :httponly => true } - end - - def rescue_action(e) - raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output + head :ok end end @@ -54,38 +58,38 @@ class CookieTest < ActionController::TestCase def test_setting_cookie get :authenticate - assert_equal "user_name=david; path=/", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=david; path=/" assert_equal({"user_name" => "david"}, @response.cookies) end def test_setting_with_escapable_characters get :set_with_with_escapable_characters - assert_equal "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/", @response.headers["Set-Cookie"] + assert_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/" assert_equal({"that & guy" => "foo & bar => baz"}, @response.cookies) end def test_setting_cookie_for_fourteen_days get :authenticate_for_fourteen_days - assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT" assert_equal({"user_name" => "david"}, @response.cookies) end def test_setting_cookie_for_fourteen_days_with_symbols get :authenticate_for_fourteen_days_with_symbols - assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT" assert_equal({"user_name" => "david"}, @response.cookies) end def test_setting_cookie_with_http_only get :authenticate_with_http_only - assert_equal "user_name=david; path=/; HttpOnly", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=david; path=/; HttpOnly" assert_equal({"user_name" => "david"}, @response.cookies) end def test_multiple_cookies get :set_multiple_cookies assert_equal 2, @response.cookies.size - assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/" assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies) end @@ -95,7 +99,7 @@ class CookieTest < ActionController::TestCase def test_expiring_cookie get :logout - assert_equal "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" assert_equal({"user_name" => nil}, @response.cookies) end @@ -116,6 +120,16 @@ class CookieTest < ActionController::TestCase def test_delete_cookie_with_path get :delete_cookie_with_path - assert_equal "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT", @response.headers["Set-Cookie"] + assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT" end + + private + def assert_cookie_header(expected) + header = @response.headers["Set-Cookie"] + if header.respond_to?(:to_str) + assert_equal expected, header + else + assert_equal expected.split("\n"), header + end + end end diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index b315232a7b..9fae1fcf63 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -6,20 +6,20 @@ class DispatcherTest < Test::Unit::TestCase def setup ENV['REQUEST_METHOD'] = 'GET' - 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 - # Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks - Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + 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) + @old_router, Dispatcher.router = Dispatcher.router, mock() + Dispatcher.router.stubs(:call).returns([200, {}, 'response']) + Dispatcher.router.stubs(:reload) Dispatcher.stubs(:require_dependency) end def teardown + Dispatcher.router = @old_router + @dispatcher = nil ENV.delete 'REQUEST_METHOD' end @@ -29,12 +29,12 @@ class DispatcherTest < Test::Unit::TestCase end def test_reloads_routes_before_dispatch_if_in_loading_mode - ActionController::Routing::Routes.expects(:reload).once + Dispatcher.router.expects(:reload).once dispatch(false) end def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode - ActionController::Routing::Routes.expects(:reload).never + Dispatcher.router.expects(:reload).never ActiveSupport::Dependencies.expects(:clear).never dispatch @@ -55,7 +55,7 @@ class DispatcherTest < Test::Unit::TestCase assert_nil a || b || c # Run callbacks - Dispatcher.run_prepare_callbacks + dispatch assert_equal 1, a assert_equal 2, b @@ -72,16 +72,22 @@ class DispatcherTest < Test::Unit::TestCase Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 } Dispatcher.to_prepare(:unique_id) { |*args| a = 2 } - Dispatcher.run_prepare_callbacks + dispatch assert_equal 2, a assert_equal nil, b end private def dispatch(cache_classes = true) - ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response']) + ActionController::Dispatcher.prepare_each_request = false Dispatcher.define_dispatcher_callbacks(cache_classes) - Dispatcher.new.call({'rack.input' => StringIO.new('')}) + 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.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false}) end def assert_subclasses(howmany, klass, message = klass.subclasses.inspect) diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index 0b259a7980..8c9e4f81de 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -1,13 +1,30 @@ require 'abstract_unit' class FilterParamController < ActionController::Base + def payment + head :ok + end end -class FilterParamTest < Test::Unit::TestCase - def setup - @controller = FilterParamController.new +class FilterParamTest < ActionController::TestCase + tests FilterParamController + + class MockLogger + attr_reader :logged + attr_accessor :level + + def initialize + @level = Logger::DEBUG + end + + def method_missing(method, *args) + @logged ||= [] + @logged << args.first + end end + setup :set_logger + def test_filter_parameters assert FilterParamController.respond_to?(:filter_parameter_logging) assert !@controller.respond_to?(:filter_parameters) @@ -46,4 +63,26 @@ class FilterParamTest < Test::Unit::TestCase assert !FilterParamController.action_methods.include?('filter_parameters') assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) } end + + def test_filter_parameters_inside_logs + FilterParamController.filter_parameter_logging(:lifo, :amount) + + get :payment, :lifo => 'Pratik', :amount => '420', :step => '1' + + filtered_params_logs = logs.detect {|l| l =~ /\AParameters/ } + + assert filtered_params_logs.index('"amount"=>"[FILTERED]"') + assert filtered_params_logs.index('"lifo"=>"[FILTERED]"') + assert filtered_params_logs.index('"step"=>"1"') + end + + private + + def set_logger + @controller.logger = MockLogger.new + end + + def logs + @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip} + end end diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 9ad49e9282..22aa50adb2 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -1,6 +1,13 @@ require 'abstract_unit' -# FIXME: crashes Ruby 1.9 +class << ActionController::Base + %w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending| + define_method(pending) do |*args| + $stderr.puts "#{pending} unimplemented: #{args.inspect}" + end unless method_defined?(pending) + end +end + class FilterTest < Test::Unit::TestCase include ActionController::TestProcess @@ -141,14 +148,6 @@ class FilterTest < Test::Unit::TestCase before_filter :clean_up_tmp, :if => Proc.new { |c| false } end - class EmptyFilterChainController < TestController - self.filter_chain.clear - def show - @action_executed = true - render :text => "yawp!" - end - end - class PrependingController < TestController prepend_before_filter :wonderful_life # skip_before_filter :fire_flash @@ -455,12 +454,6 @@ class FilterTest < Test::Unit::TestCase assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters'] end - def test_empty_filter_chain - assert_equal 0, EmptyFilterChainController.filter_chain.size - test_process(EmptyFilterChainController) - assert @controller.template.assigns['action_executed'] - end - def test_added_filter_to_inheritance_graph assert_equal [ :ensure_login ], TestController.before_filters end @@ -607,7 +600,6 @@ class FilterTest < Test::Unit::TestCase def test_dynamic_dispatch %w(foo bar baz).each do |action| request = ActionController::TestRequest.new - request.env["action_controller.rescue.request"] = request request.query_parameters[:choose] = action response = DynamicDispatchController.action.call(request.env).last assert_equal action, response.body @@ -615,7 +607,6 @@ class FilterTest < Test::Unit::TestCase end def test_running_prepended_before_and_after_filter - assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length test_process(PrependingBeforeAndAfterController) assert_equal %w( before_all between_before_all_and_after_all after_all ), @controller.template.assigns["ran_filter"] end @@ -819,15 +810,6 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase include PostsController::AroundExceptions include ActionController::TestProcess - def test_filters_registering - assert_equal 1, ControllerWithFilterMethod.filter_chain.size - assert_equal 1, ControllerWithFilterClass.filter_chain.size - assert_equal 1, ControllerWithFilterInstance.filter_chain.size - assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size - assert_equal 6, ControllerWithNestedFilters.filter_chain.size - assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size - end - def test_base controller = PostsController assert_nothing_raised { test_process(controller,'no_raise') } diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index ef60cae0ff..84e27d7779 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -60,6 +60,7 @@ class FlashTest < ActionController::TestCase def std_action @flash_copy = {}.update(flash) + render :nothing => true end def filter_halting_action @@ -79,64 +80,64 @@ class FlashTest < ActionController::TestCase get :set_flash get :use_flash - assert_equal "hello", @controller.template.assigns["flash_copy"]["that"] - assert_equal "hello", @controller.template.assigns["flashy"] + assert_equal "hello", assigns["flash_copy"]["that"] + assert_equal "hello", assigns["flashy"] get :use_flash - assert_nil @controller.template.assigns["flash_copy"]["that"], "On second flash" + assert_nil assigns["flash_copy"]["that"], "On second flash" end def test_keep_flash get :set_flash get :use_flash_and_keep_it - assert_equal "hello", @controller.template.assigns["flash_copy"]["that"] - assert_equal "hello", @controller.template.assigns["flashy"] + assert_equal "hello", assigns["flash_copy"]["that"] + assert_equal "hello", assigns["flashy"] get :use_flash - assert_equal "hello", @controller.template.assigns["flash_copy"]["that"], "On second flash" + assert_equal "hello", assigns["flash_copy"]["that"], "On second flash" get :use_flash - assert_nil @controller.template.assigns["flash_copy"]["that"], "On third flash" + assert_nil assigns["flash_copy"]["that"], "On third flash" end def test_flash_now get :set_flash_now - assert_equal "hello", @controller.template.assigns["flash_copy"]["that"] - assert_equal "bar" , @controller.template.assigns["flash_copy"]["foo"] - assert_equal "hello", @controller.template.assigns["flashy"] + assert_equal "hello", assigns["flash_copy"]["that"] + assert_equal "bar" , assigns["flash_copy"]["foo"] + assert_equal "hello", assigns["flashy"] get :attempt_to_use_flash_now - assert_nil @controller.template.assigns["flash_copy"]["that"] - assert_nil @controller.template.assigns["flash_copy"]["foo"] - assert_nil @controller.template.assigns["flashy"] + assert_nil assigns["flash_copy"]["that"] + assert_nil assigns["flash_copy"]["foo"] + assert_nil assigns["flashy"] end def test_update_flash get :set_flash get :use_flash_and_update_it - assert_equal "hello", @controller.template.assigns["flash_copy"]["that"] - assert_equal "hello again", @controller.template.assigns["flash_copy"]["this"] + assert_equal "hello", assigns["flash_copy"]["that"] + assert_equal "hello again", assigns["flash_copy"]["this"] get :use_flash - assert_nil @controller.template.assigns["flash_copy"]["that"], "On second flash" - assert_equal "hello again", @controller.template.assigns["flash_copy"]["this"], "On second flash" + assert_nil assigns["flash_copy"]["that"], "On second flash" + assert_equal "hello again", assigns["flash_copy"]["this"], "On second flash" end def test_flash_after_reset_session get :use_flash_after_reset_session - assert_equal "hello", @controller.template.assigns["flashy_that"] - assert_equal "good-bye", @controller.template.assigns["flashy_this"] - assert_nil @controller.template.assigns["flashy_that_reset"] + assert_equal "hello", assigns["flashy_that"] + assert_equal "good-bye", assigns["flashy_this"] + assert_nil assigns["flashy_that_reset"] end def test_sweep_after_halted_filter_chain get :std_action - assert_nil @controller.template.assigns["flash_copy"]["foo"] + assert_nil assigns["flash_copy"]["foo"] get :filter_halting_action - assert_equal "bar", @controller.template.assigns["flash_copy"]["foo"] + assert_equal "bar", assigns["flash_copy"]["foo"] get :std_action # follow redirection - assert_equal "bar", @controller.template.assigns["flash_copy"]["foo"] + assert_equal "bar", assigns["flash_copy"]["foo"] get :std_action - assert_nil @controller.template.assigns["flash_copy"]["foo"] + assert_nil assigns["flash_copy"]["foo"] end end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 7b8096fccc..5b9feb3630 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -27,7 +27,7 @@ module Fun end end -class ApplicationController < ActionController::Base +class AllHelpersController < ActionController::Base helper :all end @@ -104,7 +104,6 @@ class HelperTest < Test::Unit::TestCase def call_controller(klass, action) request = ActionController::TestRequest.new - request.env["action_controller.rescue.request"] = request klass.action(action).call(request.env) end @@ -112,7 +111,6 @@ class HelperTest < Test::Unit::TestCase assert_equal 'hello: Iz guuut!', call_controller(Fun::GamesController, "render_hello_world").last.body # request = ActionController::TestRequest.new - # request.env["action_controller.rescue.request"] = request # # resp = Fun::GamesController.action(:render_hello_world).call(request.env) # assert_equal 'hello: Iz guuut!', resp.last.body @@ -129,7 +127,7 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s) + methods = AllHelpersController.master_helper_module.instance_methods.map(&:to_s) # abc_helper.rb assert methods.include?('bare_a') @@ -156,7 +154,7 @@ class HelperTest < Test::Unit::TestCase end def test_helper_proxy - methods = ApplicationController.helpers.methods.map(&:to_s) + methods = AllHelpersController.helpers.methods.map(&:to_s) # ActionView assert methods.include?('pluralize') @@ -217,7 +215,6 @@ class IsolatedHelpersTest < Test::Unit::TestCase def call_controller(klass, action) request = ActionController::TestRequest.new - request.env["action_controller.rescue.request"] = request klass.action(action).call(request.env) end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 7bebc8cd2a..15a11395bb 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -38,6 +38,15 @@ class HttpDigestAuthenticationTest < ActionController::TestCase tests DummyDigestController + setup do + # Used as secret in generating nonce to prevent tampering of timestamp + @old_secret, ActionController::Base.session_options[:secret] = ActionController::Base.session_options[:secret], "session_options_secret" + end + + teardown do + ActionController::Base.session_options[:secret] = @old_secret + end + AUTH_HEADERS.each do |header| test "successful authentication with #{header.downcase}" do @request.env[header] = encode_credentials(:username => 'lifo', :password => 'world') @@ -149,25 +158,38 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "authentication request with _method" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :method => :post) + @request.env['rack.methodoverride.original_method'] = 'POST' + put :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + private def encode_credentials(options) options.reverse_merge!(:nc => "00000001", :cnonce => "0a4f113b", :password_is_ha1 => false) password = options.delete(:password) - # Set in /initializers/session_store.rb. Used as secret in generating nonce - # to prevent tampering of timestamp - ActionController::Base.session_options[:secret] = "session_options_secret" + # Perform unauthenticated request to retrieve digest parameters to use on subsequent request + method = options.delete(:method) || 'GET' - # Perform unauthenticated GET to retrieve digest parameters to use on subsequent request - get :index + case method.to_s.upcase + when 'GET' + get :index + when 'POST' + post :index + end assert_response :unauthorized credentials = decode_credentials(@response.headers['WWW-Authenticate']) credentials.merge!(options) credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}") - ActionController::HttpAuthentication::Digest.encode_credentials("GET", credentials, password, options[:password_is_ha1]) + ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1]) end def decode_credentials(header) diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index c616107324..197ba0c69c 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'action_controller/vendor/html-scanner' class SessionTest < Test::Unit::TestCase StubApp = lambda { |env| @@ -123,8 +124,8 @@ class SessionTest < Test::Unit::TestCase def test_xml_http_request_get path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest", - "Accept" => "text/javascript, text/html, application/xml, text/xml, */*" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" ) @session.expects(:process).with(:get,path,params,headers_after_xhr) @session.xml_http_request(:get,path,params,headers) @@ -133,8 +134,8 @@ class SessionTest < Test::Unit::TestCase def test_xml_http_request_post path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest", - "Accept" => "text/javascript, text/html, application/xml, text/xml, */*" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" ) @session.expects(:process).with(:post,path,params,headers_after_xhr) @session.xml_http_request(:post,path,params,headers) @@ -143,8 +144,8 @@ class SessionTest < Test::Unit::TestCase def test_xml_http_request_put path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest", - "Accept" => "text/javascript, text/html, application/xml, text/xml, */*" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" ) @session.expects(:process).with(:put,path,params,headers_after_xhr) @session.xml_http_request(:put,path,params,headers) @@ -153,8 +154,8 @@ class SessionTest < Test::Unit::TestCase def test_xml_http_request_delete path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest", - "Accept" => "text/javascript, text/html, application/xml, text/xml, */*" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" ) @session.expects(:process).with(:delete,path,params,headers_after_xhr) @session.xml_http_request(:delete,path,params,headers) @@ -163,17 +164,17 @@ class SessionTest < Test::Unit::TestCase def test_xml_http_request_head path = "/index"; params = "blah"; headers = {:location => 'blah'} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest", - "Accept" => "text/javascript, text/html, application/xml, text/xml, */*" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*" ) @session.expects(:process).with(:head,path,params,headers_after_xhr) @session.xml_http_request(:head,path,params,headers) end def test_xml_http_request_override_accept - path = "/index"; params = "blah"; headers = {:location => 'blah', "Accept" => "application/xml"} + path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"} headers_after_xhr = headers.merge( - "X-Requested-With" => "XMLHttpRequest" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" ) @session.expects(:process).with(:post,path,params,headers_after_xhr) @session.xml_http_request(:post,path,params,headers) @@ -253,7 +254,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest assert_response 200 assert_response :success assert_response :ok - assert_equal({}, cookies) + assert_equal({}, cookies.to_hash) assert_equal "OK", body assert_equal "OK", response.body assert_kind_of HTML::Document, html_document @@ -269,7 +270,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest assert_response 201 assert_response :success assert_response :created - assert_equal({}, cookies) + assert_equal({}, cookies.to_hash) assert_equal "Created", body assert_equal "Created", response.body assert_kind_of HTML::Document, html_document @@ -287,7 +288,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest assert_response 410 assert_response :gone assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"] - assert_equal({"cookie_1"=>nil, "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies) + assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies.to_hash) assert_equal "Gone", response.body end end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 5b7d40d16d..cb9bdf57bb 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -9,8 +9,11 @@ ActionView::Template::register_template_handler :mab, ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ] +require "fixture_template" + class LayoutTest < ActionController::Base def self.controller_path; 'views' end + def self._implied_layout_name; to_s.underscore.gsub(/_controller$/, '') ; end self.view_paths = ActionController::Base.view_paths.dup end @@ -35,6 +38,15 @@ end class MultipleExtensions < LayoutTest end +if defined?(ActionController::Http) + LayoutTest._write_layout_method + ProductController._write_layout_method + ItemController._write_layout_method + ThirdPartyTemplateLibraryController._write_layout_method + MultipleExtensions._write_layout_method + ControllerNameSpace::NestedController._write_layout_method +end + class LayoutAutoDiscoveryTest < ActionController::TestCase def setup super @@ -56,23 +68,19 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase def test_third_party_template_library_auto_discovers_layout @controller = ThirdPartyTemplateLibraryController.new get :hello - assert @controller.active_layout(true).identifier.include?('layouts/third_party_template_library.mab') - assert @controller.template.layout.include?('layouts/third_party_template_library') assert_response :success - assert_equal 'Mab', @response.body + assert_equal 'layouts/third_party_template_library.mab', @response.body end - def test_namespaced_controllers_auto_detect_layouts + def test_namespaced_controllers_auto_detect_layouts1 @controller = ControllerNameSpace::NestedController.new get :hello - assert_equal 'layouts/controller_name_space/nested', @controller.active_layout(true).to_s assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body end - def test_namespaced_controllers_auto_detect_layouts + def test_namespaced_controllers_auto_detect_layouts2 @controller = MultipleExtensions.new get :hello - assert @controller.active_layout(true).identifier.include?('layouts/multiple_extensions.html.erb') assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip end end @@ -139,7 +147,7 @@ class LayoutSetInResponseTest < ActionController::TestCase def test_layout_only_exception_when_excepted @controller = OnlyLayoutController.new get :goodbye - assert_equal nil, @controller.template.layout + assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'" end def test_layout_except_exception_when_included @@ -151,7 +159,7 @@ class LayoutSetInResponseTest < ActionController::TestCase def test_layout_except_exception_when_excepted @controller = ExceptLayoutController.new get :goodbye - assert_equal nil, @controller.template.layout + assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'" end def test_layout_set_when_using_render @@ -166,15 +174,18 @@ class LayoutSetInResponseTest < ActionController::TestCase assert_nil @controller.template.layout end - def test_exempt_from_layout_honored_by_render_template - ActionController::Base.exempt_from_layout :erb - @controller = RenderWithTemplateOptionController.new + for_tag(:old_base) do + # exempt_from_layout is deprecated + def test_exempt_from_layout_honored_by_render_template + ActionController::Base.exempt_from_layout :erb + @controller = RenderWithTemplateOptionController.new - get :hello - assert_equal "alt/hello.rhtml", @response.body.strip + get :hello + assert_equal "alt/hello.rhtml", @response.body.strip - ensure - ActionController::Base.exempt_from_layout.delete(ERB) + ensure + ActionController::Base.exempt_from_layout.delete(ERB) + end end def test_layout_is_picked_from_the_controller_instances_view_path diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb index 1f3ff4ef52..a7ed1b8665 100644 --- a/actionpack/test/controller/logging_test.rb +++ b/actionpack/test/controller/logging_test.rb @@ -11,6 +11,11 @@ class LoggingTest < ActionController::TestCase class MockLogger attr_reader :logged + attr_accessor :level + + def initialize + @level = Logger::DEBUG + end def method_missing(method, *args) @logged ||= [] @@ -30,7 +35,7 @@ class LoggingTest < ActionController::TestCase end def test_logging_with_parameters - get :show, :id => 10 + get :show, :id => '10' assert_equal 3, logs.size params = logs.detect {|l| l =~ /Parameters/ } @@ -44,6 +49,6 @@ class LoggingTest < ActionController::TestCase end def logs - @logs ||= @controller.logger.logged.compact.map {|l| l.strip} + @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 7cd5145a2f..56b49251c6 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -375,9 +375,11 @@ class MimeControllerTest < ActionController::TestCase end def test_rjs_type_skips_layout - @request.accept = "text/javascript" - get :all_types_with_layout - assert_equal 'RJS for all_types_with_layout', @response.body + pending(:new_base) do + @request.accept = "text/javascript" + get :all_types_with_layout + assert_equal 'RJS for all_types_with_layout', @response.body + end end def test_html_type_with_layout @@ -437,7 +439,7 @@ class MimeControllerTest < ActionController::TestCase @controller.instance_eval do def render(*args) unless args.empty? - @action = args.first[:action] + @action = args.first[:action] || action_name end response.body = "#{@action} - #{@template.formats}" end @@ -490,14 +492,15 @@ class PostController < AbstractPostController end end - protected - def with_iphone - Mime::Type.register_alias("text/html", :iphone) - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - yield - ensure - Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } - end +protected + + def with_iphone + Mime::Type.register_alias("text/html", :iphone) + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + yield + ensure + Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } + end end class SuperPostController < PostController @@ -509,6 +512,11 @@ class SuperPostController < PostController end end +if defined?(ActionController::Http) + PostController._write_layout_method + SuperPostController._write_layout_method +end + class MimeControllerLayoutsTest < ActionController::TestCase tests PostController @@ -526,14 +534,16 @@ class MimeControllerLayoutsTest < ActionController::TestCase assert_equal 'Hello iPhone', @response.body end - def test_format_with_inherited_layouts - @controller = SuperPostController.new + for_tag(:old_base) do + def test_format_with_inherited_layouts + @controller = SuperPostController.new - get :index - assert_equal 'Super Firefox', @response.body + get :index + assert_equal 'Super Firefox', @response.body - @request.accept = "text/iphone" - get :index - assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body + @request.accept = "text/iphone" + get :index + assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body + end end end diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index 7b50242910..d02fd3fd4c 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -19,6 +19,8 @@ class TestController < ActionController::Base end class RenderTest < ActionController::TestCase + tests TestController + def test_render_vanilla_js get :render_vanilla_js_hello assert_equal "alert('hello')", @response.body diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index ddbdd2d213..05645e47fa 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -4,6 +4,7 @@ require 'pathname' class TestController < ActionController::Base protect_from_forgery + layout :determine_layout module RenderTestHelper def rjs_helper_method_from_module @@ -103,11 +104,26 @@ class TestController < ActionController::Base end private + def default_render + if @alternate_default_render + @alternate_default_render.call + else + super + end + end + def determine_layout case action_name - when "render_js_with_explicit_template", - "render_js_with_explicit_action_template", - "delete_with_js", "update_page", "update_page_with_instance_variables" + 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" diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index a750f018b8..9e42d1738a 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -625,9 +625,7 @@ class TestController < ActionController::Base "accessing_params_in_template_with_layout", "render_with_explicit_template", "render_with_explicit_string_template", - "render_js_with_explicit_template", - "render_js_with_explicit_action_template", - "delete_with_js", "update_page", "update_page_with_instance_variables" + "update_page", "update_page_with_instance_variables" "layouts/standard" when "action_talk_to_layout", "layout_overriding_layout" diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index f745926b20..490a4ff3b3 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -1,5 +1,19 @@ require 'abstract_unit' +module ActionDispatch + class ShowExceptions + private + def public_path + "#{FIXTURE_LOAD_PATH}/public" + end + + # Silence logger + def logger + nil + end + end +end + class RescueController < ActionController::Base class NotAuthorized < StandardError end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 77abb68f32..11bffdb42e 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'controller/fake_controllers' +require 'active_support/dependencies' class MilestonesController < ActionController::Base def index() head :ok end diff --git a/actionpack/test/controller/selector_test.rb b/actionpack/test/controller/selector_test.rb index 9d0613d1e2..5a5dc840b5 100644 --- a/actionpack/test/controller/selector_test.rb +++ b/actionpack/test/controller/selector_test.rb @@ -5,6 +5,7 @@ require 'abstract_unit' require 'controller/fake_controllers' +require 'action_controller/vendor/html-scanner' class SelectorTest < Test::Unit::TestCase # diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 6007ebef7a..0bc0eb2df6 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -11,12 +11,17 @@ class SendFileController < ActionController::Base layout "layouts/standard" # to make sure layouts don't interfere attr_writer :options - def options() @options ||= {} end + def options + @options ||= {} + end - def file() send_file(file_path, options) end - def data() send_data(file_data, options) end + def file + send_file(file_path, options) + end - def rescue_action(e) raise end + def data + send_data(file_data, options) + end end class SendFileTest < ActionController::TestCase @@ -40,17 +45,19 @@ class SendFileTest < ActionController::TestCase assert_equal file_data, response.body end - def test_file_stream - response = nil - assert_nothing_raised { response = process('file') } - assert_not_nil response - assert_kind_of Array, response.body_parts - - require 'stringio' - output = StringIO.new - output.binmode - assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } } - assert_equal file_data, output.string + for_tag(:old_base) do + def test_file_stream + response = nil + assert_nothing_raised { response = process('file') } + assert_not_nil response + assert_kind_of Array, response.body_parts + + require 'stringio' + output = StringIO.new + output.binmode + assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } } + assert_equal file_data, output.string + end end def test_file_url_based_filename diff --git a/actionpack/test/controller/verification_test.rb b/actionpack/test/controller/verification_test.rb index 418a81baa8..d568030e41 100644 --- a/actionpack/test/controller/verification_test.rb +++ b/actionpack/test/controller/verification_test.rb @@ -103,17 +103,15 @@ class VerificationTest < ActionController::TestCase end protected - def rescue_action(e) raise end - def unconditional_redirect - redirect_to :action => "unguarded" - end + def unconditional_redirect + redirect_to :action => "unguarded" + end end - def setup - @controller = TestController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new + tests TestController + + setup do ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo' end @@ -184,7 +182,7 @@ class VerificationTest < ActionController::TestCase def test_unguarded_without_params get :unguarded - assert_equal "", @response.body + assert @response.body.blank? end def test_guarded_in_session_with_prereqs diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index e89d6bb960..9bf8da7276 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -34,7 +34,7 @@ class WebServiceTest < ActionController::IntegrationTest def test_check_parameters with_test_route_set do get "/" - assert_equal '', @controller.response.body + assert @controller.response.body.blank? end end @@ -163,7 +163,7 @@ class WebServiceTest < ActionController::IntegrationTest with_test_route_set do ActionController::Base.param_parsers[Mime::XML] = :xml_simple assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} } - assert_equal "", @controller.response.body + assert @controller.response.body.blank? end end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index f8f562e7c1..0c0c087340 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -6,6 +6,11 @@ module ActionDispatch def public_path "#{FIXTURE_LOAD_PATH}/public" end + + # Silence logger + def logger + nil + end end end diff --git a/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab b/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab index 018abfb0ac..fcee620d82 100644 --- a/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab +++ b/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab @@ -1 +1 @@ -Mab
\ No newline at end of file +layouts/third_party_template_library.mab
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/greeting.erb b/actionpack/test/fixtures/test/greeting.html.erb index 62fb0293f0..62fb0293f0 100644 --- a/actionpack/test/fixtures/test/greeting.erb +++ b/actionpack/test/fixtures/test/greeting.html.erb diff --git a/actionpack/test/controller/html-scanner/cdata_node_test.rb b/actionpack/test/html-scanner/cdata_node_test.rb index 1822cc565a..1822cc565a 100644 --- a/actionpack/test/controller/html-scanner/cdata_node_test.rb +++ b/actionpack/test/html-scanner/cdata_node_test.rb diff --git a/actionpack/test/controller/html-scanner/document_test.rb b/actionpack/test/html-scanner/document_test.rb index c68f04fa75..c68f04fa75 100644 --- a/actionpack/test/controller/html-scanner/document_test.rb +++ b/actionpack/test/html-scanner/document_test.rb diff --git a/actionpack/test/controller/html-scanner/node_test.rb b/actionpack/test/html-scanner/node_test.rb index b0df36877e..b0df36877e 100644 --- a/actionpack/test/controller/html-scanner/node_test.rb +++ b/actionpack/test/html-scanner/node_test.rb diff --git a/actionpack/test/controller/html-scanner/sanitizer_test.rb b/actionpack/test/html-scanner/sanitizer_test.rb index e85a5c7abf..e85a5c7abf 100644 --- a/actionpack/test/controller/html-scanner/sanitizer_test.rb +++ b/actionpack/test/html-scanner/sanitizer_test.rb diff --git a/actionpack/test/controller/html-scanner/tag_node_test.rb b/actionpack/test/html-scanner/tag_node_test.rb index d1d4667378..d1d4667378 100644 --- a/actionpack/test/controller/html-scanner/tag_node_test.rb +++ b/actionpack/test/html-scanner/tag_node_test.rb diff --git a/actionpack/test/controller/html-scanner/text_node_test.rb b/actionpack/test/html-scanner/text_node_test.rb index 1ab3f4454e..1ab3f4454e 100644 --- a/actionpack/test/controller/html-scanner/text_node_test.rb +++ b/actionpack/test/html-scanner/text_node_test.rb diff --git a/actionpack/test/controller/html-scanner/tokenizer_test.rb b/actionpack/test/html-scanner/tokenizer_test.rb index a001bcbbad..a001bcbbad 100644 --- a/actionpack/test/controller/html-scanner/tokenizer_test.rb +++ b/actionpack/test/html-scanner/tokenizer_test.rb diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/lib/active_record_unit.rb index 9e0c66055d..1ba308e9d7 100644 --- a/actionpack/test/active_record_unit.rb +++ b/actionpack/test/lib/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/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb index 75c114c103..75c114c103 100644 --- a/actionpack/test/controller/fake_controllers.rb +++ b/actionpack/test/lib/controller/fake_controllers.rb diff --git a/actionpack/test/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 0b30c79b10..0b30c79b10 100644 --- a/actionpack/test/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/test/lib/fixture_template.rb index e43e329a9e..59fb6819ed 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/test/lib/fixture_template.rb @@ -46,7 +46,7 @@ class Template end end - %r'#{Regexp.escape(path)}#{extensions}#{handler_regexp}' + %r'^#{Regexp.escape(path)}#{extensions}#{handler_regexp}$' end # TODO: fix me diff --git a/actionpack/test/testing_sandbox.rb b/actionpack/test/lib/testing_sandbox.rb index c36585104f..c36585104f 100644 --- a/actionpack/test/testing_sandbox.rb +++ b/actionpack/test/lib/testing_sandbox.rb diff --git a/actionpack/test/abstract_unit2.rb b/actionpack/test/new_base/abstract_unit.rb index 519e6bea36..e6690d41d9 100644 --- a/actionpack/test/abstract_unit2.rb +++ b/actionpack/test/new_base/abstract_unit.rb @@ -1,10 +1,19 @@ +$:.unshift(File.dirname(__FILE__) + '/../../lib') +$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') $:.unshift(File.dirname(__FILE__) + '/../lib') -$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/lib') +$:.unshift(File.dirname(__FILE__) + '/../fixtures/helpers') +$:.unshift(File.dirname(__FILE__) + '/../fixtures/alternate_helpers') + +ENV['new_base'] = "true" +$stderr.puts "Running old tests on new_base" require 'test/unit' require 'active_support' + +# TODO : Revisit requiring all the core extensions here +require 'active_support/core_ext' + require 'active_support/test_case' require 'action_controller/abstract' require 'action_controller/new_base' @@ -14,9 +23,27 @@ require 'action_view/test_case' require 'action_controller/testing/integration' require 'active_support/dependencies' +$tags[:new_base] = true + +begin + require 'ruby-debug' + Debugger.settings[:autoeval] = true + Debugger.start +rescue LoadError + # Debugging disabled. `gem install ruby-debug` to enable. +end + ActiveSupport::Dependencies.hook! -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +# Show backtraces for deprecated behavior for quicker cleanup. +ActiveSupport::Deprecation.debug = true + +# 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') module ActionController Base.session = { @@ -101,7 +128,7 @@ module ActionController hax = @controller._action_view.instance_variable_get(:@_rendered) case options - when NilClass, String + when NilClass, String rendered = (hax[:template] || []).map { |t| t.identifier } msg = build_message(message, "expecting <?> but rendering with <?>", diff --git a/actionpack/test/new_base/base_test.rb b/actionpack/test/new_base/base_test.rb index a32653f128..d9d552f9e5 100644 --- a/actionpack/test/new_base/base_test.rb +++ b/actionpack/test/new_base/base_test.rb @@ -10,78 +10,60 @@ module Dispatching def modify_response_body self.response_body = "success" end - + def modify_response_body_twice ret = (self.response_body = "success") self.response_body = "#{ret}!" end - + def modify_response_headers - end end - - class TestSimpleDispatch < SimpleRouteCase - - get "/dispatching/simple/index" - - test "sets the body" do + + class EmptyController < ActionController::Base ; end + + module Submodule + class ContainedEmptyController < ActionController::Base ; end + end + + class BaseTest < SimpleRouteCase + # :api: plugin + test "simple dispatching" do + get "/dispatching/simple/index" + assert_body "success" - end - - test "sets the status code" do assert_status 200 - end - - test "sets the content type" do assert_content_type "text/html; charset=utf-8" - end - - test "sets the content length" do assert_header "Content-Length", "7" end - - end - - # :api: plugin - class TestDirectResponseMod < SimpleRouteCase - get "/dispatching/simple/modify_response_body" - - test "sets the body" do + + # :api: plugin + test "directly modifying response body" do + get "/dispatching/simple/modify_response_body" + assert_body "success" + assert_header "Content-Length", "7" # setting the body manually sets the content length end - - test "setting the body manually sets the content length" do - assert_header "Content-Length", "7" - end - end - - # :api: plugin - class TestDirectResponseModTwice < SimpleRouteCase - get "/dispatching/simple/modify_response_body_twice" - - test "self.response_body= returns the body being set" do + + # :api: plugin + test "directly modifying response body twice" do + get "/dispatching/simple/modify_response_body_twice" + assert_body "success!" - end - - test "updating the response body updates the content length" do assert_header "Content-Length", "8" end - end - - class EmptyController < ActionController::Base ; end - module Submodule - class ContainedEmptyController < ActionController::Base ; end - end - class ControllerClassTests < Test::Unit::TestCase - def test_controller_path + test "controller path" do assert_equal 'dispatching/empty', EmptyController.controller_path assert_equal EmptyController.controller_path, EmptyController.new.controller_path + end + + test "namespaced controller path" do assert_equal 'dispatching/submodule/contained_empty', Submodule::ContainedEmptyController.controller_path assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path end - def test_controller_name + + test "controller name" do assert_equal 'empty', EmptyController.controller_name assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name end diff --git a/actionpack/test/new_base/content_type_test.rb b/actionpack/test/new_base/content_type_test.rb index a5c04e9cb6..82b817a5a3 100644 --- a/actionpack/test/new_base/content_type_test.rb +++ b/actionpack/test/new_base/content_type_test.rb @@ -5,107 +5,107 @@ module ContentType def index render :text => "Hello world!" end - + def set_on_response_obj response.content_type = Mime::RSS render :text => "Hello world!" end - + def set_on_render render :text => "Hello world!", :content_type => Mime::RSS end end - - class TestDefault < SimpleRouteCase - describe "a default response is HTML and UTF8" - - get "/content_type/base" - assert_body "Hello world!" - assert_header "Content-Type", "text/html; charset=utf-8" - end - - class TestSetOnResponseObj < SimpleRouteCase - describe "setting the content type of the response directly on the response object" - - get "/content_type/base/set_on_response_obj" - assert_body "Hello world!" - assert_header "Content-Type", "application/rss+xml; charset=utf-8" - end - - class TestSetOnRender < SimpleRouteCase - describe "setting the content type of the response as an option to render" - - get "/content_type/base/set_on_render" - assert_body "Hello world!" - assert_header "Content-Type", "application/rss+xml; charset=utf-8" - end - + class ImpliedController < ActionController::Base + # Template's mime type is used if no content_type is specified + self.view_paths = [ActionView::Template::FixturePath.new( "content_type/implied/i_am_html_erb.html.erb" => "Hello world!", "content_type/implied/i_am_xml_erb.xml.erb" => "<xml>Hello world!</xml>", "content_type/implied/i_am_html_builder.html.builder" => "xml.p 'Hello'", "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'" )] - + def i_am_html_erb() end def i_am_xml_erb() end def i_am_html_builder() end def i_am_xml_builder() end end - - class TestImpliedController < SimpleRouteCase - describe "the template's mime type is used if no content_type is specified" - + + class CharsetController < ActionController::Base + def set_on_response_obj + response.charset = "utf-16" + render :text => "Hello world!" + end + + def set_as_nil_on_response_obj + response.charset = nil + render :text => "Hello world!" + end + end + + class ExplicitContentTypeTest < SimpleRouteCase + test "default response is HTML and UTF8" do + get "/content_type/base" + + assert_body "Hello world!" + assert_header "Content-Type", "text/html; charset=utf-8" + end + + test "setting the content type of the response directly on the response object" do + get "/content_type/base/set_on_response_obj" + + assert_body "Hello world!" + assert_header "Content-Type", "application/rss+xml; charset=utf-8" + end + + test "setting the content type of the response as an option to render" do + get "/content_type/base/set_on_render" + + assert_body "Hello world!" + assert_header "Content-Type", "application/rss+xml; charset=utf-8" + end + end + + class ImpliedContentTypeTest < SimpleRouteCase test "sets Content-Type as text/html when rendering *.html.erb" do get "/content_type/implied/i_am_html_erb" + assert_header "Content-Type", "text/html; charset=utf-8" end test "sets Content-Type as application/xml when rendering *.xml.erb" do get "/content_type/implied/i_am_xml_erb" + assert_header "Content-Type", "application/xml; charset=utf-8" end test "sets Content-Type as text/html when rendering *.html.builder" do get "/content_type/implied/i_am_html_builder" + assert_header "Content-Type", "text/html; charset=utf-8" end test "sets Content-Type as application/xml when rendering *.xml.builder" do get "/content_type/implied/i_am_xml_builder" + assert_header "Content-Type", "application/xml; charset=utf-8" end - end -end -module Charset - class BaseController < ActionController::Base - def set_on_response_obj - response.charset = "utf-16" - render :text => "Hello world!" + class ExplicitCharsetTest < SimpleRouteCase + test "setting the charset of the response directly on the response object" do + get "/content_type/charset/set_on_response_obj" + + assert_body "Hello world!" + assert_header "Content-Type", "text/html; charset=utf-16" end - - def set_as_nil_on_response_obj - response.charset = nil - render :text => "Hello world!" + + test "setting the charset of the response as nil directly on the response object" do + get "/content_type/charset/set_as_nil_on_response_obj" + + assert_body "Hello world!" + assert_header "Content-Type", "text/html; charset=utf-8" end end - - class TestSetOnResponseObj < SimpleRouteCase - describe "setting the charset of the response directly on the response object" - - get "/charset/base/set_on_response_obj" - assert_body "Hello world!" - assert_header "Content-Type", "text/html; charset=utf-16" - end - - class TestSetAsNilOnResponseObj < SimpleRouteCase - describe "setting the charset of the response as nil directly on the response object" - - get "/charset/base/set_as_nil_on_response_obj" - assert_body "Hello world!" - assert_header "Content-Type", "text/html; charset=utf-8" - end -end
\ No newline at end of file +end diff --git a/actionpack/test/new_base/etag_test.rb b/actionpack/test/new_base/etag_test.rb index 7af5febfb3..a40d3c936a 100644 --- a/actionpack/test/new_base/etag_test.rb +++ b/actionpack/test/new_base/etag_test.rb @@ -1,47 +1,46 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module Etags - class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( "etags/basic/base.html.erb" => "Hello from without_layout.html.erb", "layouts/etags.html.erb" => "teh <%= yield %> tagz" )] - + def without_layout render :action => "base" end - + def with_layout - render :action => "base", :layout => "etag" + render :action => "base", :layout => "etags" end - end - - class TestBasic < SimpleRouteCase + + class EtagTest < SimpleRouteCase 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 get "/etags/basic/without_layout" + body = "Hello from without_layout.html.erb" assert_body body assert_header "Etag", etag_for(body) assert_status 200 end - + test "an action with a layout" do get "/etags/basic/with_layout" + body = "teh Hello from without_layout.html.erb tagz" assert_body body assert_header "Etag", etag_for(body) assert_status 200 end - + + private + def etag_for(text) %("#{Digest::MD5.hexdigest(text)}") end end - - end
\ No newline at end of file diff --git a/actionpack/test/new_base/render_action_test.rb b/actionpack/test/new_base/render_action_test.rb index f25faee433..4402eadf42 100644 --- a/actionpack/test/new_base/render_action_test.rb +++ b/actionpack/test/new_base/render_action_test.rb @@ -1,26 +1,24 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module RenderAction - # This has no layout and it works class BasicController < ActionController::Base - self.view_paths = [ActionView::Template::FixturePath.new( "render_action/basic/hello_world.html.erb" => "Hello world!" )] - + def hello_world render :action => "hello_world" end - + def hello_world_as_string render "hello_world" end - + def hello_world_as_string_with_options render "hello_world", :status => 404 end - + def hello_world_as_symbol render :hello_world end @@ -28,105 +26,95 @@ module RenderAction def hello_world_with_symbol render :action => :hello_world end - + def hello_world_with_layout render :action => "hello_world", :layout => true end - + def hello_world_with_layout_false render :action => "hello_world", :layout => false end - + def hello_world_with_layout_nil render :action => "hello_world", :layout => nil end - + def hello_world_with_custom_layout render :action => "hello_world", :layout => "greetings" end - - end - - class TestBasic < SimpleRouteCase - describe "Rendering an action using :action => <String>" - - get "/render_action/basic/hello_world" - assert_body "Hello world!" - assert_status 200 - end - - class TestWithString < SimpleRouteCase - describe "Render an action using 'hello_world'" - - get "/render_action/basic/hello_world_as_string" - assert_body "Hello world!" - assert_status 200 - end - - class TestWithStringAndOptions < SimpleRouteCase - describe "Render an action using 'hello_world'" - - get "/render_action/basic/hello_world_as_string_with_options" - assert_body "Hello world!" - assert_status 404 - end - - class TestAsSymbol < SimpleRouteCase - describe "Render an action using :hello_world" - - get "/render_action/basic/hello_world_as_symbol" - assert_body "Hello world!" - assert_status 200 + end - - class TestWithSymbol < SimpleRouteCase - describe "Render an action using :action => :hello_world" - - get "/render_action/basic/hello_world_with_symbol" - assert_body "Hello world!" - assert_status 200 + + class RenderActionTest < SimpleRouteCase + test "rendering an action using :action => <String>" do + get "/render_action/basic/hello_world" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering an action using '<action>'" do + get "/render_action/basic/hello_world_as_string" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering an action using '<action>' and options" do + get "/render_action/basic/hello_world_as_string_with_options" + + assert_body "Hello world!" + assert_status 404 + end + + test "rendering an action using :action" do + get "/render_action/basic/hello_world_as_symbol" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering an action using :action => :hello_world" do + get "/render_action/basic/hello_world_with_symbol" + + assert_body "Hello world!" + assert_status 200 + end end - - class TestLayoutTrue < SimpleRouteCase - describe "rendering a normal template with full path with layout => true" - - test "raises an exception when requesting a layout and none exist" do - assert_raise(ArgumentError, /no default layout for RenderAction::BasicController in/) do - get "/render_action/basic/hello_world_with_layout" + + class RenderLayoutTest < SimpleRouteCase + describe "Both <controller_path>.html.erb and application.html.erb are missing" + + test "rendering with layout => true" do + assert_raise(ArgumentError, /no default layout for RenderAction::BasicController in/) do + get "/render_action/basic/hello_world_with_layout", {}, "action_dispatch.show_exceptions" => false end end - end - - class TestLayoutFalse < SimpleRouteCase - describe "rendering a normal template with full path with layout => false" - - get "/render_action/basic/hello_world_with_layout_false" - assert_body "Hello world!" - assert_status 200 - end - - class TestLayoutNil < SimpleRouteCase - describe "rendering a normal template with full path with layout => :nil" - - get "/render_action/basic/hello_world_with_layout_nil" - assert_body "Hello world!" - assert_status 200 - end - - class TestCustomLayout < SimpleRouteCase - describe "rendering a normal template with full path with layout => 'greetings'" - - test "raises an exception when requesting a layout that does not exist" do - assert_raise(ActionView::MissingTemplate) { get "/render_action/basic/hello_world_with_custom_layout" } + + test "rendering with layout => false" do + get "/render_action/basic/hello_world_with_layout_false" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering with layout => :nil" do + get "/render_action/basic/hello_world_with_layout_nil" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering with layout => 'greetings'" do + assert_raise(ActionView::MissingTemplate) do + get "/render_action/basic/hello_world_with_custom_layout", {}, "action_dispatch.show_exceptions" => false + end end end - end module RenderActionWithApplicationLayout - # # ==== Render actions with layouts ==== - class BasicController < ::ApplicationController # Set the view path to an application view structure with layouts self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new( @@ -136,205 +124,197 @@ module RenderActionWithApplicationLayout "layouts/greetings.html.erb" => "Greetings <%= yield %> Bai", "layouts/builder.html.builder" => "xml.html do\n xml << yield\nend" )] - + def hello_world render :action => "hello_world" end - + def hello_world_with_layout render :action => "hello_world", :layout => true end - + def hello_world_with_layout_false render :action => "hello_world", :layout => false end - + def hello_world_with_layout_nil render :action => "hello_world", :layout => nil end - + def hello_world_with_custom_layout render :action => "hello_world", :layout => "greetings" end - + def with_builder_and_layout render :action => "hello", :layout => "builder" end end - - class TestDefaultLayout < SimpleRouteCase - describe %( - Render hello_world and implicitly use application.html.erb as a layout if - no layout is specified and no controller layout is present - ) - - get "/render_action_with_application_layout/basic/hello_world" - assert_body "OHAI Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutTrue < SimpleRouteCase - describe "rendering a normal template with full path with layout => true" - - get "/render_action_with_application_layout/basic/hello_world_with_layout" - assert_body "OHAI Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutFalse < SimpleRouteCase - describe "rendering a normal template with full path with layout => false" - - get "/render_action_with_application_layout/basic/hello_world_with_layout_false" - assert_body "Hello World!" - assert_status 200 - end - - class TestLayoutNil < SimpleRouteCase - describe "rendering a normal template with full path with layout => :nil" - - get "/render_action_with_application_layout/basic/hello_world_with_layout_nil" - assert_body "Hello World!" - assert_status 200 - end - - class TestCustomLayout < SimpleRouteCase - describe "rendering a normal template with full path with layout => 'greetings'" - - get "/render_action_with_application_layout/basic/hello_world_with_custom_layout" - assert_body "Greetings Hello World! Bai" - assert_status 200 + + class LayoutTest < SimpleRouteCase + describe "Only application.html.erb is present and <controller_path>.html.erb is missing" + + test "rendering implicit application.html.erb as layout" do + get "/render_action_with_application_layout/basic/hello_world" + + assert_body "OHAI Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => true" do + get "/render_action_with_application_layout/basic/hello_world_with_layout" + + assert_body "OHAI Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => false" do + get "/render_action_with_application_layout/basic/hello_world_with_layout_false" + + assert_body "Hello World!" + assert_status 200 + end + + test "rendering with layout => :nil" do + get "/render_action_with_application_layout/basic/hello_world_with_layout_nil" + + assert_body "Hello World!" + assert_status 200 + end + + test "rendering with layout => 'greetings'" do + get "/render_action_with_application_layout/basic/hello_world_with_custom_layout" + + assert_body "Greetings Hello World! Bai" + assert_status 200 + end end - + class TestLayout < SimpleRouteCase testing BasicController - + test "builder works with layouts" do get :with_builder_and_layout assert_response "<html>\n<p>Omg</p>\n</html>\n" end end - + end module RenderActionWithControllerLayout - class BasicController < ActionController::Base self.view_paths = self.view_paths = [ActionView::Template::FixturePath.new( "render_action_with_controller_layout/basic/hello_world.html.erb" => "Hello World!", "layouts/render_action_with_controller_layout/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI" )] - + def hello_world render :action => "hello_world" end - + def hello_world_with_layout render :action => "hello_world", :layout => true end - + def hello_world_with_layout_false render :action => "hello_world", :layout => false end - + def hello_world_with_layout_nil render :action => "hello_world", :layout => nil end - + def hello_world_with_custom_layout render :action => "hello_world", :layout => "greetings" end end - - class TestControllerLayout < SimpleRouteCase - describe "Render hello_world and implicitly use <controller_path>.html.erb as a layout." - get "/render_action_with_controller_layout/basic/hello_world" - assert_body "With Controller Layout! Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutTrue < SimpleRouteCase - describe "rendering a normal template with full path with layout => true" - - get "/render_action_with_controller_layout/basic/hello_world_with_layout" - assert_body "With Controller Layout! Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutFalse < SimpleRouteCase - describe "rendering a normal template with full path with layout => false" - - get "/render_action_with_controller_layout/basic/hello_world_with_layout_false" - assert_body "Hello World!" - assert_status 200 - end - - class TestLayoutNil < SimpleRouteCase - describe "rendering a normal template with full path with layout => :nil" - - get "/render_action_with_controller_layout/basic/hello_world_with_layout_nil" - assert_body "Hello World!" - assert_status 200 + class ControllerLayoutTest < SimpleRouteCase + 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 + get "/render_action_with_controller_layout/basic/hello_world" + + assert_body "With Controller Layout! Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => true" do + get "/render_action_with_controller_layout/basic/hello_world_with_layout" + + assert_body "With Controller Layout! Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => false" do + get "/render_action_with_controller_layout/basic/hello_world_with_layout_false" + + assert_body "Hello World!" + assert_status 200 + end + + test "rendering with layout => :nil" do + get "/render_action_with_controller_layout/basic/hello_world_with_layout_nil" + + assert_body "Hello World!" + assert_status 200 + end end - end module RenderActionWithBothLayouts - class BasicController < ActionController::Base self.view_paths = [ActionView::Template::FixturePath.new({ "render_action_with_both_layouts/basic/hello_world.html.erb" => "Hello World!", "layouts/application.html.erb" => "OHAI <%= yield %> KTHXBAI", "layouts/render_action_with_both_layouts/basic.html.erb" => "With Controller Layout! <%= yield %> KTHXBAI" })] - + def hello_world render :action => "hello_world" end - + def hello_world_with_layout render :action => "hello_world", :layout => true end - + def hello_world_with_layout_false render :action => "hello_world", :layout => false end - + def hello_world_with_layout_nil render :action => "hello_world", :layout => nil end end - - class TestControllerLayoutFirst < SimpleRouteCase - describe "Render hello_world and implicitly use <controller_path>.html.erb over application.html.erb as a layout" - get "/render_action_with_both_layouts/basic/hello_world" - assert_body "With Controller Layout! Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutTrue < SimpleRouteCase - describe "rendering a normal template with full path with layout => true" - - get "/render_action_with_both_layouts/basic/hello_world_with_layout" - assert_body "With Controller Layout! Hello World! KTHXBAI" - assert_status 200 - end - - class TestLayoutFalse < SimpleRouteCase - describe "rendering a normal template with full path with layout => false" - - get "/render_action_with_both_layouts/basic/hello_world_with_layout_false" - assert_body "Hello World!" - assert_status 200 - end - - class TestLayoutNil < SimpleRouteCase - describe "rendering a normal template with full path with layout => :nil" - - get "/render_action_with_both_layouts/basic/hello_world_with_layout_nil" - assert_body "Hello World!" - assert_status 200 + class ControllerLayoutTest < SimpleRouteCase + 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 + get "/render_action_with_both_layouts/basic/hello_world" + + assert_body "With Controller Layout! Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => true" do + get "/render_action_with_both_layouts/basic/hello_world_with_layout" + + assert_body "With Controller Layout! Hello World! KTHXBAI" + assert_status 200 + end + + test "rendering with layout => false" do + get "/render_action_with_both_layouts/basic/hello_world_with_layout_false" + + assert_body "Hello World!" + assert_status 200 + end + + test "rendering with layout => :nil" do + get "/render_action_with_both_layouts/basic/hello_world_with_layout_nil" + + assert_body "Hello World!" + assert_status 200 + end end - end
\ No newline at end of file diff --git a/actionpack/test/new_base/render_implicit_action_test.rb b/actionpack/test/new_base/render_implicit_action_test.rb index 58f5cec181..2846df48da 100644 --- a/actionpack/test/new_base/render_implicit_action_test.rb +++ b/actionpack/test/new_base/render_implicit_action_test.rb @@ -10,19 +10,19 @@ module RenderImplicitAction def hello_world() end end - class TestImplicitRender < SimpleRouteCase - describe "render a simple action with new explicit call to render" - - get "/render_implicit_action/simple/hello_world" - assert_body "Hello world!" - assert_status 200 - end - - class TestImplicitWithSpecialCharactersRender < SimpleRouteCase - describe "render an action with a missing method and has special characters" - - get "/render_implicit_action/simple/hyphen-ated" - assert_body "Hello hyphen-ated!" - assert_status 200 + class RenderImplicitActionTest < SimpleRouteCase + test "render a simple action with new explicit call to render" do + get "/render_implicit_action/simple/hello_world" + + assert_body "Hello world!" + assert_status 200 + end + + test "render an action with a missing method and has special characters" do + get "/render_implicit_action/simple/hyphen-ated" + + assert_body "Hello hyphen-ated!" + assert_status 200 + end end end
\ No newline at end of file diff --git a/actionpack/test/new_base/render_layout_test.rb b/actionpack/test/new_base/render_layout_test.rb index dc858b4f5c..f32c60d683 100644 --- a/actionpack/test/new_base/render_layout_test.rb +++ b/actionpack/test/new_base/render_layout_test.rb @@ -2,72 +2,100 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") module ControllerLayouts class ImplicitController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( "layouts/application.html.erb" => "OMG <%= yield %> KTHXBAI", "layouts/override.html.erb" => "Override! <%= yield %>", "basic.html.erb" => "Hello world!", "controller_layouts/implicit/layout_false.html.erb" => "hai(layout_false.html.erb)" )] - + def index render :template => "basic" end - + def override render :template => "basic", :layout => "override" end - + def layout_false render :layout => false end - + def builder_override - end end - - class TestImplicitLayout < SimpleRouteCase - describe "rendering a normal template, but using the implicit layout" - - get "/controller_layouts/implicit/index" - assert_body "OMG Hello world! KTHXBAI" - assert_status 200 - end - + class ImplicitNameController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( "layouts/controller_layouts/implicit_name.html.erb" => "OMGIMPLICIT <%= yield %> KTHXBAI", "basic.html.erb" => "Hello world!" )] - + def index render :template => "basic" end end - - class TestImplicitNamedLayout < SimpleRouteCase - describe "rendering a normal template, but using an implicit NAMED layout" - - get "/controller_layouts/implicit_name/index" - assert_body "OMGIMPLICIT Hello world! KTHXBAI" - assert_status 200 - end - - class TestOverridingImplicitLayout < SimpleRouteCase - describe "overriding an implicit layout with render :layout option" - - get "/controller_layouts/implicit/override" - assert_body "Override! Hello world!" + + class RenderLayoutTest < SimpleRouteCase + test "rendering a normal template, but using the implicit layout" do + get "/controller_layouts/implicit/index" + + assert_body "OMG Hello world! KTHXBAI" + assert_status 200 + end + + test "rendering a normal template, but using an implicit NAMED layout" do + get "/controller_layouts/implicit_name/index" + + assert_body "OMGIMPLICIT Hello world! KTHXBAI" + assert_status 200 + end + + test "overriding an implicit layout with render :layout option" do + get "/controller_layouts/implicit/override" + assert_body "Override! Hello world!" + end + end - - class TestLayoutOptions < SimpleRouteCase + + class LayoutOptionsTest < SimpleRouteCase testing ControllerLayouts::ImplicitController - + test "rendering with :layout => false leaves out the implicit layout" do get :layout_false assert_response "hai(layout_false.html.erb)" end end + + class MismatchFormatController < ::ApplicationController + self.view_paths = [ActionView::Template::FixturePath.new( + "layouts/application.html.erb" => "<html><%= yield %></html>", + "controller_layouts/mismatch_format/index.js.rjs" => "page[:test].omg", + "controller_layouts/mismatch_format/implicit.rjs" => "page[:test].omg" + )] + + def explicit + render :layout => "application" + end + end + + class MismatchFormatTest < SimpleRouteCase + testing ControllerLayouts::MismatchFormatController + + test "if JS is selected, an HTML template is not also selected" do + get :index + assert_response "$(\"test\").omg();" + end + + test "if JS is implicitly selected, an HTML template is not also selected" do + get :implicit + assert_response "$(\"test\").omg();" + end + + test "if an HTML template is explicitly provides for a JS template, an error is raised" do + assert_raises ActionView::MissingTemplate do + get :explicit, {}, "action_dispatch.show_exceptions" => false + end + end + end end
\ No newline at end of file diff --git a/actionpack/test/new_base/render_template_test.rb b/actionpack/test/new_base/render_template_test.rb index 6c50ae4203..face5b7571 100644 --- a/actionpack/test/new_base/render_template_test.rb +++ b/actionpack/test/new_base/render_template_test.rb @@ -79,7 +79,6 @@ module RenderTemplate end class WithLayoutController < ::ApplicationController - self.view_paths = [ActionView::Template::FixturePath.new( "test/basic.html.erb" => "Hello from basic.html.erb", "shared.html.erb" => "Elastica", @@ -108,46 +107,45 @@ module RenderTemplate end end - class TestTemplateRenderWithLayout < SimpleRouteCase - describe "rendering a normal template with full path with layout" - - get "/render_template/with_layout" - assert_body "Hello from basic.html.erb, I'm here!" - assert_status 200 - end - - class TestTemplateRenderWithLayoutTrue < SimpleRouteCase - describe "rendering a normal template with full path with layout => :true" - - get "/render_template/with_layout/with_layout" - assert_body "Hello from basic.html.erb, I'm here!" - assert_status 200 - end - - class TestTemplateRenderWithLayoutFalse < SimpleRouteCase - describe "rendering a normal template with full path with layout => :false" - - get "/render_template/with_layout/with_layout_false" - assert_body "Hello from basic.html.erb" - assert_status 200 - end - - class TestTemplateRenderWithLayoutNil < SimpleRouteCase - describe "rendering a normal template with full path with layout => :nil" - - get "/render_template/with_layout/with_layout_nil" - assert_body "Hello from basic.html.erb" - assert_status 200 - end - - class TestTemplateRenderWithCustomLayout < SimpleRouteCase - describe "rendering a normal template with full path with layout => 'greetings'" - - get "/render_template/with_layout/with_custom_layout" - assert_body "Hello from basic.html.erb, I wish thee well." - assert_status 200 + class TestWithLayout < SimpleRouteCase + describe "Rendering with :template using implicit or explicit layout" + + test "rendering with implicit layout" do + get "/render_template/with_layout" + + assert_body "Hello from basic.html.erb, I'm here!" + assert_status 200 + end + + test "rendering with layout => :true" do + get "/render_template/with_layout/with_layout" + + assert_body "Hello from basic.html.erb, I'm here!" + assert_status 200 + end + + test "rendering with layout => :false" do + get "/render_template/with_layout/with_layout_false" + + assert_body "Hello from basic.html.erb" + assert_status 200 + end + + test "rendering with layout => :nil" do + get "/render_template/with_layout/with_layout_nil" + + assert_body "Hello from basic.html.erb" + assert_status 200 + end + + test "rendering layout => 'greetings'" do + get "/render_template/with_layout/with_custom_layout" + + assert_body "Hello from basic.html.erb, I wish thee well." + assert_status 200 + end end - + module Compatibility class WithoutLayoutController < ActionController::Base self.view_paths = [ActionView::Template::FixturePath.new( @@ -161,11 +159,12 @@ module RenderTemplate end class TestTemplateRenderWithForwardSlash < SimpleRouteCase - describe "rendering a normal template with full path starting with a leading slash" + test "rendering a normal template with full path starting with a leading slash" do + get "/render_template/compatibility/without_layout/with_forward_slash" - get "/render_template/compatibility/without_layout/with_forward_slash" - assert_body "Hello from basic.html.erb" - assert_status 200 + assert_body "Hello from basic.html.erb" + assert_status 200 + end end end end
\ No newline at end of file diff --git a/actionpack/test/new_base/render_test.rb b/actionpack/test/new_base/render_test.rb index b1867fdcc2..ed3d50fa0b 100644 --- a/actionpack/test/new_base/render_test.rb +++ b/actionpack/test/new_base/render_test.rb @@ -8,63 +8,60 @@ module Render "render/blank_render/access_action_name.html.erb" => "Action Name: <%= action_name %>", "render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>" )] - + def index render end - + def access_request render :action => "access_request" end - + def render_action_name render :action => "access_action_name" end - - private - + + private + def secretz render :text => "FAIL WHALE!" end end - - class TestBlankRender < SimpleRouteCase - describe "Render with blank" - get "/render/blank_render" - assert_body "Hello world!" - assert_status 200 - end - class DoubleRenderController < ActionController::Base def index render :text => "hello" render :text => "world" end end - - class TestBasic < SimpleRouteCase - describe "Rendering more than once" - - test "raises an exception" do + + class RenderTest < SimpleRouteCase + test "render with blank" do + get "/render/blank_render" + + assert_body "Hello world!" + assert_status 200 + end + + test "rendering more than once raises an exception" do assert_raises(AbstractController::DoubleRenderError) do - get "/render/double_render" + get "/render/double_render", {}, "action_dispatch.show_exceptions" => false end end end - + class TestOnlyRenderPublicActions < SimpleRouteCase describe "Only public methods on actual controllers are callable actions" - + test "raises an exception when a method of Object is called" do assert_raises(AbstractController::ActionNotFound) do - get "/render/blank_render/clone" + get "/render/blank_render/clone", {}, "action_dispatch.show_exceptions" => false end end - + test "raises an exception when a private method is called" do assert_raises(AbstractController::ActionNotFound) do - get "/render/blank_render/secretz" + get "/render/blank_render/secretz", {}, "action_dispatch.show_exceptions" => false end end end @@ -74,12 +71,12 @@ module Render get "/render/blank_render/access_request" assert_body "The request: GET" end - + test "The action_name is accessible in the view" do get "/render/blank_render/render_action_name" assert_body "Action Name: render_action_name" end - + test "The controller_name is accessible in the view" do get "/render/blank_render/access_controller_name" assert_body "Controller Name: blank_render" diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/new_base/render_text_test.rb index 39f2f7abbf..ffc149283b 100644 --- a/actionpack/test/new_base/render_text_test.rb +++ b/actionpack/test/new_base/render_text_test.rb @@ -1,8 +1,5 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") -class ApplicationController < ActionController::Base -end - module RenderText class SimpleController < ActionController::Base self.view_paths = [ActionView::Template::FixturePath.new] @@ -11,15 +8,7 @@ module RenderText render :text => "hello david" end end - - class TestSimpleTextRenderWithNoLayout < SimpleRouteCase - describe "Rendering text from a action with default options renders the text with the layout" - - get "/render_text/simple" - assert_body "hello david" - assert_status 200 - end - + class WithLayoutController < ::ApplicationController self.view_paths = [ActionView::Template::FixturePath.new( "layouts/application.html.erb" => "<%= yield %>, I'm here!", @@ -73,77 +62,78 @@ module RenderText end end - class TestSimpleTextRenderWithLayout < SimpleRouteCase - describe "Rendering text from a action with default options renders the text without the layout" - - get "/render_text/with_layout" - assert_body "hello david" - assert_status 200 - end - - class TestTextRenderWithStatus < SimpleRouteCase - describe "Rendering text, while also providing a custom status code" - - get "/render_text/with_layout/custom_code" - assert_body "hello world" - assert_status 404 - end - - class TestTextRenderWithNil < SimpleRouteCase - describe "Rendering text with nil returns a single space character" - - get "/render_text/with_layout/with_nil" - assert_body " " - assert_status 200 - end - - class TestTextRenderWithNilAndStatus < SimpleRouteCase - describe "Rendering text with nil and custom status code returns a single space character with the status" - - get "/render_text/with_layout/with_nil_and_status" - assert_body " " - assert_status 403 - end - - class TestTextRenderWithFalse < SimpleRouteCase - describe "Rendering text with false returns the string 'false'" - - get "/render_text/with_layout/with_false" - assert_body "false" - assert_status 200 - end - - class TestTextRenderWithLayoutTrue < SimpleRouteCase - describe "Rendering text with :layout => true" - - get "/render_text/with_layout/with_layout_true" - assert_body "hello world, I'm here!" - assert_status 200 - end - - class TestTextRenderWithCustomLayout < SimpleRouteCase - describe "Rendering text with :layout => 'greetings'" - - get "/render_text/with_layout/with_custom_layout" - assert_body "hello world, I wish thee well." - assert_status 200 - end - - class TestTextRenderWithLayoutFalse < SimpleRouteCase - describe "Rendering text with :layout => false" - - get "/render_text/with_layout/with_layout_false" - assert_body "hello world" - assert_status 200 - end - - class TestTextRenderWithLayoutNil < SimpleRouteCase - describe "Rendering text with :layout => nil" - - get "/render_text/with_layout/with_layout_nil" - assert_body "hello world" - assert_status 200 + class RenderTextTest < SimpleRouteCase + describe "Rendering text using render :text" + + test "rendering text from a action with default options renders the text with the layout" do + get "/render_text/simple" + assert_body "hello david" + assert_status 200 + end + + test "rendering text from a action with default options renders the text without the layout" do + get "/render_text/with_layout" + + assert_body "hello david" + assert_status 200 + end + + test "rendering text, while also providing a custom status code" do + get "/render_text/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering text with nil returns an empty body padded for Safari" do + get "/render_text/with_layout/with_nil" + + assert_body " " + assert_status 200 + end + + test "Rendering text with nil and custom status code returns an empty body padded for Safari and the status" do + get "/render_text/with_layout/with_nil_and_status" + + assert_body " " + assert_status 403 + end + + test "rendering text with false returns the string 'false'" do + get "/render_text/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering text with :layout => true" do + get "/render_text/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering text with :layout => 'greetings'" do + get "/render_text/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "rendering text with :layout => false" do + get "/render_text/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering text with :layout => nil" do + get "/render_text/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end end end -ActionController::Base.app_loaded!
\ No newline at end of file +ActionController::Base.app_loaded! diff --git a/actionpack/test/new_base/test_helper.rb b/actionpack/test/new_base/test_helper.rb index ec7dbffaae..d92029df7f 100644 --- a/actionpack/test/new_base/test_helper.rb +++ b/actionpack/test/new_base/test_helper.rb @@ -20,8 +20,8 @@ require 'action_controller/abstract' require 'action_controller/new_base' require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late -require 'rubygems' -require 'rack/test' +require 'action_controller/testing/process' +require 'action_controller/testing/integration' module Rails def self.env @@ -32,19 +32,17 @@ module Rails end # Temporary base class -class Rack::TestCase < ActiveSupport::TestCase - include Rack::Test::Methods - +class Rack::TestCase < ActionController::IntegrationTest setup do ActionController::Base.session_options[:key] = "abc" ActionController::Base.session_options[:secret] = ("*" * 30) - + controllers = ActionController::Base.subclasses.map do |k| k.underscore.sub(/_controller$/, '') end - + ActionController::Routing.use_controllers!(controllers) - + # Move into a bootloader ActionController::Base.subclasses.each do |klass| klass = klass.constantize @@ -52,13 +50,13 @@ class Rack::TestCase < ActiveSupport::TestCase klass.class_eval do _write_layout_method end - end + end end - + def app @app ||= ActionController::Dispatcher.new end - + def self.testing(klass = nil) if klass @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '') @@ -66,41 +64,23 @@ class Rack::TestCase < ActiveSupport::TestCase @testing end end - - def self.get(url) - setup do |test| - test.get url - end - end - + def get(thing, *args) if thing.is_a?(Symbol) - super("#{self.class.testing}/#{thing}") + super("#{self.class.testing}/#{thing}", *args) else super end end - + def assert_body(body) - assert_equal body, Array.wrap(last_response.body).join - end - - def self.assert_body(body) - test "body is set to '#{body}'" do - assert_body body - end + assert_equal body, Array.wrap(response.body).join end - + def assert_status(code) - assert_equal code, last_response.status - end - - def self.assert_status(code) - test "status code is set to #{code}" do - assert_status code - end + assert_equal code, response.status end - + def assert_response(body, status = 200, headers = {}) assert_body body assert_status status @@ -108,27 +88,14 @@ class Rack::TestCase < ActiveSupport::TestCase assert_header header, value end end - + def assert_content_type(type) - assert_equal type, last_response.headers["Content-Type"] + assert_equal type, response.headers["Content-Type"] end - - def self.assert_content_type(type) - test "content type is set to #{type}" do - assert_content_type(type) - end - end - + def assert_header(name, value) - assert_equal value, last_response.headers[name] - end - - def self.assert_header(name, value) - test "'#{name}' header is set to #{value.inspect}" do - assert_header(name, value) - end + assert_equal value, response.headers[name] end - end class ::ApplicationController < ActionController::Base diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index f9bc92c7c9..8caabfc3e1 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -3,6 +3,8 @@ require 'abstract_unit' class JavaScriptHelperTest < ActionView::TestCase tests ActionView::Helpers::JavaScriptHelper + def _evaluate_assigns_and_ivars() end + attr_accessor :formats, :output_buffer def setup @@ -10,6 +12,8 @@ class JavaScriptHelperTest < ActionView::TestCase @template = self end + def _evaluate_assigns_and_ivars() end + def test_escape_javascript assert_equal '', escape_javascript(nil) assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos')) diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index 29cb60fd73..b6542ef29d 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -118,6 +118,10 @@ class NumberHelperTest < ActionView::TestCase assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2) assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) + assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 0) + assert_equal '500 MB', number_to_human_size(524288000, :precision=>0) + assert_equal '40 KB', number_to_human_size(41010, :precision => 0) + assert_equal '40 KB', number_to_human_size(41100, :precision => 0) end def test_number_to_human_size_with_custom_delimiter_and_separator diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 28851f113f..f9f418aec9 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -61,6 +61,8 @@ class PrototypeHelperBaseTest < ActionView::TestCase end class PrototypeHelperTest < PrototypeHelperBaseTest + def _evaluate_assigns_and_ivars() end + def setup @record = @author = Author.new @article = Article.new @@ -304,6 +306,8 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest @generator = create_generator end + def _evaluate_assigns_and_ivars() end + def test_insert_html_with_string assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });', @generator.insert_html(:top, 'element', '<p>This is a test</p>') diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index be7163888e..706b5085f4 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -49,6 +49,9 @@ class TextHelperTest < ActionView::TestCase assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]") assert_equal "Hello W...", truncate("Hello World!", :length => 10) assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10) + assert_equal "Hello[...]", truncate("Hello Big World!", :omission => "[...]", :length => 13, :separator => ' ') + assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 14, :separator => ' ') + assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 15, :separator => ' ') end if RUBY_VERSION < '1.9.0' @@ -401,6 +404,13 @@ class TextHelperTest < ActionView::TestCase auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.", :link => :all, :html => { :class => "menu", :target => "_blank" }) end + + def test_auto_link_with_multiple_trailing_punctuations + url = "http://youtube.com" + url_result = generate_result(url) + assert_equal url_result, auto_link(url) + assert_equal "(link: #{url_result}).", auto_link("(link: #{url}).") + end def test_cycle_class value = Cycle.new("one", 2, "3") diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d58b44144b..411b640c9e 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Implement #many? for NamedScope and AssociationCollection using #size. #1500 [Chris Kampmeier] + * Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] * Added ActiveRecord::Base#touch to update the updated_at/on attributes (or another specified timestamp) with the current time [DHH] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 41c0394763..eb80f5e8d2 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1668,7 +1668,7 @@ module ActiveRecord def tables_in_string(string) return [] if string.blank? - string.scan(/([\.a-zA-Z_]+).?\./).flatten + string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten end def tables_in_hash(hash) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 1e2313ab62..c3e5bd8575 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -302,6 +302,15 @@ module ActiveRecord end end + # Returns true if the collection has more than 1 record. Equivalent to collection.size > 1. + def many? + if block_given? + method_missing(:many?) { |*block_args| yield(*block_args) } + else + size > 1 + end + end + def uniq(collection = self) seen = Set.new collection.inject([]) do |kept, record| diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 2dca84b911..e8dbae9011 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -17,6 +17,13 @@ module ActiveRecord end end + def destroy(*records) + transaction do + delete_records(flatten_deeper(records)) + super + end + end + # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero, # and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length. diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index 8073ebaf9f..d93c8e7852 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -1,31 +1,31 @@ module ActiveRecord module Associations class HasOneThroughAssociation < HasManyThroughAssociation - + def create_through_record(new_value) #nodoc: klass = @reflection.through_reflection.klass current_object = @owner.send(@reflection.through_reflection.name) - + if current_object - current_object.update_attributes(construct_join_attributes(new_value)) + new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy else - @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) + @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value end end - + private def find(*args) super(args.merge(:limit => 1)) end - + def find_target super.first end def reset_target! @target = nil - end - end + end + end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9014bfdfa6..36a88494f2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1,6 +1,7 @@ require 'yaml' require 'set' require 'active_support/dependencies' +require 'active_support/time' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/class/inheritable_attributes' @@ -9,7 +10,7 @@ require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' -require 'active_support/core/time' +require 'active_support/core_ext/symbol' module ActiveRecord #:nodoc: # Generic Active Record exception class. @@ -1541,12 +1542,12 @@ module ActiveRecord #:nodoc: end def reverse_sql_order(order_query) - reversed_query = order_query.to_s.split(/,/).each { |s| + order_query.to_s.split(/,/).each { |s| if s.match(/\s(asc|ASC)$/) s.gsub!(/\s(asc|ASC)$/, ' DESC') elsif s.match(/\s(desc|DESC)$/) s.gsub!(/\s(desc|DESC)$/, ' ASC') - elsif !s.match(/\s(asc|ASC|desc|DESC)$/) + else s.concat(' DESC') end }.join(',') @@ -2176,7 +2177,7 @@ module ActiveRecord #:nodoc: # default_scope :order => 'last_name, first_name' # end def default_scope(options = {}) - self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} } + self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} } end # Test whether the given method and optional key are scoped. diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index cc3c4ece47..3eb547f713 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -1,6 +1,7 @@ require 'erb' require 'yaml' require 'csv' +require 'zlib' require 'active_support/dependencies' require 'active_support/test_case' require 'active_support/core_ext/logger' @@ -434,6 +435,7 @@ end # Any fixture labeled "DEFAULTS" is safely ignored. class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) + MAX_ID = 2 ** 31 - 1 DEFAULT_FILTER_RE = /\.ya?ml$/ @@all_cached_fixtures = {} @@ -525,11 +527,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) cached_fixtures(connection, table_names) end - # Returns a consistent identifier for +label+. This will always - # be a positive integer, and will always be the same for a given - # label, assuming the same OS, platform, and version of Ruby. + # Returns a consistent, platform-independent identifier for +label+. + # Identifiers are positive integers less than 2^32. def self.identify(label) - label.to_s.hash.abs + Zlib.crc32(label.to_s) % MAX_ID end attr_reader :table_name, :name diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 07f98dc743..e7151a3d47 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -109,7 +109,7 @@ module ActiveRecord class Scope attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined - NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set + NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? many? respond_to?).to_set [].methods.each do |m| unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s) delegate m, :to => :proxy_found @@ -168,6 +168,15 @@ module ActiveRecord end end + # Returns true if the named scope has more than 1 matching record. + def many? + if block_given? + proxy_found.many? { |*block_args| yield(*block_args) } + else + size > 1 + end + end + protected def proxy_found @found || load_found diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 21471da419..9dda3361d8 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -295,7 +295,7 @@ module ActiveRecord def set_session(env, sid, session_data) Base.silence do - record = env[SESSION_RECORD_KEY] ||= find_session(sid) + record = get_session_model(env, sid) record.data = session_data return false unless record.save @@ -309,6 +309,14 @@ module ActiveRecord return true end + + def get_session_model(env, sid) + if env[ENV_SESSION_OPTIONS_KEY][:id].nil? + env[SESSION_RECORD_KEY] = find_session(sid) + else + env[SESSION_RECORD_KEY] ||= find_session(sid) + end + end def find_session(id) @@session_class.find_by_session_id(id) || diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 236c9f36de..0532a06bf2 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/integer/even_odd' + module ActiveRecord # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. diff --git a/activerecord/test/cases/aaa_create_tables_test.rb b/activerecord/test/cases/aaa_create_tables_test.rb deleted file mode 100644 index 3911afac49..0000000000 --- a/activerecord/test/cases/aaa_create_tables_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -# The filename begins with "aaa" to ensure this is the first test. -require "cases/helper" - -class AAACreateTablesTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def test_load_schema - eval(File.read(SCHEMA_ROOT + "/schema.rb")) - if File.exists?(adapter_specific_schema_file) - eval(File.read(adapter_specific_schema_file)) - end - assert true - end - - def test_drop_and_create_courses_table - eval(File.read(SCHEMA_ROOT + "/schema2.rb")) - assert true - end - - private - def adapter_specific_schema_file - SCHEMA_ROOT + '/' + ActiveRecord::Base.connection.adapter_name.downcase + '_specific_schema.rb' - end -end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index d23f86b700..4cf49be668 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -589,6 +589,10 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1) end + def test_limited_eager_with_numeric_in_association + assert_equal people(:david, :susan), Person.find(:all, :include => [:readers, :primary_contact, :number1_fan], :conditions => "number1_fans_people.first_name like 'M%'", :order => 'people.id', :limit => 2, :offset => 0) + end + def test_preload_with_interpolation assert_equal [comments(:greetings)], Post.find(posts(:welcome).id, :include => :comments_with_interpolated_conditions).comments_with_interpolated_conditions end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 5df74fcdcd..d99424f9cd 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1023,6 +1023,45 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert firm.clients.loaded? end + def test_calling_many_should_count_instead_of_loading_association + firm = companies(:first_firm) + assert_queries(1) do + firm.clients.many? # use count query + end + assert !firm.clients.loaded? + end + + def test_calling_many_on_loaded_association_should_not_use_query + firm = companies(:first_firm) + firm.clients.collect # force load + assert_no_queries { assert firm.clients.many? } + end + + def test_calling_many_should_defer_to_collection_if_using_a_block + firm = companies(:first_firm) + assert_queries(1) do + firm.clients.expects(:size).never + firm.clients.many? { true } + end + assert firm.clients.loaded? + end + + def test_calling_many_should_return_false_if_none_or_one + firm = companies(:another_firm) + assert !firm.clients_like_ms.many? + assert_equal 0, firm.clients_like_ms.size + + firm = companies(:first_firm) + assert !firm.limited_clients.many? + assert_equal 1, firm.limited_clients.size + end + + def test_calling_many_should_return_true_if_more_than_one + firm = companies(:first_firm) + assert firm.clients.many? + assert_equal 2, firm.clients.size + end + def test_joins_with_namespaced_model_should_use_correct_type old = ActiveRecord::Base.store_full_sti_class ActiveRecord::Base.store_full_sti_class = true diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 97efca7891..4254badef2 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -13,7 +13,7 @@ require 'models/pet' require 'models/toy' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys + fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references def test_associate_existing assert_queries(2) { posts(:thinking);people(:david) } @@ -23,49 +23,49 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_queries(1) do posts(:thinking).people << people(:david) end - + assert_queries(1) do assert posts(:thinking).people.include?(people(:david)) end - + assert posts(:thinking).reload.people(true).include?(people(:david)) end def test_associating_new assert_queries(1) { posts(:thinking) } new_person = nil # so block binding catches it - + assert_queries(0) do new_person = Person.new :first_name => 'bob' end - + # Associating new records always saves them # Thus, 1 query for the new person record, 1 query for the new join table record assert_queries(2) do posts(:thinking).people << new_person end - + assert_queries(1) do assert posts(:thinking).people.include?(new_person) end - + assert posts(:thinking).reload.people(true).include?(new_person) end def test_associate_new_by_building assert_queries(1) { posts(:thinking) } - + assert_queries(0) do posts(:thinking).people.build(:first_name=>"Bob") posts(:thinking).people.new(:first_name=>"Ted") end - + # Should only need to load the association once assert_queries(1) do assert posts(:thinking).people.collect(&:first_name).include?("Bob") assert posts(:thinking).people.collect(&:first_name).include?("Ted") end - + # 2 queries for each new record (1 to save the record itself, 1 for the join model) # * 2 new records = 4 # + 1 query to save the actual post = 5 @@ -73,27 +73,27 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase posts(:thinking).body += '-changed' posts(:thinking).save end - + assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob") assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted") end def test_delete_association assert_queries(2){posts(:welcome);people(:michael); } - + assert_queries(1) do posts(:welcome).people.delete(people(:michael)) end - + assert_queries(1) do assert posts(:welcome).people.empty? end - + assert posts(:welcome).reload.people(true).empty? end def test_destroy_association - assert_difference "Person.count", -1 do + assert_difference ["Person.count", "Reader.count"], -1 do posts(:welcome).people.destroy(people(:michael)) end @@ -102,7 +102,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_destroy_all - assert_difference "Person.count", -1 do + assert_difference ["Person.count", "Reader.count"], -1 do posts(:welcome).people.destroy_all end @@ -110,38 +110,44 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert posts(:welcome).people(true).empty? end + def test_should_raise_exception_for_destroying_mismatching_records + assert_no_difference ["Person.count", "Reader.count"] do + assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) } + end + end + def test_replace_association assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)} - + # 1 query to delete the existing reader (michael) # 1 query to associate the new reader (david) assert_queries(2) do posts(:welcome).people = [people(:david)] end - + assert_queries(0){ assert posts(:welcome).people.include?(people(:david)) assert !posts(:welcome).people.include?(people(:michael)) } - + assert posts(:welcome).reload.people(true).include?(people(:david)) assert !posts(:welcome).reload.people(true).include?(people(:michael)) end def test_associate_with_create assert_queries(1) { posts(:thinking) } - + # 1 query for the new record, 1 for the join table record # No need to update the actual collection yet! assert_queries(2) do posts(:thinking).people.create(:first_name=>"Jeb") end - + # *Now* we actually need the collection so it's loaded assert_queries(1) do assert posts(:thinking).people.collect(&:first_name).include?("Jeb") end - + assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb") end @@ -159,15 +165,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_clear_associations assert_queries(2) { posts(:welcome);posts(:welcome).people(true) } - + assert_queries(1) do posts(:welcome).people.clear end - + assert_queries(0) do assert posts(:welcome).people.empty? end - + assert posts(:welcome).reload.people(true).empty? end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 12c598751b..ab6e6d20fc 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -43,7 +43,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase @member.reload end end - + + def test_set_record_to_nil_should_delete_association + @member.club = nil + @member.reload + assert_equal nil, @member.current_membership + assert_nil @member.club + end + def test_has_one_through_polymorphic assert_equal clubs(:moustache_club), @member.sponsor_club end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 919b6f857c..f95b2c0079 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -12,6 +12,7 @@ require 'models/reader' require 'models/ship' require 'models/ship_part' require 'models/treasure' +require 'models/company' class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase def test_autosave_should_be_a_valid_option_for_has_one @@ -38,6 +39,8 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase end class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase + fixtures :companies, :accounts + def test_should_save_parent_but_not_invalid_child firm = Firm.new(:name => 'GlobalMegaCorp') assert firm.valid? @@ -137,6 +140,8 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas end class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase + fixtures :companies + def test_should_save_parent_but_not_invalid_child client = Client.new(:name => 'Joe (the Plumber)') assert client.valid? @@ -920,4 +925,4 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T end include AutosaveAssociationOnACollectionAssociationTests -end
\ No newline at end of file +end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d0d7094e30..037b67ec84 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -7,10 +7,10 @@ require 'models/company' require 'models/topic' require 'models/reply' require 'models/entrant' +require 'models/project' require 'models/developer' require 'models/customer' require 'models/job' -require 'models/categorization' class DynamicFinderMatchTest < ActiveRecord::TestCase def test_find_no_match @@ -64,7 +64,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase end class FinderTest < ActiveRecord::TestCase - fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers + fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations def test_find assert_equal(topics(:first).title, Topic.find(1).title) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 252bf4ff61..b07d4f3521 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -518,6 +518,11 @@ class FoxyFixturesTest < ActiveRecord::TestCase assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo)) end + def test_identifies_consistently + assert_equal 1281023246, Fixtures.identify(:ruby) + assert_equal 2140105598, Fixtures.identify(:sapphire_2) + end + TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) def test_populates_timestamp_columns diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 05e92433cd..f82784836e 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -5,6 +5,7 @@ require 'config' require 'rubygems' require 'test/unit' +require 'stringio' gem 'mocha', '>= 0.9.5' require 'mocha' @@ -72,3 +73,20 @@ class ActiveSupport::TestCase Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block) end end + +# silence verbose schema loading +original_stdout = $stdout +$stdout = StringIO.new + +begin + adapter_name = ActiveRecord::Base.connection.adapter_name.downcase + adapter_specific_schema_file = SCHEMA_ROOT + "/#{adapter_name}_specific_schema.rb" + + load SCHEMA_ROOT + "/schema.rb" + + if File.exists?(adapter_specific_schema_file) + load adapter_specific_schema_file + end +ensure + $stdout = original_stdout +end diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index c479859ee0..d8246f49b8 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -591,6 +591,16 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end + def test_default_scope_with_conditions_string + assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort + assert_equal nil, DeveloperCalledDavid.create!.name + end + + def test_default_scope_with_conditions_hash + assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort + assert_equal 'Jamis', DeveloperCalledJamis.create!.name + end + def test_default_scoping_with_threads scope = [{ :create => {}, :find => { :order => 'salary DESC' } }] diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 7dcea6d42e..92fe48cb5a 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -235,6 +235,40 @@ class NamedScopeTest < ActiveRecord::TestCase assert_no_queries { assert topics.any? } end + def test_many_should_not_load_results + topics = Topic.base + assert_queries(2) do + topics.many? # use count query + topics.collect # force load + topics.many? # use loaded (no query) + end + end + + def test_many_should_call_proxy_found_if_using_a_block + topics = Topic.base + assert_queries(1) do + topics.expects(:size).never + topics.many? { true } + end + end + + def test_many_should_not_fire_query_if_named_scope_loaded + topics = Topic.base + topics.collect # force load + assert_no_queries { assert topics.many? } + end + + def test_many_should_return_false_if_none_or_one + topics = Topic.base.scoped(:conditions => {:id => 0}) + assert !topics.many? + topics = Topic.base.scoped(:conditions => {:id => 1}) + assert !topics.many? + end + + def test_many_should_return_true_if_more_than_one + assert Topic.base.many? + end + def test_should_build_with_proxy_options topic = Topic.approved.build({}) assert topic.approved @@ -339,7 +373,7 @@ class NamedScopeTest < ActiveRecord::TestCase end end -class DynamicScopeMatchTest < ActiveRecord::TestCase +class DynamicScopeMatchTest < ActiveRecord::TestCase def test_scoped_by_no_match assert_nil ActiveRecord::DynamicScopeMatch.match("not_scoped_at_all") end @@ -353,6 +387,8 @@ class DynamicScopeMatchTest < ActiveRecord::TestCase end class DynamicScopeTest < ActiveRecord::TestCase + fixtures :posts + def test_dynamic_scope assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1) assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"}) diff --git a/activerecord/test/fixtures/people.yml b/activerecord/test/fixtures/people.yml index 3babb1fe59..123673a2af 100644 --- a/activerecord/test/fixtures/people.yml +++ b/activerecord/test/fixtures/people.yml @@ -2,14 +2,17 @@ michael: id: 1 first_name: Michael primary_contact_id: 2 + number1_fan_id: 3 gender: M david: id: 2 first_name: David primary_contact_id: 3 + number1_fan_id: 1 gender: M susan: id: 3 first_name: Susan primary_contact_id: 2 - gender: F
\ No newline at end of file + number1_fan_id: 1 + gender: F diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 92039a4f54..058970336b 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -89,3 +89,13 @@ class DeveloperOrderedBySalary < ActiveRecord::Base end end end + +class DeveloperCalledDavid < ActiveRecord::Base + self.table_name = 'developers' + default_scope :conditions => "name = 'David'" +end + +class DeveloperCalledJamis < ActiveRecord::Base + self.table_name = 'developers' + default_scope :conditions => { :name => 'Jamis' } +end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index ec2f684a6e..57fa6418f1 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -10,6 +10,7 @@ class Person < ActiveRecord::Base belongs_to :primary_contact, :class_name => 'Person' has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id' + belongs_to :number1_fan, :class_name => 'Person' named_scope :males, :conditions => { :gender => 'M' } named_scope :females, :conditions => { :gender => 'F' } diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a776cd974b..6e8813d8ab 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -1,4 +1,3 @@ - ActiveRecord::Schema.define do def except(adapter_names_to_exclude) unless [adapter_names_to_exclude].flatten.include?(adapter_name) @@ -325,6 +324,7 @@ ActiveRecord::Schema.define do t.string :first_name, :null => false t.references :primary_contact t.string :gender, :limit => 1 + t.references :number1_fan t.integer :lock_version, :null => false, :default => 0 end @@ -500,3 +500,7 @@ ActiveRecord::Schema.define do execute "ALTER TABLE fk_test_has_fk ADD CONSTRAINT fk_name FOREIGN KEY (#{quote_column_name 'fk_id'}) REFERENCES #{quote_table_name 'fk_test_has_pk'} (#{quote_column_name 'id'})" end end + +Course.connection.create_table :courses, :force => true do |t| + t.column :name, :string, :null => false +end diff --git a/activerecord/test/schema/schema2.rb b/activerecord/test/schema/schema2.rb deleted file mode 100644 index 8527f7ba8f..0000000000 --- a/activerecord/test/schema/schema2.rb +++ /dev/null @@ -1,6 +0,0 @@ -ActiveRecord::Schema.define do - - Course.connection.create_table :courses, :force => true do |t| - t.column :name, :string, :null => false - end -end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 032f0fb9c1..27d26b2ba5 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp] + * Change spelling of Kyev timezone to Kyiv #2613 [Alexander Dymo] * Add ActiveSupport.parse_json_times to disable time parsing in JSON backends that don't support it or don't need it. [rick] diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index dab017770d..a20635ba62 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -22,35 +22,19 @@ #++ module ActiveSupport - def self.load_all! - [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + Core.load_all! + class << self + attr_accessor :load_all_hooks + def on_load_all(&hook) load_all_hooks << hook end + def load_all!; load_all_hooks.each { |hook| hook.call } end end + self.load_all_hooks = [] - autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' - autoload :Base64, 'active_support/base64' - autoload :BasicObject, 'active_support/basic_object' - autoload :BufferedLogger, 'active_support/buffered_logger' - autoload :Cache, 'active_support/cache' - autoload :Callbacks, 'active_support/callbacks' - autoload :NewCallbacks, 'active_support/new_callbacks' - autoload :ConcurrentHash, 'active_support/concurrent_hash' - autoload :DependencyModule, 'active_support/dependency_module' - autoload :Deprecation, 'active_support/deprecation' - autoload :Gzip, 'active_support/gzip' - autoload :Inflector, 'active_support/inflector' - autoload :Memoizable, 'active_support/memoizable' - autoload :MessageEncryptor, 'active_support/message_encryptor' - autoload :MessageVerifier, 'active_support/message_verifier' - autoload :Multibyte, 'active_support/multibyte' - autoload :OptionMerger, 'active_support/option_merger' - autoload :OrderedHash, 'active_support/ordered_hash' - autoload :OrderedOptions, 'active_support/ordered_options' - autoload :Rescuable, 'active_support/rescuable' - autoload :SecureRandom, 'active_support/secure_random' - autoload :StringInquirer, 'active_support/string_inquirer' - autoload :XmlMini, 'active_support/xml_mini' + on_load_all do + [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + end end +require 'active_support/autoload' require 'active_support/vendor' I18n.load_path << "#{File.dirname(__FILE__)}/active_support/locale/en.yml" diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb new file mode 100644 index 0000000000..f537818300 --- /dev/null +++ b/activesupport/lib/active_support/all.rb @@ -0,0 +1,3 @@ +require 'active_support' +require 'active_support/time' +require 'active_support/core_ext' diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb new file mode 100644 index 0000000000..ed229d1c5f --- /dev/null +++ b/activesupport/lib/active_support/autoload.rb @@ -0,0 +1,25 @@ +module ActiveSupport + autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' + autoload :Base64, 'active_support/base64' + autoload :BasicObject, 'active_support/basic_object' + autoload :BufferedLogger, 'active_support/buffered_logger' + autoload :Cache, 'active_support/cache' + autoload :Callbacks, 'active_support/callbacks' + autoload :NewCallbacks, 'active_support/new_callbacks' + autoload :ConcurrentHash, 'active_support/concurrent_hash' + autoload :DependencyModule, 'active_support/dependency_module' + autoload :Deprecation, 'active_support/deprecation' + autoload :Gzip, 'active_support/gzip' + autoload :Inflector, 'active_support/inflector' + autoload :Memoizable, 'active_support/memoizable' + autoload :MessageEncryptor, 'active_support/message_encryptor' + autoload :MessageVerifier, 'active_support/message_verifier' + autoload :Multibyte, 'active_support/multibyte' + autoload :OptionMerger, 'active_support/option_merger' + autoload :OrderedHash, 'active_support/ordered_hash' + autoload :OrderedOptions, 'active_support/ordered_options' + autoload :Rescuable, 'active_support/rescuable' + autoload :SecureRandom, 'active_support/secure_random' + autoload :StringInquirer, 'active_support/string_inquirer' + autoload :XmlMini, 'active_support/xml_mini' +end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index ab8ab4eb68..8e9ae71d84 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -23,7 +23,12 @@ module ActiveSupport DELETED = "DELETED\r\n" end - attr_reader :addresses + def self.build_mem_cache(*addresses) + addresses = addresses.flatten + options = addresses.extract_options! + addresses = ["localhost"] if addresses.empty? + MemCache.new(addresses, options) + end # Creates a new MemCacheStore object, with the given memcached server # addresses. Each address is either a host name, or a host-with-port string @@ -34,15 +39,20 @@ module ActiveSupport # If no addresses are specified, then MemCacheStore will connect to # localhost port 11211 (the default memcached port). def initialize(*addresses) - addresses = addresses.flatten - options = addresses.extract_options! - addresses = ["localhost"] if addresses.empty? - @addresses = addresses - @data = MemCache.new(addresses, options) + if addresses.first.respond_to?(:get) + @data = addresses.first + else + @data = self.class.build_mem_cache(*addresses) + end extend Strategy::LocalCache end + # Reads multiple keys from the cache. + def read_multi(*keys) + @data.get_multi keys + end + def read(key, options = nil) # :nodoc: super @data.get(key, raw?(options)) diff --git a/activesupport/lib/active_support/core.rb b/activesupport/lib/active_support/core.rb deleted file mode 100644 index ad8db94941..0000000000 --- a/activesupport/lib/active_support/core.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActiveSupport - module Core - def self.load_all! - [TimeWithZone] - end - end -end diff --git a/activesupport/lib/active_support/core/all.rb b/activesupport/lib/active_support/core/all.rb deleted file mode 100644 index 55e8b4cfac..0000000000 --- a/activesupport/lib/active_support/core/all.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/core_ext' -require 'active_support/core' -Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each do |path| - require "active_support/core/#{File.basename(path, '.rb')}" -end diff --git a/activesupport/lib/active_support/core/time.rb b/activesupport/lib/active_support/core/time.rb deleted file mode 100644 index 43e13b5e58..0000000000 --- a/activesupport/lib/active_support/core/time.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'active_support/core/time/autoload' -require 'active_support/core_ext/time' -require 'active_support/core_ext/date' -require 'active_support/core_ext/date_time' diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index fe1f79050c..2a34874d08 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,8 +1,7 @@ -require 'date' +require 'active_support/time' require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/hash/reverse_merge' -require 'active_support/core/time' class Hash # This module exists to decorate files deserialized using Hash.from_xml with diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb index fb1bcdb98f..80011dfbed 100644 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -1,80 +1,3 @@ -class Object - # Returns +value+ after yielding +value+ to the block. This simplifies the - # process of constructing an object, performing work on the object, and then - # returning the object from a method. It is a Ruby-ized realization of the K - # combinator, courtesy of Mikael Brockman. - # - # ==== Examples - # - # # Without returning - # def foo - # values = [] - # values << "bar" - # values << "baz" - # return values - # end - # - # foo # => ['bar', 'baz'] - # - # # returning with a local variable - # def foo - # returning values = [] do - # values << 'bar' - # values << 'baz' - # end - # end - # - # foo # => ['bar', 'baz'] - # - # # returning with a block argument - # def foo - # returning [] do |values| - # values << 'bar' - # values << 'baz' - # end - # end - # - # foo # => ['bar', 'baz'] - def returning(value) - yield(value) - value - end - - # Yields <code>x</code> to the block, and then returns <code>x</code>. - # The primary purpose of this method is to "tap into" a method chain, - # in order to perform operations on intermediate results within the chain. - # - # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. - # tap { |x| puts "array: #{x.inspect}" }. - # select { |x| x%2 == 0 }. - # tap { |x| puts "evens: #{x.inspect}" }. - # map { |x| x*x }. - # tap { |x| puts "squares: #{x.inspect}" } - def tap - yield self - self - end unless Object.respond_to?(:tap) - - # An elegant way to factor duplication out of options passed to a series of - # method calls. Each method called in the block, with the block variable as - # the receiver, will have its options merged with the default +options+ hash - # provided. Each method called on the block variable must take an options - # hash as its final argument. - # - # with_options :order => 'created_at', :class_name => 'Comment' do |post| - # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all - # post.has_many :unapproved_comments, :conditions => ['approved = ?', false] - # post.has_many :all_comments - # end - # - # Can also be used with an explicit receiver: - # - # map.with_options :controller => "people" do |people| - # people.connect "/people", :action => "index" - # people.connect "/people/:id", :action => "show" - # end - # - def with_options(options) - yield ActiveSupport::OptionMerger.new(self, options) - end -end +require 'active_support/core_ext/object/returning' +require 'active_support/core_ext/object/tap' +require 'active_support/core_ext/object/with_options' diff --git a/activesupport/lib/active_support/core_ext/object/returning.rb b/activesupport/lib/active_support/core_ext/object/returning.rb new file mode 100644 index 0000000000..0dc2e1266a --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/returning.rb @@ -0,0 +1,42 @@ +class Object + # Returns +value+ after yielding +value+ to the block. This simplifies the + # process of constructing an object, performing work on the object, and then + # returning the object from a method. It is a Ruby-ized realization of the K + # combinator, courtesy of Mikael Brockman. + # + # ==== Examples + # + # # Without returning + # def foo + # values = [] + # values << "bar" + # values << "baz" + # return values + # end + # + # foo # => ['bar', 'baz'] + # + # # returning with a local variable + # def foo + # returning values = [] do + # values << 'bar' + # values << 'baz' + # end + # end + # + # foo # => ['bar', 'baz'] + # + # # returning with a block argument + # def foo + # returning [] do |values| + # values << 'bar' + # values << 'baz' + # end + # end + # + # foo # => ['bar', 'baz'] + def returning(value) + yield(value) + value + end +end diff --git a/activesupport/lib/active_support/core_ext/object/tap.rb b/activesupport/lib/active_support/core_ext/object/tap.rb new file mode 100644 index 0000000000..db7e715e2d --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/tap.rb @@ -0,0 +1,16 @@ +class Object + # Yields <code>x</code> to the block, and then returns <code>x</code>. + # The primary purpose of this method is to "tap into" a method chain, + # in order to perform operations on intermediate results within the chain. + # + # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. + # tap { |x| puts "array: #{x.inspect}" }. + # select { |x| x%2 == 0 }. + # tap { |x| puts "evens: #{x.inspect}" }. + # map { |x| x*x }. + # tap { |x| puts "squares: #{x.inspect}" } + def tap + yield self + self + end unless Object.respond_to?(:tap) +end diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb new file mode 100644 index 0000000000..dd38b7d261 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -0,0 +1,24 @@ +class Object + # An elegant way to factor duplication out of options passed to a series of + # method calls. Each method called in the block, with the block variable as + # the receiver, will have its options merged with the default +options+ hash + # provided. Each method called on the block variable must take an options + # hash as its final argument. + # + # with_options :order => 'created_at', :class_name => 'Comment' do |post| + # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all + # post.has_many :unapproved_comments, :conditions => ['approved = ?', false] + # post.has_many :all_comments + # end + # + # Can also be used with an explicit receiver: + # + # map.with_options :controller => "people" do |people| + # people.connect "/people", :action => "index" + # people.connect "/people/:id", :action => "show" + # end + # + def with_options(options) + yield ActiveSupport::OptionMerger.new(self, options) + end +end diff --git a/activesupport/lib/active_support/core_ext/symbol.rb b/activesupport/lib/active_support/core_ext/symbol.rb index 520369452b..c103cd9dcf 100644 --- a/activesupport/lib/active_support/core_ext/symbol.rb +++ b/activesupport/lib/active_support/core_ext/symbol.rb @@ -1,14 +1 @@ -class Symbol - # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: - # - # # The same as people.collect { |p| p.name } - # people.collect(&:name) - # - # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } - # people.select(&:manager?).collect(&:salary) - # - # This is a builtin method in Ruby 1.8.7 and later. - def to_proc - Proc.new { |*args| args.shift.__send__(self, *args) } - end unless :to_proc.respond_to?(:to_proc) -end +require 'active_support/core_ext/symbol/to_proc' diff --git a/activesupport/lib/active_support/core_ext/symbol/to_proc.rb b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb new file mode 100644 index 0000000000..520369452b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb @@ -0,0 +1,14 @@ +class Symbol + # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: + # + # # The same as people.collect { |p| p.name } + # people.collect(&:name) + # + # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } + # people.select(&:manager?).collect(&:salary) + # + # This is a builtin method in Ruby 1.8.7 and later. + def to_proc + Proc.new { |*args| args.shift.__send__(self, *args) } + end unless :to_proc.respond_to?(:to_proc) +end diff --git a/activesupport/lib/active_support/dependency_module.rb b/activesupport/lib/active_support/dependency_module.rb index 8c202acc8f..9872b9654b 100644 --- a/activesupport/lib/active_support/dependency_module.rb +++ b/activesupport/lib/active_support/dependency_module.rb @@ -16,10 +16,12 @@ module ActiveSupport end end - def depends_on(mod) - return if self < mod - @_dependencies ||= [] - @_dependencies << mod + def depends_on(*mods) + mods.each do |mod| + next if self < mod + @_dependencies ||= [] + @_dependencies << mod + end end end end diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 5072992cdf..6d845182fb 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -40,9 +40,15 @@ module ActiveSupport end class << self - attr_reader :backend delegate :decode, :to => :backend - + + def backend + unless defined? @backend + self.backend = defined?(::JSON) ? "JSONGem" : "Yaml" + end + @backend + end + def backend=(name) if name.is_a?(Module) @backend = name @@ -77,6 +83,5 @@ module ActiveSupport end ActiveSupport.escape_html_entities_in_json = true -ActiveSupport::JSON.backend = 'Yaml' require 'active_support/json/encoding' diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb index de847e30a3..649e6301d1 100644 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -1,13 +1,18 @@ +require 'json' unless defined?(JSON) + module ActiveSupport module JSON - ParseError = ::JSON::ParserError + ParseError = ::JSON::ParserError unless const_defined?(:ParseError) module Backends module JSONGem extend self - # Converts a JSON string into a Ruby object. + # Parses a JSON string or IO and convert it into an object def decode(json) + if json.respond_to?(:read) + json = json.read + end data = ::JSON.parse(json) if ActiveSupport.parse_json_times convert_dates_from(data) diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index c7db508c23..667016f45d 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -2,15 +2,20 @@ require 'active_support/core_ext/string/starts_ends_with' module ActiveSupport module JSON - class ParseError < StandardError + unless const_defined?(:ParseError) + class ParseError < StandardError + end end module Backends module Yaml extend self - # Converts a JSON string into a Ruby object. + # Parses a JSON string or IO and converts it into an object def decode(json) + if json.respond_to?(:read) + json = json.read + end YAML.load(convert_json_to_yaml(json)) rescue ArgumentError => e raise ParseError, "Invalid JSON string" diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 60f082bcc1..96ed35f0e0 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +require 'active_support/core_ext/string/behavior' module ActiveSupport #:nodoc: module Multibyte #:nodoc: diff --git a/activesupport/lib/active_support/new_callbacks.rb b/activesupport/lib/active_support/new_callbacks.rb index 9316d6d2b6..f8108780f1 100644 --- a/activesupport/lib/active_support/new_callbacks.rb +++ b/activesupport/lib/active_support/new_callbacks.rb @@ -296,9 +296,13 @@ module ActiveSupport method_name else kind, name = @kind, @name - @klass.send(:define_method, method_name) do - filter.send("#{kind}_#{name}", self) - end + @klass.send(:define_method, "#{method_name}_object") { filter } + + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{method_name}(&blk) + #{method_name}_object.send("#{kind}_#{name}", self, &blk) + end + RUBY_EVAL method_name end end diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb new file mode 100644 index 0000000000..37c57c485a --- /dev/null +++ b/activesupport/lib/active_support/ruby/shim.rb @@ -0,0 +1,24 @@ +# Backported Ruby builtins so you can code with the latest & greatest +# but still run on any Ruby 1.8.x. +# +# Date next_year, next_month +# DateTime to_date, to_datetime, xmlschema +# Enumerable group_by, each_with_object, none? +# Integer even?, odd? +# Object tap +# Process Process.daemon +# REXML security fix +# String ord +# Symbol to_proc +# Time to_date, to_time, to_datetime +require 'active_support' +require 'active_support/core_ext/date/calculations' +require 'active_support/core_ext/date_time/conversions' +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/integer/even_odd' +require 'active_support/core_ext/object/tap' +require 'active_support/core_ext/process/daemon' +require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/rexml' +require 'active_support/core_ext/symbol/to_proc' +require 'active_support/core_ext/time/conversions' diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 50e25ef740..bab2a401eb 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -32,6 +32,11 @@ module ActiveSupport include ActiveSupport::Testing::Default end + $tags = {} + def self.for_tag(tag) + yield if $tags[tag] + end + include ActiveSupport::Testing::SetupAndTeardown include ActiveSupport::Testing::Assertions include ActiveSupport::Testing::Deprecation diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb index d945c7e476..21134ff9e2 100644 --- a/activesupport/lib/active_support/testing/pending.rb +++ b/activesupport/lib/active_support/testing/pending.rb @@ -11,6 +11,11 @@ module ActiveSupport @@at_exit = false def pending(description = "", &block) + if description.is_a?(Symbol) + is_pending = $tags[description] + return block.call unless is_pending + end + if block_given? failed = false diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb new file mode 100644 index 0000000000..d36a683601 --- /dev/null +++ b/activesupport/lib/active_support/time.rb @@ -0,0 +1,14 @@ +require 'active_support' +require 'active_support/core_ext/time' +require 'active_support/core_ext/date' +require 'active_support/core_ext/date_time' + +module ActiveSupport + autoload :Duration, 'active_support/duration' + autoload :TimeWithZone, 'active_support/time_with_zone' + autoload :TimeZone, 'active_support/values/time_zone' + + on_load_all do + [Duration, TimeWithZone, TimeZone] + end +end diff --git a/activesupport/lib/active_support/core/time/autoload.rb b/activesupport/lib/active_support/time/autoload.rb index c9a7731b39..c9a7731b39 100644 --- a/activesupport/lib/active_support/core/time/autoload.rb +++ b/activesupport/lib/active_support/time/autoload.rb diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 208e2a8fb6..5c3d93c4a1 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -24,15 +24,19 @@ module ActiveSupport node_type_map = {} NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type } - # Parse an XML Document string into a simple hash using Java's jdom. - # string:: - # XML Document string to parse - def parse(string) - if string.blank? + # Parse an XML Document string or IO into a simple hash using Java's jdom. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + + if data.blank? {} else @dbf = DocumentBuilderFactory.new_instance - xml_string_reader = StringReader.new(string) + xml_string_reader = StringReader.new(data) xml_input_source = InputSource.new(xml_string_reader) doc = @dbf.new_document_builder.parse(xml_input_source) merge_element!({}, doc.document_element) diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 370205409a..d4c4dc7be5 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -5,16 +5,20 @@ module ActiveSupport module XmlMini_LibXML #:nodoc: extend self - # Parse an XML Document string into a simple hash using libxml. - # string:: - # XML Document string to parse - def parse(string) + # Parse an XML Document string or IO into a simple hash using libxml. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + LibXML::XML.default_keep_blanks = false - if string.blank? + if data.blank? {} else - LibXML::XML::Parser.string(string.strip).parse.to_hash + LibXML::XML::Parser.string(data.strip).parse.to_hash end end diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index c71c6badab..6c64612aee 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -5,14 +5,18 @@ module ActiveSupport module XmlMini_Nokogiri #:nodoc: extend self - # Parse an XML Document string into a simple hash using libxml / nokogiri. - # string:: - # XML Document string to parse - def parse(string) - if string.blank? + # Parse an XML Document string or IO into a simple hash using libxml / nokogiri. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + + if data.blank? {} else - doc = Nokogiri::XML(string) + doc = Nokogiri::XML(data) raise doc.errors.first if doc.errors.length > 0 doc.to_hash end diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 9b3243c0e4..08f797749f 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -7,16 +7,20 @@ module ActiveSupport CONTENT_KEY = '__content__'.freeze - # Parse an XML Document string into a simple hash + # Parse an XML Document string or IO into a simple hash # # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, # and uses the defaults from ActiveSupport # - # string:: - # XML Document string to parse - def parse(string) + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + require 'rexml/document' unless defined?(REXML::Document) - doc = REXML::Document.new(string) + doc = REXML::Document.new(data) merge_element!({}, doc.root) end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 8bb0c155cf..bd237a5c8e 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -15,22 +15,34 @@ class CacheStoreSettingTest < ActiveSupport::TestCase end def test_mem_cache_fragment_cache_store + MemCache.expects(:new).with(%w[localhost], {}) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost" assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost), store.addresses + end + + def test_mem_cache_fragment_cache_store_with_given_mem_cache + mem_cache = MemCache.new + MemCache.expects(:new).never + store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + end + + def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object + MemCache.expects(:new).never + store = ActiveSupport::Cache.lookup_store :mem_cache_store, stub("memcache", :get => true) + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) end def test_mem_cache_fragment_cache_store_with_multiple_servers + MemCache.expects(:new).with(%w[localhost 192.168.1.1], {}) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost 192.168.1.1), store.addresses end def test_mem_cache_fragment_cache_store_with_options + MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :namespace => "foo" }) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost 192.168.1.1), store.addresses - assert_equal 'foo', store.instance_variable_get('@data').instance_variable_get('@namespace') end def test_object_assigned_fragment_cache_store @@ -251,6 +263,15 @@ uses_memcached 'memcached backed store' do end end + def test_multi_get + @cache.with_local_cache do + @cache.write('foo', 1) + @cache.write('goo', 2) + result = @cache.read_multi('foo', 'goo') + assert_equal({'foo' => 1, 'goo' => 2}, result) + end + end + def test_middleware app = lambda { |env| result = @cache.write('foo', 'bar') diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 30d4152729..6f16621ae5 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'active_support/core/time' +require 'active_support/time' class DurationTest < ActiveSupport::TestCase def test_inspect diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 74b086fa9c..992ec60302 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' +require 'active_support/time' require 'active_support/core_ext/numeric' require 'active_support/core_ext/integer' -require 'active_support/core/time' class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index 8869b053e6..f0121b862d 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' +require 'active_support/time' require 'active_support/core_ext/object' require 'active_support/core_ext/class/removal' -require 'active_support/core/time' class ClassA; end class ClassB < ClassA; end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index e265423f06..1c2d0fbce4 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'active_support/core/time' +require 'active_support/time' class TimeExtCalculationsTest < Test::Unit::TestCase def test_seconds_since_midnight diff --git a/activesupport/test/dependency_module_test.rb b/activesupport/test/dependency_module_test.rb index 07090d15a1..be7db0fa7b 100644 --- a/activesupport/test/dependency_module_test.rb +++ b/activesupport/test/dependency_module_test.rb @@ -42,6 +42,12 @@ class DependencyModuleTest < Test::Unit::TestCase end end + module Foo + extend ActiveSupport::DependencyModule + + depends_on Bar, Baz + end + def setup @klass = Class.new end @@ -74,4 +80,9 @@ class DependencyModuleTest < Test::Unit::TestCase assert_equal "baz", @klass.baz assert_equal [DependencyModuleTest::Bar, DependencyModuleTest::Baz], @klass.included_modules[0..1] end + + def test_depends_on_with_multiple_modules + @klass.send(:include, Foo) + assert_equal [DependencyModuleTest::Foo, DependencyModuleTest::Bar, DependencyModuleTest::Baz], @klass.included_modules[0..2] + end end diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index 2a08abfb3e..9868f1e87d 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'active_support/core/time' +require 'active_support/time' require 'active_support/core_ext/array/conversions' class I18nTest < Test::Unit::TestCase diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 7e1bfcca84..09fd0d09ba 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -36,6 +36,9 @@ class TestJSONDecoding < ActiveSupport::TestCase %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]} } + # load the default JSON backend + ActiveSupport::JSON.backend + backends = %w(Yaml) begin gem 'json', '>= 1.1' diff --git a/activesupport/test/new_callbacks_test.rb b/activesupport/test/new_callbacks_test.rb index dec6106ac1..7bec47224d 100644 --- a/activesupport/test/new_callbacks_test.rb +++ b/activesupport/test/new_callbacks_test.rb @@ -396,6 +396,68 @@ module NewCallbacksTest end end + class CallbackObject + def before_save(caller) + caller.record << "before" + end + + def around_save(caller) + caller.record << "around before" + yield + caller.record << "around after" + end + end + + class UsingObjectBefore + include ActiveSupport::NewCallbacks + + define_callbacks :save + save_callback :before, CallbackObject.new + + attr_accessor :record + def initialize + @record = [] + end + + def save + _run_save_callbacks do + @record << "yielded" + end + end + end + + class UsingObjectAround + include ActiveSupport::NewCallbacks + + define_callbacks :save + save_callback :around, CallbackObject.new + + attr_accessor :record + def initialize + @record = [] + end + + def save + _run_save_callbacks do + @record << "yielded" + 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 + class CallbackTerminatorTest < Test::Unit::TestCase def test_termination terminator = CallbackTerminator.new diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 87d6ccc30d..99c4310854 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'active_support/core/time' +require 'active_support/time' class TimeZoneTest < Test::Unit::TestCase def test_utc_to_local diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index 886a9d1aba..7c3a591e63 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -148,6 +148,19 @@ class NokogiriEngineTest < Test::Unit::TestCase eoxml end + def test_parse_from_io + io = StringIO.new(<<-eoxml) + <root> + good + <products> + hello everyone + </products> + morning + </root> + eoxml + XmlMini.parse(io) + end + private def assert_equal_rexml(xml) hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb index a412d8ca05..57bb35254a 100644 --- a/activesupport/test/xml_mini/rexml_engine_test.rb +++ b/activesupport/test/xml_mini/rexml_engine_test.rb @@ -12,4 +12,18 @@ class REXMLEngineTest < Test::Unit::TestCase XmlMini.backend = 'REXML' assert_equal XmlMini_REXML, XmlMini.backend end + + def test_parse_from_io + XmlMini.backend = 'REXML' + io = StringIO.new(<<-eoxml) + <root> + good + <products> + hello everyone + </products> + morning + </root> + eoxml + XmlMini.parse(io) + end end diff --git a/ci/ci_setup_notes.txt b/ci/ci_setup_notes.txt index 63f1851b19..780277c939 100644 --- a/ci/ci_setup_notes.txt +++ b/ci/ci_setup_notes.txt @@ -47,7 +47,7 @@ $ sudo aptitude update * If you did not configure no-root-gem installation via ~/.gemrc as shown above, then allow no-password sudo for gem installation: $ sudo visudo # add this line to bottom: -ci ALL=NOPASSWD: /usr/local/bin/geminstaller, /usr/local/bin/ruby, /usr/local/bin/gem +ci ALL=(ALL) NOPASSWD: ALL * Start ccrb via init script and check for default homepage at port 3333 diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb index 7985e3c8df..2247a1b6ea 100644 --- a/ci/cruise_config.rb +++ b/ci/cruise_config.rb @@ -1,5 +1,5 @@ Project.configure do |project| - project.build_command = 'ruby ci/ci_build.rb' + 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' diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 387e370a6f..f1ef9b59a0 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -21,3 +21,5 @@ gems: version: >= 2.2.3 - name: sqlite3-ruby version: >= 1.2.2 +- name: rubygems-update + version: >= 1.3.3 diff --git a/railties/builtin/rails_info/rails/info.rb b/railties/builtin/rails_info/rails/info.rb index 0dd1c090c1..fdacc469f5 100644 --- a/railties/builtin/rails_info/rails/info.rb +++ b/railties/builtin/rails_info/rails/info.rb @@ -1,9 +1,13 @@ +require "active_support/core_ext/object/misc" +require "cgi" +require "active_support/core_ext/cgi" + module Rails module Info mattr_accessor :properties class << (@@properties = []) def names - map &:first + map {|val| val.first } end def value_for(property_name) @@ -53,7 +57,7 @@ module Rails alias inspect to_s def to_html - returning table = '<table>' do + (table = '<table>').tap do properties.each do |(name, value)| table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>) formatted_value = if value.kind_of?(Array) @@ -108,7 +112,7 @@ module Rails end property 'Middleware' do - ActionController::Dispatcher.middleware.active.map(&:inspect) + ActionController::Dispatcher.middleware.active.map {|middle| middle.inspect } end # The Rails Git revision, if it's checked out into vendor/rails. diff --git a/railties/configs/routes.rb b/railties/configs/routes.rb index 4f3d9d22dd..ea14ce1bfc 100644 --- a/railties/configs/routes.rb +++ b/railties/configs/routes.rb @@ -37,7 +37,7 @@ ActionController::Routing::Routes.draw do |map| # Install the default routes as the lowest priority. # Note: These default routes make all actions in every controller accessible via GET requests. You should - # consider removing the them or commenting them out if you're using named routes and resources. + # consider removing or commenting them out if you're using named routes and resources. map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end diff --git a/railties/lib/commands/performance/profiler.rb b/railties/lib/commands/performance/profiler.rb index fd111bae87..7df840f197 100644 --- a/railties/lib/commands/performance/profiler.rb +++ b/railties/lib/commands/performance/profiler.rb @@ -29,7 +29,7 @@ begin printer_class = RubyProf::FlatPrinter end printer = printer_class.new(results) - printer.print($stderr, 0) + printer.print($stderr) rescue LoadError require "prof" $stderr.puts 'Using the old ruby-prof extension.' diff --git a/railties/lib/console_app.rb b/railties/lib/console_app.rb index c944d49205..75e6f11ea3 100644 --- a/railties/lib/console_app.rb +++ b/railties/lib/console_app.rb @@ -1,5 +1,4 @@ -require 'active_support' -require 'active_support/core/all' +require 'active_support/all' require 'active_support/test_case' require 'action_controller' @@ -26,8 +25,7 @@ end #reloads the environment def reload! puts "Reloading..." - dispatcher = ActionController::Dispatcher.new - dispatcher.cleanup_application - dispatcher.reload_application + ActionController::Dispatcher.new + ActionController::Dispatcher.router.reload true end diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 40000f0dfd..3c0d5940ea 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -263,9 +263,8 @@ module Rails # list. By default, all frameworks (Active Record, Active Support, # Action Pack, Action Mailer, and Active Resource) are loaded. def require_frameworks - require 'active_support' + require 'active_support/all' configuration.frameworks.each { |framework| require(framework.to_s) } - require 'active_support/core/all' rescue LoadError => e # Re-raise as RuntimeError because Mongrel would swallow LoadError. raise e.to_s @@ -311,7 +310,7 @@ module Rails end def check_for_unbuilt_gems - unbuilt_gems = @configuration.gems.select(&:frozen?).reject(&:built?) + unbuilt_gems = @configuration.gems.select {|gem| gem.frozen? && !gem.built? } if unbuilt_gems.size > 0 # don't print if the gems:build rake tasks are being run unless $gems_build_rake_task @@ -462,7 +461,7 @@ Run `rake gems:install` to install the missing gems. if RAILS_CACHE.respond_to?(:middleware) # Insert middleware to setup and teardown local cache for each request - configuration.middleware.insert_after(:"ActionDispatch::Failsafe", RAILS_CACHE.middleware) + configuration.middleware.insert_after(:"Rack::Lock", RAILS_CACHE.middleware) end end end @@ -632,7 +631,6 @@ Run `rake gems:install` to install the missing gems. return unless configuration.frameworks.include?(:action_controller) require 'dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) - Dispatcher.run_prepare_callbacks end def disable_dependency_loading diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index e66166306a..49ec5c7fba 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/kernel/reporting' - module Rails # The Plugin class should be an object which provides the following methods: # @@ -141,6 +139,7 @@ module Rails def evaluate_init_rb(initializer) if has_init_file? + require 'active_support/core_ext/kernel/reporting' silence_warnings do # Allow plugins to reference the current configuration object config = initializer.configuration diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb index 66e01d70da..bc22dfc591 100644 --- a/railties/lib/rails/plugin/loader.rb +++ b/railties/lib/rails/plugin/loader.rb @@ -24,7 +24,7 @@ module Rails # Returns the plugins that are in engine-form (have an app/ directory) def engines - @engines ||= plugins.select(&:engine?) + @engines ||= plugins.select {|plugin| plugin.engine? } end # Returns all the plugins that could be found by the current locators. @@ -66,7 +66,7 @@ module Rails end def engine_metal_paths - engines.collect(&:metal_path) + engines.collect {|engine| engine.metal_path } end protected @@ -79,18 +79,18 @@ module Rails end def add_engine_routing_configurations - engines.select(&:routed?).collect(&:routing_file).each do |routing_file| + engines.select {|engine| engine.routed? }.map {|engine| engine.routing_file }.each do |routing_file| ActionController::Routing::Routes.add_configuration_file(routing_file) end end def add_engine_controller_paths - ActionController::Routing.controller_paths += engines.collect(&:controller_path) + ActionController::Routing.controller_paths += engines.collect {|engine| engine.controller_path } end def add_engine_view_paths # reverse it such that the last engine can overwrite view paths from the first, like with routes - paths = ActionView::PathSet.new(engines.collect(&:view_path).reverse) + paths = ActionView::PathSet.new(engines.collect {|engine| engine.view_path }.reverse) ActionController::Base.view_paths.concat(paths) ActionMailer::Base.view_paths.concat(paths) if configuration.frameworks.include?(:action_mailer) end @@ -170,7 +170,7 @@ module Rails # so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is # non empty, we load the named plugins in the order specified. def registered_plugin_names - configuration.plugins ? configuration.plugins.map(&:to_s) : nil + configuration.plugins ? configuration.plugins.map {|plugin| plugin.to_s } : nil end def loaded?(plugin_name) diff --git a/railties/lib/rails/plugin/locator.rb b/railties/lib/rails/plugin/locator.rb index a6fc388a8e..1057c004e0 100644 --- a/railties/lib/rails/plugin/locator.rb +++ b/railties/lib/rails/plugin/locator.rb @@ -25,7 +25,7 @@ module Rails end def plugin_names - plugins.map(&:name) + plugins.map {|plugin| plugin.name } end end diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb index 201a9e0f91..85400932dd 100644 --- a/railties/lib/rails_generator.rb +++ b/railties/lib/rails_generator.rb @@ -21,16 +21,9 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -begin - require 'active_support' -rescue LoadError - activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" - if File.directory?(activesupport_path) - $:.unshift activesupport_path - require 'active_support' - end -end -require 'active_support/core/all' +activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" +$:.unshift(activesupport_path) if File.directory?(activesupport_path) +require 'active_support/all' $:.unshift(File.dirname(__FILE__)) require 'rails_generator/base' diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb index 3b49b1fa92..0e24d11950 100644 --- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb +++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -227,7 +227,7 @@ module Rails # def generate(what, *args) log 'generating', what - argument = args.map(&:to_s).flatten.join(" ") + argument = args.map {|arg| arg.to_s }.flatten.join(" ") in_root { run_ruby_script("script/generate #{what} #{argument}", false) } end diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/rails_generator/generators/components/model/templates/model.rb index 6fcf393bdf..0656b06dfe 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/model.rb +++ b/railties/lib/rails_generator/generators/components/model/templates/model.rb @@ -1,5 +1,5 @@ class <%= class_name %> < ActiveRecord::Base -<% attributes.select(&:reference?).each do |attribute| -%> +<% attributes.select {|attr| attr.reference? }.each do |attribute| -%> belongs_to :<%= attribute.name %> <% end -%> end diff --git a/railties/lib/rails_generator/scripts.rb b/railties/lib/rails_generator/scripts.rb index 9b1a99838a..3763b75dba 100644 --- a/railties/lib/rails_generator/scripts.rb +++ b/railties/lib/rails_generator/scripts.rb @@ -57,7 +57,7 @@ module Rails usage << <<end_blurb -More are available at http://wiki.rubyonrails.org/rails/pages/AvailableGenerators +You can also install additional generators for your own use: 1. Download, for example, login_generator.zip 2. Unzip to directory #{Dir.user_home}/.rails/generators/login to use the generator with all your Rails apps diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake index 7cf7061f38..e496e1a04f 100644 --- a/railties/lib/tasks/gems.rake +++ b/railties/lib/tasks/gems.rake @@ -20,7 +20,7 @@ namespace :gems do desc "Build any native extensions for unpacked gems" task :build do $gems_build_rake_task = true - frozen_gems.each &:build + frozen_gems.each {|gem| gem.build } end namespace :build do @@ -33,12 +33,12 @@ namespace :gems do desc "Installs all required gems." task :install => :base do - current_gems.each &:install + current_gems.each {|gem| gem.install } end desc "Unpacks all required gems into vendor/gems." task :unpack => :install do - current_gems.each &:unpack + current_gems.each {|gem| gem.unpack } end namespace :unpack do @@ -50,7 +50,7 @@ namespace :gems do desc "Regenerate gem specifications in correct format." task :refresh_specs => :base do - current_gems.each &:refresh + current_gems.each {|gem| gem.refresh } end end diff --git a/railties/lib/tasks/misc.rake b/railties/lib/tasks/misc.rake index a2c338aa5b..fb2fc31dc1 100644 --- a/railties/lib/tasks/misc.rake +++ b/railties/lib/tasks/misc.rake @@ -31,7 +31,7 @@ namespace :time do desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time' task :local do require 'active_support' - require 'active_support/core/time' + require 'active_support/time' jan_offset = Time.now.beginning_of_year.utc_offset jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset offset = jan_offset < jul_offset ? jan_offset : jul_offset @@ -41,7 +41,7 @@ namespace :time do # to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600 def build_time_zone_list(method, offset = ENV['OFFSET']) require 'active_support' - require 'active_support/core/time' + require 'active_support/time' if offset offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/) sign = $1 == '-' ? -1 : 1 diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 971cba89d0..fdcc7a1ef6 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -14,6 +14,8 @@ unless defined?(Rails) && defined?(Rails::Info) end end +require "active_support/core_ext/kernel/reporting" + class InfoTest < ActiveSupport::TestCase def setup Rails.send :remove_const, :Info diff --git a/tools/profile_requires b/tools/profile_requires index 0fd11c7d41..927467bc4e 100755 --- a/tools/profile_requires +++ b/tools/profile_requires @@ -4,58 +4,52 @@ # tools/profile_requires activeresource/examples/simple.rb abort 'Use REE so you can profile memory and object allocation' unless GC.respond_to?(:enable_stats) +ENV['NO_RELOAD'] ||= '1' +ENV['RAILS_ENV'] ||= 'development' + GC.enable_stats require 'rubygems' Gem.source_index require 'benchmark' -module TrackHeapGrowth +module RequireProfiler + def require(file, *args) RequireProfiler.profile(file) { super } end + def load(file, *args) RequireProfiler.profile(file) { super } end + + @depth, @stats = 0, [] class << self - attr_accessor :indent + attr_accessor :depth attr_accessor :stats - end - self.indent = 0 - self.stats = [] - - def track_growth(file) - TrackHeapGrowth.stats << [file, TrackHeapGrowth.indent] - TrackHeapGrowth.indent += 1 - heap_before, objects_before = GC.allocated_size, ObjectSpace.allocated_objects - result = nil - elapsed = Benchmark.realtime { result = yield } - heap_after, objects_after = GC.allocated_size, ObjectSpace.allocated_objects - TrackHeapGrowth.indent -= 1 - TrackHeapGrowth.stats.pop if TrackHeapGrowth.stats.last.first == file - TrackHeapGrowth.stats << [file, TrackHeapGrowth.indent, elapsed, heap_after - heap_before, objects_after - objects_before] if result - result - end - def require(file, *args) - track_growth(file) { super } - end - - def load(file, *args) - track_growth(file) { super } + def profile(file) + stats << [file, depth] + self.depth += 1 + heap_before, objects_before = GC.allocated_size, ObjectSpace.allocated_objects + result = nil + elapsed = Benchmark.realtime { result = yield } + heap_after, objects_after = GC.allocated_size, ObjectSpace.allocated_objects + self.depth -= 1 + stats.pop if stats.last.first == file + stats << [file, depth, elapsed, heap_after - heap_before, objects_after - objects_before] if result + result + end end end -Object.instance_eval { include TrackHeapGrowth } - GC.start before = GC.allocated_size before_rss = `ps -o rss= -p #{Process.pid}`.to_i before_live_objects = ObjectSpace.live_objects path = ARGV.shift - if mode = ARGV.shift require 'ruby-prof' RubyProf.measure_mode = RubyProf.const_get(mode.upcase) RubyProf.start +else + Object.instance_eval { include RequireProfiler } end -ENV['NO_RELOAD'] ||= '1' -ENV['RAILS_ENV'] ||= 'development' elapsed = Benchmark.realtime { require path } results = RubyProf.stop if mode @@ -66,16 +60,16 @@ after = GC.allocated_size usage = (after - before) / 1024.0 if mode - File.open("profile_startup.#{mode}.tree", 'w') do |out| + File.open("#{File.basename(path, '.rb')}.#{mode}.callgrind", 'w') do |out| RubyProf::CallTreePrinter.new(results).print(out) end end -TrackHeapGrowth.stats.each do |file, indent, sec, bytes, objects| +RequireProfiler.stats.each do |file, depth, sec, bytes, objects| if sec - puts "%10.2f KB %10d obj %8.1f ms %s%s" % [bytes / 1024.0, objects, sec * 1000, ' ' * indent, file] + puts "%10.2f KB %10d obj %8.1f ms %s%s" % [bytes / 1024.0, objects, sec * 1000, ' ' * depth, file] else - puts "#{' ' * (42 + indent)}#{file}" + puts "#{' ' * (42 + depth)}#{file}" end end puts "%10.2f KB %10d obj %8.1f ms %d KB RSS" % [usage, after_live_objects - before_live_objects, elapsed * 1000, after_rss - before_rss] |