diff options
45 files changed, 837 insertions, 698 deletions
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index aedcd81e52..9a1a27c8ed 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -64,7 +64,7 @@ module ActionMailer raise "Delivery method cannot be nil" when Symbol if klass = delivery_methods[method] - mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {})) + mail.delivery_method(klass,(send(:"#{method}_settings") || {}).merge!(options || {})) else raise "Invalid delivery method #{method.inspect}" end diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index 20412c7bb2..61a037ea18 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -152,9 +152,6 @@ class MailDeliveryTest < ActiveSupport::TestCase assert_equal "overridden", delivery_method_instance.settings[:user_name] assert_equal "somethingobtuse", delivery_method_instance.settings[:password] assert_equal delivery_method_instance.settings.merge(overridden_options), delivery_method_instance.settings - - # make sure that overriding delivery method options per mail instance doesn't affect the Base setting - assert_equal settings, ActionMailer::Base.smtp_settings end test "non registered delivery methods raises errors" do diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 1faecc850b..67d261db77 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -59,7 +59,7 @@ module ActionController # <input type="text" name="post[address]" value="hyacintvej"> # # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>. - # If the address input had been named <tt>post[address][street]</tt>, the params would have included + # If the address input had been named "post[address][street]", the params would have included # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting. # # == Sessions diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 5272dc6cdb..abed6e53cc 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -6,6 +6,13 @@ module ActionController Renderers.add(key, &block) end + class MissingRenderer < LoadError + def initialize(format) + @format = format + super("No renderer defined for format: #{@format}") + end + end + module Renderers extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index fd5b661209..66ff34a794 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -202,6 +202,7 @@ module ActionController #:nodoc: # This is the common behavior for formats associated with APIs, such as :xml and :json. def api_behavior(error) raise error unless resourceful? + raise MissingRenderer.new(format) unless has_renderer? if get? display resource @@ -269,6 +270,11 @@ module ActionController #:nodoc: resource.respond_to?(:errors) && !resource.errors.empty? end + # Check whether the neceessary Renderer is available + def has_renderer? + Renderers::RENDERERS.include?(format) + end + # By default, render the <code>:edit</code> action for HTML requests with errors, unless # the verb was POST. # diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 943fc15026..0e5dc1fc6c 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -514,12 +514,11 @@ module ActionDispatch @recall = recall.dup @set = set - normalize_recall! normalize_options! normalize_controller_action_id! use_relative_controller! normalize_controller! - normalize_action! + handle_nil_action! end def controller @@ -538,11 +537,6 @@ module ActionDispatch end end - # Set 'index' as default action for recall - def normalize_recall! - @recall[:action] ||= 'index' - end - def normalize_options! # If an explicit :controller was given, always make :action explicit # too, so that action expiry works as expected for things like @@ -558,8 +552,8 @@ module ActionDispatch options[:controller] = options[:controller].to_s end - if options.key?(:action) - options[:action] = (options[:action] || 'index').to_s + if options[:action] + options[:action] = options[:action].to_s end end @@ -569,6 +563,8 @@ module ActionDispatch # :controller, :action or :id is not found, don't pull any # more keys from the recall. def normalize_controller_action_id! + @recall[:action] ||= 'index' if current_controller + use_recall_for(:controller) or return use_recall_for(:action) or return use_recall_for(:id) @@ -590,11 +586,13 @@ module ActionDispatch @options[:controller] = controller.sub(%r{^/}, '') if controller end - # Move 'index' action from options to recall - def normalize_action! - if @options[:action] == 'index' - @recall[:action] = @options.delete(:action) + # This handles the case of action: nil being explicitly passed. + # It is identical to action: "index" + def handle_nil_action! + if options.has_key?(:action) && options[:action].nil? + options[:action] = 'index' end + recall[:action] = options.delete(:action) if options[:action] == 'index' end # Generates a path from routes, returns [path, params]. diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb new file mode 100644 index 0000000000..c03c7edeb8 --- /dev/null +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -0,0 +1,94 @@ +require 'abstract_unit' + +class StarStarMimeController < ActionController::Base + layout nil + + def index + render + end +end + +class StarStarMimeControllerTest < ActionController::TestCase + tests StarStarMimeController + + def test_javascript_with_format + @request.accept = "text/javascript" + get :index, :format => 'js' + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format + @request.accept = "text/javascript" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end + + def test_javascript_with_no_format_only_star_star + @request.accept = "*/*" + get :index + assert_match "function addition(a,b){ return a+b; }", @response.body + end +end + +class AbstractPostController < ActionController::Base + self.view_paths = File.dirname(__FILE__) + "/../../fixtures/post_test/" +end + +# For testing layouts which are set automatically +class PostController < AbstractPostController + around_action :with_iphone + + def index + respond_to(:html, :iphone, :js) + end + +protected + + def with_iphone + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + yield + end +end + +class SuperPostController < PostController +end + +class MimeControllerLayoutsTest < ActionController::TestCase + tests PostController + + def setup + super + @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + end + + def teardown + super + Mime::Type.unregister(:iphone) + end + + def test_missing_layout_renders_properly + get :index + assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body + + @request.accept = "text/iphone" + get :index + assert_equal 'Hello iPhone', @response.body + end + + def test_format_with_inherited_layouts + @controller = SuperPostController.new + + get :index + assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body + + @request.accept = "text/iphone" + get :index + assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body + end + + def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout + get :index, :format => :js + assert_equal "Hello Firefox", @response.body + end +end diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb new file mode 100644 index 0000000000..774dabe105 --- /dev/null +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -0,0 +1,493 @@ +require 'abstract_unit' + +class RespondToController < ActionController::Base + layout :set_layout + + def html_xml_or_rss + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + type.rss { render :text => "RSS" } + type.all { render :text => "Nothing" } + end + end + + def js_or_html + respond_to do |type| + type.html { render :text => "HTML" } + type.js { render :text => "JS" } + type.all { render :text => "Nothing" } + end + end + + def json_or_yaml + respond_to do |type| + type.json { render :text => "JSON" } + type.yaml { render :text => "YAML" } + end + end + + def html_or_xml + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + type.all { render :text => "Nothing" } + end + end + + def json_xml_or_html + respond_to do |type| + type.json { render :text => 'JSON' } + type.xml { render :xml => 'XML' } + type.html { render :text => 'HTML' } + end + end + + + def forced_xml + request.format = :xml + + respond_to do |type| + type.html { render :text => "HTML" } + type.xml { render :text => "XML" } + end + end + + def just_xml + respond_to do |type| + type.xml { render :text => "XML" } + end + end + + def using_defaults + respond_to do |type| + type.html + type.xml + end + end + + def using_defaults_with_type_list + respond_to(:html, :xml) + end + + def using_defaults_with_all + respond_to do |type| + type.html + type.all{ render text: "ALL" } + end + end + + def made_for_content_type + respond_to do |type| + type.rss { render :text => "RSS" } + type.atom { render :text => "ATOM" } + type.all { render :text => "Nothing" } + end + end + + def custom_type_handling + respond_to do |type| + type.html { render :text => "HTML" } + type.custom("application/crazy-xml") { render :text => "Crazy XML" } + type.all { render :text => "Nothing" } + end + end + + + def custom_constant_handling + respond_to do |type| + type.html { render :text => "HTML" } + type.mobile { render :text => "Mobile" } + end + end + + def custom_constant_handling_without_block + respond_to do |type| + type.html { render :text => "HTML" } + type.mobile + end + end + + def handle_any + respond_to do |type| + type.html { render :text => "HTML" } + type.any(:js, :xml) { render :text => "Either JS or XML" } + end + end + + def handle_any_any + respond_to do |type| + type.html { render :text => 'HTML' } + type.any { render :text => 'Whatever you ask for, I got it' } + end + end + + def all_types_with_layout + respond_to do |type| + type.html + end + end + + def iphone_with_html_response_type + request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" + + respond_to do |type| + type.html { @type = "Firefox" } + type.iphone { @type = "iPhone" } + end + end + + def iphone_with_html_response_type_without_layout + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + + respond_to do |type| + type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" } + type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" } + end + end + + protected + def set_layout + case action_name + when "all_types_with_layout", "iphone_with_html_response_type" + "respond_to/layouts/standard" + when "iphone_with_html_response_type_without_layout" + "respond_to/layouts/missing" + end + end +end + +class RespondToControllerTest < ActionController::TestCase + tests RespondToController + + def setup + super + @request.host = "www.example.com" + Mime::Type.register_alias("text/html", :iphone) + Mime::Type.register("text/x-mobile", :mobile) + end + + def teardown + super + Mime::Type.unregister(:iphone) + Mime::Type.unregister(:mobile) + end + + def test_html + @request.accept = "text/html" + get :js_or_html + assert_equal 'HTML', @response.body + + get :html_or_xml + assert_equal 'HTML', @response.body + + assert_raises(ActionController::UnknownFormat) do + get :just_xml + end + end + + def test_all + @request.accept = "*/*" + get :js_or_html + assert_equal 'HTML', @response.body # js is not part of all + + get :html_or_xml + assert_equal 'HTML', @response.body + + get :just_xml + assert_equal 'XML', @response.body + end + + def test_xml + @request.accept = "application/xml" + get :html_xml_or_rss + assert_equal 'XML', @response.body + end + + def test_js_or_html + @request.accept = "text/javascript, text/html" + xhr :get, :js_or_html + assert_equal 'JS', @response.body + + @request.accept = "text/javascript, text/html" + xhr :get, :html_or_xml + assert_equal 'HTML', @response.body + + @request.accept = "text/javascript, text/html" + + assert_raises(ActionController::UnknownFormat) do + xhr :get, :just_xml + end + end + + def test_json_or_yaml_with_leading_star_star + @request.accept = "*/*, application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body + + @request.accept = "*/* , application/json" + get :json_xml_or_html + assert_equal 'HTML', @response.body + end + + def test_json_or_yaml + xhr :get, :json_or_yaml + assert_equal 'JSON', @response.body + + get :json_or_yaml, :format => 'json' + assert_equal 'JSON', @response.body + + get :json_or_yaml, :format => 'yaml' + assert_equal 'YAML', @response.body + + { 'YAML' => %w(text/yaml), + 'JSON' => %w(application/json text/x-json) + }.each do |body, content_types| + content_types.each do |content_type| + @request.accept = content_type + get :json_or_yaml + assert_equal body, @response.body + end + end + end + + def test_js_or_anything + @request.accept = "text/javascript, */*" + xhr :get, :js_or_html + assert_equal 'JS', @response.body + + xhr :get, :html_or_xml + assert_equal 'HTML', @response.body + + xhr :get, :just_xml + assert_equal 'XML', @response.body + end + + def test_using_defaults + @request.accept = "*/*" + get :using_defaults + assert_equal "text/html", @response.content_type + assert_equal 'Hello world!', @response.body + + @request.accept = "application/xml" + get :using_defaults + assert_equal "application/xml", @response.content_type + assert_equal "<p>Hello world!</p>\n", @response.body + end + + def test_using_defaults_with_all + @request.accept = "*/*" + get :using_defaults_with_all + assert_equal "HTML!", @response.body.strip + + @request.accept = "text/html" + get :using_defaults_with_all + assert_equal "HTML!", @response.body.strip + + @request.accept = "application/json" + get :using_defaults_with_all + assert_equal "ALL", @response.body + end + + def test_using_defaults_with_type_list + @request.accept = "*/*" + get :using_defaults_with_type_list + assert_equal "text/html", @response.content_type + assert_equal 'Hello world!', @response.body + + @request.accept = "application/xml" + get :using_defaults_with_type_list + assert_equal "application/xml", @response.content_type + assert_equal "<p>Hello world!</p>\n", @response.body + end + + def test_with_atom_content_type + @request.accept = "" + @request.env["CONTENT_TYPE"] = "application/atom+xml" + xhr :get, :made_for_content_type + assert_equal "ATOM", @response.body + end + + def test_with_rss_content_type + @request.accept = "" + @request.env["CONTENT_TYPE"] = "application/rss+xml" + xhr :get, :made_for_content_type + assert_equal "RSS", @response.body + end + + def test_synonyms + @request.accept = "application/javascript" + get :js_or_html + assert_equal 'JS', @response.body + + @request.accept = "application/x-xml" + get :html_xml_or_rss + assert_equal "XML", @response.body + end + + def test_custom_types + @request.accept = "application/crazy-xml" + get :custom_type_handling + assert_equal "application/crazy-xml", @response.content_type + assert_equal 'Crazy XML', @response.body + + @request.accept = "text/html" + get :custom_type_handling + assert_equal "text/html", @response.content_type + assert_equal 'HTML', @response.body + end + + def test_xhtml_alias + @request.accept = "application/xhtml+xml,application/xml" + get :html_or_xml + assert_equal 'HTML', @response.body + end + + def test_firefox_simulation + @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + get :html_or_xml + assert_equal 'HTML', @response.body + end + + def test_handle_any + @request.accept = "*/*" + get :handle_any + assert_equal 'HTML', @response.body + + @request.accept = "text/javascript" + get :handle_any + assert_equal 'Either JS or XML', @response.body + + @request.accept = "text/xml" + get :handle_any + assert_equal 'Either JS or XML', @response.body + end + + def test_handle_any_any + @request.accept = "*/*" + get :handle_any_any + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_parameter_format + get :handle_any_any, {:format=>'html'} + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_explicit_html + @request.accept = "text/html" + get :handle_any_any + assert_equal 'HTML', @response.body + end + + def test_handle_any_any_javascript + @request.accept = "text/javascript" + get :handle_any_any + assert_equal 'Whatever you ask for, I got it', @response.body + end + + def test_handle_any_any_xml + @request.accept = "text/xml" + get :handle_any_any + assert_equal 'Whatever you ask for, I got it', @response.body + end + + def test_browser_check_with_any_any + @request.accept = "application/json, application/xml" + get :json_xml_or_html + assert_equal 'JSON', @response.body + + @request.accept = "application/json, application/xml, */*" + get :json_xml_or_html + assert_equal 'HTML', @response.body + end + + def test_html_type_with_layout + @request.accept = "text/html" + get :all_types_with_layout + assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body + end + + def test_xhr + xhr :get, :js_or_html + assert_equal 'JS', @response.body + end + + def test_custom_constant + get :custom_constant_handling, :format => "mobile" + assert_equal "text/x-mobile", @response.content_type + assert_equal "Mobile", @response.body + end + + def test_custom_constant_handling_without_block + get :custom_constant_handling_without_block, :format => "mobile" + assert_equal "text/x-mobile", @response.content_type + assert_equal "Mobile", @response.body + end + + def test_forced_format + get :html_xml_or_rss + assert_equal "HTML", @response.body + + get :html_xml_or_rss, :format => "html" + assert_equal "HTML", @response.body + + get :html_xml_or_rss, :format => "xml" + assert_equal "XML", @response.body + + get :html_xml_or_rss, :format => "rss" + assert_equal "RSS", @response.body + end + + def test_internally_forced_format + get :forced_xml + assert_equal "XML", @response.body + + get :forced_xml, :format => "html" + assert_equal "XML", @response.body + end + + def test_extension_synonyms + get :html_xml_or_rss, :format => "xhtml" + assert_equal "HTML", @response.body + end + + def test_render_action_for_html + @controller.instance_eval do + def render(*args) + @action = args.first[:action] unless args.empty? + @action ||= action_name + + response.body = "#{@action} - #{formats}" + end + end + + get :using_defaults + assert_equal "using_defaults - #{[:html].to_s}", @response.body + + get :using_defaults, :format => "xml" + assert_equal "using_defaults - #{[:xml].to_s}", @response.body + end + + def test_format_with_custom_response_type + get :iphone_with_html_response_type + assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body + + get :iphone_with_html_response_type, :format => "iphone" + assert_equal "text/html", @response.content_type + assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body + end + + def test_format_with_custom_response_type_and_request_headers + @request.accept = "text/iphone" + get :iphone_with_html_response_type + assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body + assert_equal "text/html", @response.content_type + end + + def test_invalid_format + assert_raises(ActionController::UnknownFormat) do + get :using_defaults, :format => "invalidformat" + end + end +end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime/respond_with_test.rb index 2d89969fd3..29ddbff8d4 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime/respond_with_test.rb @@ -1,529 +1,5 @@ require 'abstract_unit' require 'controller/fake_models' -require 'active_support/core_ext/hash/conversions' - -class StarStarMimeController < ActionController::Base - layout nil - - def index - render - end -end - -class RespondToController < ActionController::Base - layout :set_layout - - def html_xml_or_rss - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - type.rss { render :text => "RSS" } - type.all { render :text => "Nothing" } - end - end - - def js_or_html - respond_to do |type| - type.html { render :text => "HTML" } - type.js { render :text => "JS" } - type.all { render :text => "Nothing" } - end - end - - def json_or_yaml - respond_to do |type| - type.json { render :text => "JSON" } - type.yaml { render :text => "YAML" } - end - end - - def html_or_xml - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - type.all { render :text => "Nothing" } - end - end - - def json_xml_or_html - respond_to do |type| - type.json { render :text => 'JSON' } - type.xml { render :xml => 'XML' } - type.html { render :text => 'HTML' } - end - end - - - def forced_xml - request.format = :xml - - respond_to do |type| - type.html { render :text => "HTML" } - type.xml { render :text => "XML" } - end - end - - def just_xml - respond_to do |type| - type.xml { render :text => "XML" } - end - end - - def using_defaults - respond_to do |type| - type.html - type.xml - end - end - - def using_defaults_with_type_list - respond_to(:html, :xml) - end - - def using_defaults_with_all - respond_to do |type| - type.html - type.all{ render text: "ALL" } - end - end - - def made_for_content_type - respond_to do |type| - type.rss { render :text => "RSS" } - type.atom { render :text => "ATOM" } - type.all { render :text => "Nothing" } - end - end - - def custom_type_handling - respond_to do |type| - type.html { render :text => "HTML" } - type.custom("application/crazy-xml") { render :text => "Crazy XML" } - type.all { render :text => "Nothing" } - end - end - - - def custom_constant_handling - respond_to do |type| - type.html { render :text => "HTML" } - type.mobile { render :text => "Mobile" } - end - end - - def custom_constant_handling_without_block - respond_to do |type| - type.html { render :text => "HTML" } - type.mobile - end - end - - def handle_any - respond_to do |type| - type.html { render :text => "HTML" } - type.any(:js, :xml) { render :text => "Either JS or XML" } - end - end - - def handle_any_any - respond_to do |type| - type.html { render :text => 'HTML' } - type.any { render :text => 'Whatever you ask for, I got it' } - end - end - - def all_types_with_layout - respond_to do |type| - type.html - end - end - - def iphone_with_html_response_type - request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" - - respond_to do |type| - type.html { @type = "Firefox" } - type.iphone { @type = "iPhone" } - end - end - - def iphone_with_html_response_type_without_layout - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - - respond_to do |type| - type.html { @type = "Firefox"; render :action => "iphone_with_html_response_type" } - type.iphone { @type = "iPhone" ; render :action => "iphone_with_html_response_type" } - end - end - - protected - def set_layout - case action_name - when "all_types_with_layout", "iphone_with_html_response_type" - "respond_to/layouts/standard" - when "iphone_with_html_response_type_without_layout" - "respond_to/layouts/missing" - end - end -end - -class StarStarMimeControllerTest < ActionController::TestCase - tests StarStarMimeController - - def test_javascript_with_format - @request.accept = "text/javascript" - get :index, :format => 'js' - assert_match "function addition(a,b){ return a+b; }", @response.body - end - - def test_javascript_with_no_format - @request.accept = "text/javascript" - get :index - assert_match "function addition(a,b){ return a+b; }", @response.body - end - - def test_javascript_with_no_format_only_star_star - @request.accept = "*/*" - get :index - assert_match "function addition(a,b){ return a+b; }", @response.body - end - -end - -class RespondToControllerTest < ActionController::TestCase - tests RespondToController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias("text/html", :iphone) - Mime::Type.register("text/x-mobile", :mobile) - end - - def teardown - super - Mime::Type.unregister(:iphone) - Mime::Type.unregister(:mobile) - end - - def test_html - @request.accept = "text/html" - get :js_or_html - assert_equal 'HTML', @response.body - - get :html_or_xml - assert_equal 'HTML', @response.body - - assert_raises(ActionController::UnknownFormat) do - get :just_xml - end - end - - def test_all - @request.accept = "*/*" - get :js_or_html - assert_equal 'HTML', @response.body # js is not part of all - - get :html_or_xml - assert_equal 'HTML', @response.body - - get :just_xml - assert_equal 'XML', @response.body - end - - def test_xml - @request.accept = "application/xml" - get :html_xml_or_rss - assert_equal 'XML', @response.body - end - - def test_js_or_html - @request.accept = "text/javascript, text/html" - xhr :get, :js_or_html - assert_equal 'JS', @response.body - - @request.accept = "text/javascript, text/html" - xhr :get, :html_or_xml - assert_equal 'HTML', @response.body - - @request.accept = "text/javascript, text/html" - - assert_raises(ActionController::UnknownFormat) do - xhr :get, :just_xml - end - end - - def test_json_or_yaml_with_leading_star_star - @request.accept = "*/*, application/json" - get :json_xml_or_html - assert_equal 'HTML', @response.body - - @request.accept = "*/* , application/json" - get :json_xml_or_html - assert_equal 'HTML', @response.body - end - - def test_json_or_yaml - xhr :get, :json_or_yaml - assert_equal 'JSON', @response.body - - get :json_or_yaml, :format => 'json' - assert_equal 'JSON', @response.body - - get :json_or_yaml, :format => 'yaml' - assert_equal 'YAML', @response.body - - { 'YAML' => %w(text/yaml), - 'JSON' => %w(application/json text/x-json) - }.each do |body, content_types| - content_types.each do |content_type| - @request.accept = content_type - get :json_or_yaml - assert_equal body, @response.body - end - end - end - - def test_js_or_anything - @request.accept = "text/javascript, */*" - xhr :get, :js_or_html - assert_equal 'JS', @response.body - - xhr :get, :html_or_xml - assert_equal 'HTML', @response.body - - xhr :get, :just_xml - assert_equal 'XML', @response.body - end - - def test_using_defaults - @request.accept = "*/*" - get :using_defaults - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "application/xml" - get :using_defaults - assert_equal "application/xml", @response.content_type - assert_equal "<p>Hello world!</p>\n", @response.body - end - - def test_using_defaults_with_all - @request.accept = "*/*" - get :using_defaults_with_all - assert_equal "HTML!", @response.body.strip - - @request.accept = "text/html" - get :using_defaults_with_all - assert_equal "HTML!", @response.body.strip - - @request.accept = "application/json" - get :using_defaults_with_all - assert_equal "ALL", @response.body - end - - def test_using_defaults_with_type_list - @request.accept = "*/*" - get :using_defaults_with_type_list - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "application/xml" - get :using_defaults_with_type_list - assert_equal "application/xml", @response.content_type - assert_equal "<p>Hello world!</p>\n", @response.body - end - - def test_with_atom_content_type - @request.accept = "" - @request.env["CONTENT_TYPE"] = "application/atom+xml" - xhr :get, :made_for_content_type - assert_equal "ATOM", @response.body - end - - def test_with_rss_content_type - @request.accept = "" - @request.env["CONTENT_TYPE"] = "application/rss+xml" - xhr :get, :made_for_content_type - assert_equal "RSS", @response.body - end - - def test_synonyms - @request.accept = "application/javascript" - get :js_or_html - assert_equal 'JS', @response.body - - @request.accept = "application/x-xml" - get :html_xml_or_rss - assert_equal "XML", @response.body - end - - def test_custom_types - @request.accept = "application/crazy-xml" - get :custom_type_handling - assert_equal "application/crazy-xml", @response.content_type - assert_equal 'Crazy XML', @response.body - - @request.accept = "text/html" - get :custom_type_handling - assert_equal "text/html", @response.content_type - assert_equal 'HTML', @response.body - end - - def test_xhtml_alias - @request.accept = "application/xhtml+xml,application/xml" - get :html_or_xml - assert_equal 'HTML', @response.body - end - - def test_firefox_simulation - @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" - get :html_or_xml - assert_equal 'HTML', @response.body - end - - def test_handle_any - @request.accept = "*/*" - get :handle_any - assert_equal 'HTML', @response.body - - @request.accept = "text/javascript" - get :handle_any - assert_equal 'Either JS or XML', @response.body - - @request.accept = "text/xml" - get :handle_any - assert_equal 'Either JS or XML', @response.body - end - - def test_handle_any_any - @request.accept = "*/*" - get :handle_any_any - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_parameter_format - get :handle_any_any, {:format=>'html'} - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_explicit_html - @request.accept = "text/html" - get :handle_any_any - assert_equal 'HTML', @response.body - end - - def test_handle_any_any_javascript - @request.accept = "text/javascript" - get :handle_any_any - assert_equal 'Whatever you ask for, I got it', @response.body - end - - def test_handle_any_any_xml - @request.accept = "text/xml" - get :handle_any_any - assert_equal 'Whatever you ask for, I got it', @response.body - end - - def test_browser_check_with_any_any - @request.accept = "application/json, application/xml" - get :json_xml_or_html - assert_equal 'JSON', @response.body - - @request.accept = "application/json, application/xml, */*" - get :json_xml_or_html - assert_equal 'HTML', @response.body - end - - def test_html_type_with_layout - @request.accept = "text/html" - get :all_types_with_layout - assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body - end - - def test_xhr - xhr :get, :js_or_html - assert_equal 'JS', @response.body - end - - def test_custom_constant - get :custom_constant_handling, :format => "mobile" - assert_equal "text/x-mobile", @response.content_type - assert_equal "Mobile", @response.body - end - - def test_custom_constant_handling_without_block - get :custom_constant_handling_without_block, :format => "mobile" - assert_equal "text/x-mobile", @response.content_type - assert_equal "Mobile", @response.body - end - - def test_forced_format - get :html_xml_or_rss - assert_equal "HTML", @response.body - - get :html_xml_or_rss, :format => "html" - assert_equal "HTML", @response.body - - get :html_xml_or_rss, :format => "xml" - assert_equal "XML", @response.body - - get :html_xml_or_rss, :format => "rss" - assert_equal "RSS", @response.body - end - - def test_internally_forced_format - get :forced_xml - assert_equal "XML", @response.body - - get :forced_xml, :format => "html" - assert_equal "XML", @response.body - end - - def test_extension_synonyms - get :html_xml_or_rss, :format => "xhtml" - assert_equal "HTML", @response.body - end - - def test_render_action_for_html - @controller.instance_eval do - def render(*args) - @action = args.first[:action] unless args.empty? - @action ||= action_name - - response.body = "#{@action} - #{formats}" - end - end - - get :using_defaults - assert_equal "using_defaults - #{[:html].to_s}", @response.body - - get :using_defaults, :format => "xml" - assert_equal "using_defaults - #{[:xml].to_s}", @response.body - end - - def test_format_with_custom_response_type - get :iphone_with_html_response_type - assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body - - get :iphone_with_html_response_type, :format => "iphone" - assert_equal "text/html", @response.content_type - assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - end - - def test_format_with_custom_response_type_and_request_headers - @request.accept = "text/iphone" - get :iphone_with_html_response_type - assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body - assert_equal "text/html", @response.content_type - end - - def test_invalid_format - assert_raises(ActionController::UnknownFormat) do - get :using_defaults, :format => "invalidformat" - end - end -end class RespondWithController < ActionController::Base respond_to :html, :json, :touch @@ -631,6 +107,20 @@ class RenderJsonRespondWithController < RespondWithController end end +class CsvRespondWithController < ActionController::Base + respond_to :csv + + class RespondWithCsv + def to_csv + "c,s,v" + end + end + + def index + respond_with(RespondWithCsv.new) + end +end + class EmptyRespondWithController < ActionController::Base def index respond_with(Customer.new("david", 13)) @@ -646,7 +136,6 @@ class RespondWithControllerTest < ActionController::TestCase Mime::Type.register_alias('text/html', :iphone) Mime::Type.register_alias('text/html', :touch) Mime::Type.register('text/x-mobile', :mobile) - Customer.send(:undef_method, :to_json) if Customer.method_defined?(:to_json) end def teardown @@ -1132,6 +621,23 @@ class RespondWithControllerTest < ActionController::TestCase RespondWithController.responder = ActionController::Responder end + def test_uses_renderer_if_an_api_behavior + ActionController::Renderers.add :csv do |obj, options| + send_data obj.to_csv, type: Mime::CSV + end + @controller = CsvRespondWithController.new + get :index, format: 'csv' + assert_equal Mime::CSV, @response.content_type + assert_equal "c,s,v", @response.body + end + + def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer + @controller = CsvRespondWithController.new + assert_raise ActionController::MissingRenderer do + get :index, format: 'csv' + end + end + def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called @controller = EmptyRespondWithController.new @request.accept = "*/*" @@ -1155,69 +661,6 @@ class RespondWithControllerTest < ActionController::TestCase end end -class AbstractPostController < ActionController::Base - self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/" -end - -# For testing layouts which are set automatically -class PostController < AbstractPostController - around_action :with_iphone - - def index - respond_to(:html, :iphone, :js) - end - -protected - - def with_iphone - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - yield - end -end - -class SuperPostController < PostController -end - -class MimeControllerLayoutsTest < ActionController::TestCase - tests PostController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias("text/html", :iphone) - end - - def teardown - super - Mime::Type.unregister(:iphone) - end - - def test_missing_layout_renders_properly - get :index - assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body - - @request.accept = "text/iphone" - get :index - assert_equal 'Hello iPhone', @response.body - end - - def test_format_with_inherited_layouts - @controller = SuperPostController.new - - get :index - assert_equal '<html><div id="html">Super Firefox</div></html>', @response.body - - @request.accept = "text/iphone" - get :index - assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body - end - - def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout - get :index, :format => :js - assert_equal "Hello Firefox", @response.body - end -end - class FlashResponder < ActionController::Responder def initialize(controller, resources, options={}) super diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb index 2b36a399bb..f4bdd3e1d4 100644 --- a/actionpack/test/controller/new_base/render_streaming_test.rb +++ b/actionpack/test/controller/new_base/render_streaming_test.rb @@ -92,7 +92,7 @@ module RenderStreaming io.rewind assert_match "(undefined method `invalid!' for nil:NilClass)", io.read ensure - ActionView::Base.logger = _old + ActionController::Base.logger = _old end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 98b34a872b..fd835795c0 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -775,10 +775,6 @@ class RenderTest < ActionController::TestCase @request.host = "www.nextangle.com" end - def teardown - ActionView::Base.logger = nil - end - # :ported: def test_simple_show get :hello_world diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec index cdac074973..87cb568300 100644 --- a/actionview/actionview.gemspec +++ b/actionview/actionview.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = 'actionview' s.version = version s.summary = 'Rendering framework putting the V in MVC (part of Rails).' - s.description = 'Simple, battle-tested conventions and helpers for building web pages.' + s.description = '' s.required_ruby_version = '>= 1.9.3' diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index f0347081ee..e4f5e61e91 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -155,12 +155,6 @@ class JsonSerializationTest < ActiveModel::TestCase end end - test "as_json should keep the default order in the hash" do - json = @contact.as_json - - assert_equal %w(name age created_at awesome preferences), json.keys - end - test "from_json should work without a root (class attribute)" do json = @contact.to_json result = Contact.new.from_json(json) 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 724d1cbbf8..941e851aae 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -654,7 +654,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1) john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1) assert_equal sarah.agents, [john] - assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents + assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents end def test_associate_existing_with_nonstandard_primary_key_on_belongs_to diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index abfc90474c..a5cb22aaf6 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -453,11 +453,6 @@ class TransactionTest < ActiveRecord::TestCase raise ActiveRecord::Rollback end end - - ensure - Topic.reset_column_information # reset the column information to get correct reading - Topic.connection.remove_column('topics', 'stuff') if Topic.column_names.include?('stuff') - Topic.reset_column_information # reset the column information again for other tests end def test_transactions_state_from_rollback diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 7dad8041f3..feb828de31 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -8,7 +8,9 @@ class Author < ActiveRecord::Base has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post" has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post" has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post" + has_many :posts_containing_the_letter_a, :class_name => "Post" has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization' + has_many :posts_with_extension, :class_name => "Post" has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post' has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post' has_many :comments, through: :posts do @@ -30,6 +32,7 @@ class Author < ActiveRecord::Base has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post' has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments + has_many :limited_comments, -> { limit(1) }, :through => :posts, :source => :comments has_many :funky_comments, :through => :posts, :source => :comments has_many :ordered_uniq_comments, -> { distinct.order('comments.id') }, :through => :posts, :source => :comments has_many :ordered_uniq_comments_desc, -> { distinct.order('comments.id DESC') }, :through => :posts, :source => :comments diff --git a/activerecord/test/models/auto_id.rb b/activerecord/test/models/auto_id.rb index 82c6544bd5..d720e2be5e 100644 --- a/activerecord/test/models/auto_id.rb +++ b/activerecord/test/models/auto_id.rb @@ -1,4 +1,4 @@ class AutoId < ActiveRecord::Base - self.table_name = "auto_id_tests" - self.primary_key = "auto_id" + def self.table_name () "auto_id_tests" end + def self.primary_key () "auto_id" end end diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index 6d257dbe7e..a14a9febba 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -1,9 +1,12 @@ class Car < ActiveRecord::Base + has_many :bulbs has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb" + has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb" has_one :bulb + has_one :frickinawesome_bulb, -> { where :frickinawesome => true }, :class_name => "Bulb" has_many :tyres has_many :engines, :dependent => :destroy diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb index 3d87eb795c..545aa8110d 100644 --- a/activerecord/test/models/citation.rb +++ b/activerecord/test/models/citation.rb @@ -1,3 +1,6 @@ class Citation < ActiveRecord::Base belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id + + belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id + belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id end diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb index 566e0873f1..816c5e6937 100644 --- a/activerecord/test/models/club.rb +++ b/activerecord/test/models/club.rb @@ -2,6 +2,7 @@ class Club < ActiveRecord::Base has_one :membership has_many :memberships, :inverse_of => false has_many :members, :through => :memberships + has_many :current_memberships has_one :sponsor has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member" belongs_to :category diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 8104c607b5..c5d4ec0833 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -49,6 +49,7 @@ class Firm < Company has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client" has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client" has_many :plain_clients, :class_name => 'Client' + has_many :readonly_clients, -> { readonly }, :class_name => 'Client' has_many :clients_using_primary_key, :class_name => 'Client', :primary_key => 'name', :foreign_key => 'firm_name' has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client', @@ -166,6 +167,7 @@ class ExclusivelyDependentFirm < Company has_one :account, :foreign_key => "firm_id", :dependent => :delete has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all + has_many :dependent_hash_conditional_clients_of_firm, -> { order("id").where(:name => 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all end class SpecialClient < Client diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb index 72095f9236..cc47c7bc18 100644 --- a/activerecord/test/models/member.rb +++ b/activerecord/test/models/member.rb @@ -2,6 +2,7 @@ class Member < ActiveRecord::Base has_one :current_membership has_one :selected_membership has_one :membership + has_many :fellow_members, :through => :club, :source => :members has_one :club, :through => :current_membership has_one :selected_club, :through => :selected_membership, :source => :club has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club diff --git a/activerecord/test/models/movie.rb b/activerecord/test/models/movie.rb index c441be2bef..6384b4c801 100644 --- a/activerecord/test/models/movie.rb +++ b/activerecord/test/models/movie.rb @@ -1,3 +1,5 @@ class Movie < ActiveRecord::Base - self.primary_key = "movieid" + def self.primary_key + "movieid" + end end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 77564ffad4..93a7a2073c 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -122,6 +122,7 @@ class Post < ActiveRecord::Base has_many :secure_readers has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader" has_many :people, :through => :readers + has_many :secure_people, :through => :secure_readers has_many :single_people, :through => :readers has_many :people_with_callbacks, :source=>:person, :through => :readers, :before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) }, diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 7f42a4b1f8..c094b726b4 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -1,6 +1,7 @@ class Project < ActiveRecord::Base has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' } has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer" + has_and_belongs_to_many :selected_developers, -> { distinct.select "developers.*" }, :class_name => "Developer" has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer' has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer" has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer" diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 40c8e97fc2..17035bf338 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -34,6 +34,7 @@ class Topic < ActiveRecord::Base has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count' + has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title" has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id" has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id" diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index af048d0c85..c60e833441 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -129,7 +129,7 @@ class Date options.fetch(:day, day) ) end - + # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there. def compare_with_coercion(other) if other.is_a?(Time) diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index fbf2877117..8930376ac8 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -235,6 +235,7 @@ module ActiveSupport value.map! { |i| deep_to_h(i) } value.length > 1 ? value : value.first end + end end diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 48190e1e66..534bbe3c42 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -47,7 +47,7 @@ class Object end # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and - # does not implement the tried method. + # does not implemented the tried method. def try!(*a, &b) if a.empty? && block_given? yield self diff --git a/activesupport/lib/active_support/core_ext/thread.rb b/activesupport/lib/active_support/core_ext/thread.rb index 5481766f10..de752f14ef 100644 --- a/activesupport/lib/active_support/core_ext/thread.rb +++ b/activesupport/lib/active_support/core_ext/thread.rb @@ -23,14 +23,14 @@ class Thread # for the fiber local. The fiber is executed in the same thread, so the # thread local values are available. def thread_variable_get(key) - locals[key.to_sym] + _locals[key.to_sym] end # Sets a thread local with +key+ to +value+. Note that these are local to # threads, and not to fibers. Please see Thread#thread_variable_get for # more information. def thread_variable_set(key, value) - locals[key.to_sym] = value + _locals[key.to_sym] = value end # Returns an an array of the names of the thread-local variables (as Symbols). @@ -45,7 +45,7 @@ class Thread # Note that these are not fiber local variables. Please see Thread#thread_variable_get # for more details. def thread_variables - locals.keys + _locals.keys end # Returns <tt>true</tt> if the given string (or symbol) exists as a @@ -59,16 +59,16 @@ class Thread # Note that these are not fiber local variables. Please see Thread#thread_variable_get # for more details. def thread_variable?(key) - locals.has_key?(key.to_sym) + _locals.has_key?(key.to_sym) end private - def locals + def _locals if defined?(@locals) - @locals + @_locals else - LOCK.synchronize { @locals ||= {} } + LOCK.synchronize { @_locals ||= {} } end end end unless Thread.instance_methods.include?(:thread_variable_set) diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index e03bb4ca0f..a33e2c58a9 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -41,7 +41,7 @@ module ActiveSupport end # +InheritableOptions+ provides a constructor to build an +OrderedOptions+ - # hash inherited from the another hash. + # hash inherited from another hash. # # Use this if you already have some hash and you want to create a new one based on it. # diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb index e0f85f4e7c..ce91c443e1 100644 --- a/activesupport/test/transliterate_test.rb +++ b/activesupport/test/transliterate_test.rb @@ -3,6 +3,7 @@ require 'abstract_unit' require 'active_support/inflector/transliterate' class TransliterateTest < ActiveSupport::TestCase + def test_transliterate_should_not_change_ascii_chars (0..127).each do |byte| char = [byte].pack("U") @@ -23,13 +24,12 @@ class TransliterateTest < ActiveSupport::TestCase def test_transliterate_should_work_with_custom_i18n_rules_and_uncomposed_utf8 char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS I18n.backend.store_translations(:de, :i18n => {:transliterate => {:rule => {"ü" => "ue"}}}) - default_locale, I18n.locale = I18n.locale, :de + I18n.locale = :de assert_equal "ue", ActiveSupport::Inflector.transliterate(char) - ensure - I18n.locale = default_locale end def test_transliterate_should_allow_a_custom_replacement_char assert_equal "a*b", ActiveSupport::Inflector.transliterate("a索b", "*") end + end diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb index d992028323..a025279e16 100644 --- a/activesupport/test/xml_mini_test.rb +++ b/activesupport/test/xml_mini_test.rb @@ -106,11 +106,7 @@ module XmlMiniTest module Nokogiri end setup do - @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend - end - - teardown do - ActiveSupport::XmlMini.backend = @default_backend + @xml = ActiveSupport::XmlMini end test "#with_backend should switch backend and then switch back" do @@ -139,11 +135,7 @@ module XmlMiniTest module LibXML end setup do - @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend - end - - teardown do - ActiveSupport::XmlMini.backend = @default_backend + @xml = ActiveSupport::XmlMini end test "#with_backend should be thread-safe" do diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 0a0a958e30..9210c40c17 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -15,7 +15,7 @@ </p> <% end %> <p> - The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.14/">http://guides.rubyonrails.org/v3.2.14/</a>. + The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.13/">http://guides.rubyonrails.org/v3.2.13/</a>. </p> <p> The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>. diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 7fe9b8b4af..ce571c6f96 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -514,13 +514,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) Post.where.not(author: author) ``` -In other words, this query can be generated by calling `where` with no argument, -then immediately chain with `not` passing `where` conditions. This will generate -SQL code like this: - -```sql -SELECT * FROM posts WHERE (author_id != 1) -``` +In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions. Ordering -------- diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 1915252122..ca023f7f66 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -96,13 +96,12 @@ INFO: The predicate for strings uses the Unicode-aware character class `[:space: WARNING: Note that numbers are not mentioned. In particular, 0 and 0.0 are **not** blank. -For example, this method from `ActionController::HttpAuthentication::Token::ControllerMethods` uses `blank?` for checking whether a token is present: +For example, this method from `ActionDispatch::Session::AbstractStore` uses `blank?` for checking whether a session key is present: ```ruby -def authenticate(controller, &login_procedure) - token, options = token_and_options(controller.request) - unless token.blank? - login_procedure.call(token, options) +def ensure_session_key! + if @key.blank? + raise ArgumentError, 'A key is required...' end end ``` @@ -421,9 +420,9 @@ NOTE: Defined in `active_support/core_ext/object/with_options.rb`. ### JSON support -Active Support provides a better implemention of `to_json` than the `json` gem ordinarily provides for Ruby objects. This is because some classes, like `Hash` and `OrderedHash` needs special handling in order to provide a proper JSON representation. +Active Support provides a better implemention of `to_json` than the +json+ gem ordinarily provides for Ruby objects. This is because some classes, like +Hash+ and +OrderedHash+ needs special handling in order to provide a proper JSON representation. -Active Support also provides an implementation of `as_json` for the `Process::Status` class. +Active Support also provides an implementation of `as_json` for the <tt>Process::Status</tt> class. NOTE: Defined in `active_support/core_ext/object/to_json.rb`. @@ -1249,6 +1248,18 @@ Calling `to_s` on a safe string returns a safe string, but coercion with `to_str Calling `dup` or `clone` on safe strings yields safe strings. +### `remove` + +The method `remove` will remove all occurrences of the pattern: + +```ruby +"Hello World".remove(/Hello /) => "World" +``` + +There's also the destructive version `String#remove!`. + +NOTE: Defined in `active_support/core_ext/string/filters.rb`. + ### `squish` The method `squish` strips leading and trailing whitespace, and substitutes runs of whitespace with a single space each: @@ -1988,7 +1999,7 @@ Produce a string representation of a number in human-readable words: 1234567890123456.to_s(:human) # => "1.23 Quadrillion" ``` -NOTE: Defined in `active_support/core_ext/numeric/conversions.rb`. +NOTE: Defined in `active_support/core_ext/numeric/formatting.rb`. Extensions to `Integer` ----------------------- @@ -2046,7 +2057,7 @@ BigDecimal.new(5.00, 6).to_s # => "5.0" ### `to_formatted_s` -The method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation: +Te method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation: ```ruby BigDecimal.new(5.00, 6).to_formatted_s # => "5.0" @@ -2433,7 +2444,7 @@ dup[1][2] = 4 array[1][2] == nil # => true ``` -NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`. +NOTE: Defined in `active_support/core_ext/array/deep_dup.rb`. ### Grouping @@ -2659,7 +2670,45 @@ hash[:b][:e] == nil # => true hash[:b][:d] == [3, 4] # => true ``` -NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`. +NOTE: Defined in `active_support/core_ext/hash/deep_dup.rb`. + +### Diffing + +The method `diff` returns a hash that represents a diff of the receiver and the argument with the following logic: + +* Pairs `key`, `value` that exist in both hashes do not belong to the diff hash. + +* If both hashes have `key`, but with different values, the pair in the receiver wins. + +* The rest is just merged. + +```ruby +{a: 1}.diff(a: 1) +# => {}, first rule + +{a: 1}.diff(a: 2) +# => {:a=>1}, second rule + +{a: 1}.diff(b: 2) +# => {:a=>1, :b=>2}, third rule + +{a: 1, b: 2, c: 3}.diff(b: 1, c: 3, d: 4) +# => {:a=>1, :b=>2, :d=>4}, all rules + +{}.diff({}) # => {} +{a: 1}.diff({}) # => {:a=>1} +{}.diff(a: 1) # => {:a=>1} +``` + +An important property of this diff hash is that you can retrieve the original hash by applying `diff` twice: + +```ruby +hash.diff(hash2).diff(hash2) == hash +``` + +Diffing hashes may be useful for error messages related to expected option hashes for example. + +NOTE: Defined in `active_support/core_ext/hash/diff.rb`. ### Working with Keys @@ -3794,13 +3843,13 @@ def default_helper_module! module_path = module_name.underscore helper module_path rescue MissingSourceFile => e - raise e unless e.is_missing? "helpers/#{module_path}_helper" + raise e unless e.is_missing? "#{module_path}_helper" rescue NameError => e raise e unless e.missing_name? "#{module_name}Helper" end ``` -NOTE: Defined in `actionpack/lib/abstract_controller/helpers.rb`. +NOTE: Defined in `active_support/core_ext/name_error.rb`. Extensions to `LoadError` ------------------------- @@ -3823,4 +3872,4 @@ rescue NameError => e end ``` -NOTE: Defined in `actionpack/lib/abstract_controller/helpers.rb`. +NOTE: Defined in `active_support/core_ext/load_error.rb`. diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 8d02027f2d..7e61c78ed9 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -74,7 +74,7 @@ ports. If you have any problems with these libraries, you can install them manually by compiling the source code. Just follow the instructions at the [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos) . -Also, SQLite3 and its development files for the `sqlite3` gem — in Ubuntu you're done with just +Also, SQLite3 and its development files for the `sqlite3-ruby` gem — in Ubuntu you're done with just ```bash $ sudo apt-get install sqlite3 libsqlite3-dev @@ -86,6 +86,7 @@ And if you are on Fedora or CentOS, you're done with $ sudo yum install sqlite3 sqlite3-devel ``` +<<<<<<< HEAD If you are on Arch Linux, you will need to run: ```bash @@ -100,6 +101,8 @@ For FreeBSD users, you're done with: Or compile the `databases/sqlite3` port. +======= +>>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4 Get a recent version of [Bundler](http://gembundler.com/) ```bash @@ -188,6 +191,7 @@ $ sudo yum install mysql-server mysql-devel $ sudo yum install postgresql-server postgresql-devel ``` +<<<<<<< HEAD If you are running Arch Linux, MySQL isn't supported anymore so you will need to use MariaDB instead (see [this announcement](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)): @@ -207,6 +211,8 @@ Or install them through ports (they are located under the `databases` folder). If you run into troubles during the installation of MySQL, please see [the MySQL documentation](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html). +======= +>>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4 After that, run: ```bash diff --git a/guides/source/engines.md b/guides/source/engines.md index 9106b6382d..60b95fa668 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -104,7 +104,7 @@ At the root of this brand new engine's directory lives a `blorgh.gemspec` file. gem 'blorgh', path: "vendor/engines/blorgh" ``` -By specifying it as a gem within the `Gemfile`, Bundler will load it as such, parsing this `blorgh.gemspec` file and requiring a file within the `lib` directory called `lib/blorgh.rb`. This file requires the `blorgh/engine.rb` file (located at `lib/blorgh/engine.rb`) and defines a base module called `Blorgh`. +Don't foget to ` bundle install` as usual. By specifying it as a gem within the `Gemfile`, Bundler will load it as such, parsing this `blorgh.gemspec` file and requiring a file within the `lib` directory called `lib/blorgh.rb`. This file requires the `blorgh/engine.rb` file (located at `lib/blorgh/engine.rb`) and defines a base module called `Blorgh`. ```ruby require "blorgh/engine" @@ -466,7 +466,7 @@ NOTE: Other engines, such as Devise, handle this a little differently by making The engine contains migrations for the `blorgh_posts` and `blorgh_comments` table which need to be created in the application's database so that the engine's models can query them correctly. To copy these migrations into the application use this command: ```bash -$ rake blorgh_engine:install:migrations +$ rake blorgh:install:migrations ``` If you have multiple engines that need migrations copied over, use `railties:install:migrations` instead: diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 11e8db9e88..3a18fb81d8 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -605,7 +605,7 @@ The object in the `params` hash is an instance of a subclass of IO. Depending on ```ruby def upload uploaded_io = params[:person][:picture] - File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'w') do |file| + File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file| file.write(uploaded_io.read) end end diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 025e073d55..021aa070fb 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -598,8 +598,9 @@ end ``` A couple of things to note. We use `Post.find` to find the post we're -interested in. We also use an instance variable (prefixed by `@`) to -hold a reference to the post object. We do this because Rails will pass all instance +interested in, passing in `params[:id]` to get the `:id` parameter from the +request. We also use an instance variable (prefixed by `@`) to hold a +reference to the post object. We do this because Rails will pass all instance variables to the view. Now, create a new file `app/views/posts/show.html.erb` with the following diff --git a/guides/source/testing.md b/guides/source/testing.md index 2c5b1712cc..0258202c18 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -66,18 +66,34 @@ Here's a sample YAML fixture file: ```yaml # lo & behold! I am a YAML comment! david: - name: David Heinemeier Hansson - birthday: 1979-10-15 - profession: Systems development + name: David Heinemeier Hansson + birthday: 1979-10-15 + profession: Systems development steve: - name: Steve Ross Kellock - birthday: 1974-09-27 - profession: guy with keyboard + name: Steve Ross Kellock + birthday: 1974-09-27 + profession: guy with keyboard ``` Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. Keys which resemble YAML keywords such as 'yes' and 'no' are quoted so that the YAML Parser correctly interprets them. +If you are working with [associations](/association_basics.html), you can simply +define a reference node between two different fixtures. Here's an example with +a belongs_to/has_many association: + +```yaml +# In fixtures/categories.yml +about: + name: About + +# In fixtures/articles.yml +one: + title: Welcome to Rails! + body: Hello world! + category: about +``` + #### ERB'in It Up ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users: @@ -990,6 +1006,47 @@ class UserControllerTest < ActionController::TestCase end ``` +Testing helpers +--------------- + +In order to test helpers, all you need to do is check that the output of the +helper method matches what you'd expect. Tests related to the helpers are +located under the `test/helpers` directory. Rails provides a generator which +generates both the helper and the test file: + +```bash +$ rails generate helper User + create app/helpers/user_helper.rb + invoke test_unit + create test/helpers/user_helper_test.rb +``` + +The generated test file contains the following code: + +```ruby +require 'test_helper' + +class UserHelperTest < ActionView::TestCase +end +``` + +A helper is just a simple module where you can define methods which are +available into your views. To test the output of the helper's methods, you just +have to use a mixin like this: + +```ruby +class UserHelperTest < ActionView::TestCase + include UserHelper + + test "should return the user name" do + # ... + end +end +``` + +Moreover, since the test class extends from `ActionView::TestCase`, you have +access to Rails' helper methods such as `link_to` or `pluralize`. + Other Testing Approaches ------------------------ @@ -998,6 +1055,7 @@ The built-in `test/unit` based testing is not the only way to test Rails applica * [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use. * [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures. * [Machinist](https://github.com/notahat/machinist/tree/master), another replacement for fixtures. +* [Fixture Builder](https://github.com/rdy/fixture_builder), a tool that compiles Ruby factories into fixtures before a test run. * [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests. * [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions. * [RSpec](http://relishapp.com/rspec), a behavior-driven development framework diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 73c783085e..391551938a 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -320,7 +320,7 @@ config.assets.js_compressor = :uglifier ### sass-rails -* `asset-url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")` +* `asset_url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")` Upgrading from Rails 3.1 to Rails 3.2 ------------------------------------- diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb index 3e32576040..1f9568fb5f 100644 --- a/railties/lib/rails/api/task.rb +++ b/railties/lib/rails/api/task.rb @@ -16,7 +16,8 @@ module Rails :include => %w( README.rdoc lib/active_record/**/*.rb - ) + ), + :exclude => 'lib/active_record/vendor/*' }, 'activemodel' => { @@ -32,22 +33,23 @@ module Rails lib/abstract_controller/**/*.rb lib/action_controller/**/*.rb lib/action_dispatch/**/*.rb - ) + ), + :exclude => 'lib/action_controller/vendor/*' }, 'actionview' => { :include => %w( README.rdoc lib/action_view/**/*.rb - ), - :exclude => 'lib/action_view/vendor/*' + ) }, 'actionmailer' => { :include => %w( README.rdoc lib/action_mailer/**/*.rb - ) + ), + :exclude => 'lib/action_mailer/vendor/*' }, 'railties' => { diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_rails_loader.rb index fbb83fa10e..4a17803f1c 100644 --- a/railties/lib/rails/app_rails_loader.rb +++ b/railties/lib/rails/app_rails_loader.rb @@ -2,7 +2,7 @@ require 'pathname' module Rails module AppRailsLoader - RUBY = Gem.ruby + RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"] EXECUTABLES = ['bin/rails', 'script/rails'] BUNDLER_WARNING = <<EOS Looks like your app's ./bin/rails is a stub that was generated by Bundler. diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index be8af5c46c..8000fc3b1e 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -351,13 +351,8 @@ module Rails Rails::Railtie::Configuration.eager_load_namespaces << base base.called_from = begin - call_stack = if Kernel.respond_to?(:caller_locations) - caller_locations.map(&:path) - else - # Remove the line number from backtraces making sure we don't leave anything behind - caller.map { |p| p.sub(/:\d+.*/, '') } - end - + # Remove the line number from backtraces making sure we don't leave anything behind + call_stack = caller.map { |p| p.sub(/:\d+.*/, '') } File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] }) end end |